From fad6f5b64d02c0f4e49fd39cacfdf4e5293f857d Mon Sep 17 00:00:00 2001 From: Tan Yuanhong Date: Sat, 11 Apr 2020 11:34:05 +0800 Subject: [PATCH 1/5] Integrate the plugin as part of MarkBind --- asset/js/setup.js | 14 ++++++------- src/Page.js | 20 ++++++++++++++++++- src/lib/markbind/src/parser.js | 5 +++++ .../test_site/expected/markbind/js/setup.js | 14 ++++++------- .../expected/markbind/js/setup.js | 14 ++++++------- .../expected/markbind/js/setup.js | 14 ++++++------- .../expected/markbind/js/setup.js | 14 ++++++------- .../expected/markbind/js/setup.js | 14 ++++++------- .../expected/markbind/js/setup.js | 14 ++++++------- .../expected/markbind/js/setup.js | 14 ++++++------- 10 files changed, 72 insertions(+), 65 deletions(-) diff --git a/asset/js/setup.js b/asset/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/asset/js/setup.js +++ b/asset/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/src/Page.js b/src/Page.js index 59a67e7a1f..82528cf4b1 100644 --- a/src/Page.js +++ b/src/Page.js @@ -240,6 +240,12 @@ class Page { * @type {boolean} */ this.hasSiteNav = false; + + /** + * Flag to indicate whether a fixed header is enabled. + * @type {boolean} + */ + this.fixedHeader = false; } /** @@ -603,6 +609,7 @@ class Page { /** * Inserts the page layout's header to the start of the page + * Determines if a fixed header is present, update the page config accordingly * @param pageData a page with its front matter collected */ insertHeaderFile(pageData) { @@ -623,6 +630,12 @@ class Page { } // Retrieve Markdown file contents const headerContent = fs.readFileSync(headerPath, 'utf8'); + // Decide if fixed header is applied + const headerSelector = cheerio.load(headerContent)('header'); + if (headerSelector.length >= 1 + && headerSelector[0].attribs.class === 'header-fixed') { + this.fixedHeader = true; + } // Set header file as an includedFile this.includedFiles.add(headerPath); // Map variables @@ -927,7 +940,12 @@ class Page { .then(result => this.insertFooterFile(result)) .then(result => Page.insertTemporaryStyles(result)) .then(result => markbinder.resolveBaseUrl(result, fileConfig)) - .then(result => markbinder.render(result, this.sourcePath, fileConfig)) + .then((result) => { + // this.fixedHeader may be updated in previous step insertHeaderFile + // we need to reflect the update and pass it to the renderer, if necessary + fileConfig.fixedHeader = this.fixedHeader; + return markbinder.render(result, this.sourcePath, fileConfig); + }) .then(result => this.postRender(result)) .then(result => this.collectPluginsAssets(result)) .then(result => markbinder.processDynamicResources(this.sourcePath, result)) diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js index 97bdf4be25..b38671bbf7 100644 --- a/src/lib/markbind/src/parser.js +++ b/src/lib/markbind/src/parser.js @@ -353,6 +353,11 @@ class Parser { componentParser.postParseComponents(node, this._onError); + // If a fixed header is applied to this page, generate dummy spans as anchor points + if (config.fixedHeader && (/^h[1-6]$/).test(node.name) && node.attribs.id) { + cheerio(node).append(cheerio.parseHTML(``)); + } + return node; } diff --git a/test/functional/test_site/expected/markbind/js/setup.js b/test/functional/test_site/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site/expected/markbind/js/setup.js +++ b/test/functional/test_site/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js b/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js +++ b/test/functional/test_site_algolia_plugin/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_convert/expected/markbind/js/setup.js b/test/functional/test_site_convert/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site_convert/expected/markbind/js/setup.js +++ b/test/functional/test_site_convert/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js b/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js +++ b/test/functional/test_site_expressive_layout/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_special_tags/expected/markbind/js/setup.js b/test/functional/test_site_special_tags/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site_special_tags/expected/markbind/js/setup.js +++ b/test/functional/test_site_special_tags/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js b/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js +++ b/test/functional/test_site_templates/test_default/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } diff --git a/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js b/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js index c6b54ef24f..40e37c1bb1 100644 --- a/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js +++ b/test/functional/test_site_templates/test_minimal/expected/markbind/js/setup.js @@ -35,11 +35,16 @@ function setupAnchors() { jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); insertCss( `span.anchor { - display: block; position: relative; top: calc(-${headerHeight}px - ${bufferHeight}rem) }`, ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); } jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { @@ -48,13 +53,6 @@ function setupAnchors() { jQuery(heading).on('mouseleave', () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); if (isFixed) { - /** - * Fixing the top navbar would break anchor navigation, - * by creating empty spans above the tag we can prevent - * the headings from being covered by the navbar. - */ - const spanId = heading.id; - heading.insertAdjacentHTML('beforebegin', ``); jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } } From 58ddd5ef1b5839aff965c5d938c4cc7c996e4759 Mon Sep 17 00:00:00 2001 From: Tan Yuanhong Date: Sat, 11 Apr 2020 21:17:17 +0800 Subject: [PATCH 2/5] Resolve comments --- src/Page.js | 15 +++++++-------- src/lib/markbind/src/parser.js | 6 ++++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Page.js b/src/Page.js index 82528cf4b1..820887c6a1 100644 --- a/src/Page.js +++ b/src/Page.js @@ -611,8 +611,9 @@ class Page { * Inserts the page layout's header to the start of the page * Determines if a fixed header is present, update the page config accordingly * @param pageData a page with its front matter collected + * @param {FileConfig} fileConfig */ - insertHeaderFile(pageData) { + insertHeaderFile(pageData, fileConfig) { const { header } = this.frontMatter; if (header === FRONT_MATTER_NONE_ATTR) { return pageData; @@ -635,6 +636,7 @@ class Page { if (headerSelector.length >= 1 && headerSelector[0].attribs.class === 'header-fixed') { this.fixedHeader = true; + fileConfig.fixedHeader = true; } // Set header file as an includedFile this.includedFiles.add(headerPath); @@ -906,6 +908,7 @@ class Page { * @property {string} rootPath * @property {Object} userDefinedVariablesMap * @property {Object} headerIdMap + * @property {boolean} fixedHeader indicates whether the header of the page is fixed */ generate(builtFiles) { @@ -923,6 +926,7 @@ class Page { rootPath: this.rootPath, userDefinedVariablesMap: this.userDefinedVariablesMap, headerIdMap: this.headerIdMap, + fixedHeader: this.fixedHeader, }; return new Promise((resolve, reject) => { markbinder.includeFile(this.sourcePath, fileConfig) @@ -936,16 +940,11 @@ class Page { .then(result => this.collectPluginSources(result)) .then(result => this.preRender(result)) .then(result => this.insertSiteNav((result))) - .then(result => this.insertHeaderFile(result)) + .then(result => this.insertHeaderFile(result, fileConfig)) .then(result => this.insertFooterFile(result)) .then(result => Page.insertTemporaryStyles(result)) .then(result => markbinder.resolveBaseUrl(result, fileConfig)) - .then((result) => { - // this.fixedHeader may be updated in previous step insertHeaderFile - // we need to reflect the update and pass it to the renderer, if necessary - fileConfig.fixedHeader = this.fixedHeader; - return markbinder.render(result, this.sourcePath, fileConfig); - }) + .then(result => markbinder.render(result, this.sourcePath, fileConfig)) .then(result => this.postRender(result)) .then(result => this.collectPluginsAssets(result)) .then(result => markbinder.processDynamicResources(this.sourcePath, result)) diff --git a/src/lib/markbind/src/parser.js b/src/lib/markbind/src/parser.js index b38671bbf7..3fb28f84a6 100644 --- a/src/lib/markbind/src/parser.js +++ b/src/lib/markbind/src/parser.js @@ -292,7 +292,9 @@ class Parser { node.name = node.name.toLowerCase(); } - if ((/^h[1-6]$/).test(node.name) && !node.attribs.id) { + const isHeadingTag = (/^h[1-6]$/).test(node.name); + + if (isHeadingTag && !node.attribs.id) { const textContent = utils.getTextContent(node); // remove the '<' and '>' symbols that markdown-it uses to escape '<' and '>' const cleanedContent = textContent.replace(/<|>/g, ''); @@ -354,7 +356,7 @@ class Parser { componentParser.postParseComponents(node, this._onError); // If a fixed header is applied to this page, generate dummy spans as anchor points - if (config.fixedHeader && (/^h[1-6]$/).test(node.name) && node.attribs.id) { + if (config.fixedHeader && isHeadingTag && node.attribs.id) { cheerio(node).append(cheerio.parseHTML(``)); } From 6bdb2dd69f041b8600b9c8e98d44be0b9d54085e Mon Sep 17 00:00:00 2001 From: Tan Yuanhong Date: Sun, 12 Apr 2020 19:43:52 +0800 Subject: [PATCH 3/5] Update documentation and remove unnecessary code --- asset/css/markbind.css | 14 --- asset/js/setup.js | 59 +++++-------- docs/_markbind/headers/header.md | 2 +- docs/_markbind/layouts/devGuide/header.md | 2 +- docs/_markbind/layouts/devGuide/navigation.md | 2 +- docs/_markbind/layouts/userGuide/header.md | 2 +- docs/_markbind/navigation/devGuideSections.md | 2 + docs/about.md | 24 +++-- docs/devGuide/contributing.md | 4 +- docs/dg-site.json | 2 +- docs/userGuide/syntax/headers.mbdf | 11 +++ docs/userGuide/usingPlugins.md | 11 ++- src/Page.js | 49 ++++++++++- src/Site.js | 10 +++ src/constants.js | 5 +- .../markbind/src/parsers/componentParser.js | 23 +++++ .../preprocessors/componentPreprocessor.js | 28 ------ src/plugins/codeBlockCopyButtons.js | 2 +- .../default/markbind-plugin-anchors.css | 23 +++++ .../default/markbind-plugin-anchors.js | 11 ++- src/plugins/googleAnalytics.js | 2 +- src/util/pluginUtil.js | 6 +- .../_markbind/plugins/testMarkbindPlugin.js | 13 ++- .../plugins/testMarkbindPluginScript.js | 2 + .../plugins/testMarkbindPluginStylesheet.css | 3 + .../_markbind/plugins/testMarkbindPlugin.js | 13 ++- .../plugins/testMarkbindPluginScript.js | 2 + .../plugins/testMarkbindPluginStylesheet.css | 3 + .../test_site/expected/bugs/index.html | 7 +- test/functional/test_site/expected/index.html | 79 ++++++++--------- .../expected/markbind/css/markbind.css | 14 --- .../test_site/expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ .../testMarkbindPluginScript.js | 2 + .../testMarkbindPluginStylesheet.css | 3 + .../SpecifyingRequirements._include_.html | 6 +- .../expected/sub_site/index._include_.html | 2 +- .../test_site/expected/sub_site/index.html | 9 +- .../sub_site/nested_sub_site/index.html | 7 +- .../test_site/expected/testAfterSetup.html | 7 +- .../expected/testAnchorGeneration.html | 87 ++++++++++--------- .../expected/testAntiFOUCStyles.html | 7 +- .../test_site/expected/testCodeBlocks.html | 7 +- .../test_site/expected/testDates.html | 9 +- .../expected/testEmptyFrontmatter.html | 7 +- .../expected/testExternalScripts.html | 7 +- .../testImportVariables._include_.html | 4 +- .../expected/testImportVariables.html | 11 +-- .../expected/testIncludeMultipleModals.html | 7 +- .../expected/testIncludePluginsRendered.html | 7 +- .../test_site/expected/testLayouts.html | 7 +- .../expected/testLayoutsOverride.html | 7 +- .../testPanels/NestedPanel._include_.html | 2 +- .../NormalPanelContent._include_.html | 2 +- .../PanelNormalSource._include_.html | 2 +- .../PanelSourceContainsSegment._include_.html | 4 +- .../testPanels/boilerTestPanel._include_.html | 2 +- .../testPanels/notInside._include_.html | 2 +- .../testPanelsWithImportedVariables.html | 7 +- .../test_site/expected/testPlantUML.html | 7 +- .../expected/testPopoverTrigger.html | 7 +- .../test_site/expected/testThumbnails.html | 29 ++++--- .../expected/testTooltipSpacing.html | 9 +- .../test_site/expected/test_md_fragment.html | 9 +- .../expected/index.html | 1 + .../expected/markbind/css/markbind.css | 14 --- .../expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ .../test_site_convert/expected/Home.html | 1 + .../test_site_convert/expected/Page-1.html | 3 +- .../test_site_convert/expected/_Footer.html | 1 + .../test_site_convert/expected/_Sidebar.html | 1 + .../test_site_convert/expected/about.html | 3 +- .../expected/contents/topic1.html | 3 +- .../expected/contents/topic2.html | 1 + .../expected/contents/topic3a.html | 1 + .../expected/contents/topic3b.html | 1 + .../test_site_convert/expected/index.html | 1 + .../expected/markbind/css/markbind.css | 14 --- .../expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ .../expected/index.html | 3 +- .../expected/markbind/css/markbind.css | 14 --- .../expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ .../expected/index.html | 5 +- .../expected/markbind/css/markbind.css | 14 --- .../expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ .../test_site_special_tags/site.json | 1 + .../expected/contents/topic1.html | 3 +- .../expected/contents/topic2.html | 1 + .../expected/contents/topic3a.html | 1 + .../expected/contents/topic3b.html | 1 + .../test_default/expected/index.html | 13 +-- .../expected/markbind/css/markbind.css | 14 --- .../expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ .../test_minimal/expected/index.html | 3 +- .../expected/markbind/css/markbind.css | 14 --- .../expected/markbind/js/setup.js | 59 +++++-------- .../markbind-plugin-anchors.css | 23 +++++ 102 files changed, 753 insertions(+), 668 deletions(-) create mode 100644 src/plugins/default/markbind-plugin-anchors.css create mode 100644 test/functional/test_site/_markbind/plugins/testMarkbindPluginScript.js create mode 100644 test/functional/test_site/_markbind/plugins/testMarkbindPluginStylesheet.css create mode 100644 test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginScript.js create mode 100644 test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginStylesheet.css create mode 100644 test/functional/test_site/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css create mode 100644 test/functional/test_site/expected/plugins/testMarkbindPlugin/testMarkbindPluginScript.js create mode 100644 test/functional/test_site/expected/plugins/testMarkbindPlugin/testMarkbindPluginStylesheet.css create mode 100644 test/functional/test_site_algolia_plugin/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css create mode 100644 test/functional/test_site_convert/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css create mode 100644 test/functional/test_site_expressive_layout/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css create mode 100644 test/functional/test_site_special_tags/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css create mode 100644 test/functional/test_site_templates/test_default/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css create mode 100644 test/functional/test_site_templates/test_minimal/expected/plugins/markbind-plugin-anchors/markbind-plugin-anchors.css diff --git a/asset/css/markbind.css b/asset/css/markbind.css index e21c7c4808..24462b16fa 100644 --- a/asset/css/markbind.css +++ b/asset/css/markbind.css @@ -73,20 +73,6 @@ kbd { outline: none !important; } -.fa.fa-anchor { - color: #ccc; - display: inline; - font-size: 14px; - margin-left: 10px; - padding: 3px; - text-decoration: none; - visibility: hidden; -} - -.fa.fa-anchor:hover { - color: #555; -} - code.hljs.inline { background: #f8f8f8; color: #333; diff --git a/asset/js/setup.js b/asset/js/setup.js index 40e37c1bb1..b2f155a254 100644 --- a/asset/js/setup.js +++ b/asset/js/setup.js @@ -13,61 +13,45 @@ function scrollToUrlAnchorHeading() { } } -function flattenModals() { - jQuery('.modal').each((index, modal) => { - jQuery(modal).detach().appendTo(jQuery('#app')); - }); -} - function insertCss(cssCode) { const newNode = document.createElement('style'); newNode.innerHTML = cssCode; document.getElementsByTagName('head')[0].appendChild(newNode); } -function setupAnchors() { +function setupAnchorsForFixedNavbar() { const headerSelector = jQuery('header'); const isFixed = headerSelector.filter('.header-fixed').length !== 0; + if (!isFixed) { + return; + } + const headerHeight = headerSelector.height(); const bufferHeight = 1; - if (isFixed) { - jQuery('.nav-inner').css('padding-top', `calc(${headerHeight}px)`); - jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); - insertCss( - `span.anchor { - position: relative; - top: calc(-${headerHeight}px - ${bufferHeight}rem) - }`, - ); - insertCss(`span.card-container::before { - display: block; - content: ''; - margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); - height: calc(${headerHeight}px + ${bufferHeight}rem); - }`); - } + jQuery('.nav-inner').css('padding-top', `calc(${headerHeight}px)`); + jQuery('#content-wrapper').css('padding-top', `calc(${headerHeight}px)`); + insertCss( + `span.anchor { + position: relative; + top: calc(-${headerHeight}px - ${bufferHeight}rem) + }`, + ); + insertCss(`span.card-container::before { + display: block; + content: ''; + margin-top: calc(-${headerHeight}px - ${bufferHeight}rem); + height: calc(${headerHeight}px + ${bufferHeight}rem); + }`); jQuery('h1, h2, h3, h4, h5, h6, .header-wrapper').each((index, heading) => { if (heading.id) { - jQuery(heading).on('mouseenter', - () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'visible')); - jQuery(heading).on('mouseleave', - () => jQuery(heading).find('.fa.fa-anchor').css('visibility', 'hidden')); - if (isFixed) { - jQuery(heading).removeAttr('id'); // to avoid duplicated id problem - } + jQuery(heading).removeAttr('id'); // to avoid duplicated id problem } }); - jQuery('.fa-anchor').each((index, anchor) => { - jQuery(anchor).on('click', function () { - window.location.href = jQuery(this).attr('href'); - }); - }); } function updateSearchData(vm) { jQuery.getJSON(`${baseUrl}/siteData.json`) .then((siteData) => { - // eslint-disable-next-line no-param-reassign vm.searchData = siteData.pages; }); } @@ -95,9 +79,8 @@ function executeAfterCreatedRoutines() { } function executeAfterMountedRoutines() { - flattenModals(); scrollToUrlAnchorHeading(); - setupAnchors(); + setupAnchorsForFixedNavbar(); MarkBind.executeAfterSetupScripts.resolve(); } diff --git a/docs/_markbind/headers/header.md b/docs/_markbind/headers/header.md index 57f169c4b7..9d621f94e7 100644 --- a/docs/_markbind/headers/header.md +++ b/docs/_markbind/headers/header.md @@ -1,4 +1,4 @@ -
+
diff --git a/docs/_markbind/layouts/devGuide/header.md b/docs/_markbind/layouts/devGuide/header.md index 57f169c4b7..9d621f94e7 100644 --- a/docs/_markbind/layouts/devGuide/header.md +++ b/docs/_markbind/layouts/devGuide/header.md @@ -1,4 +1,4 @@ -
+
diff --git a/docs/_markbind/layouts/devGuide/navigation.md b/docs/_markbind/layouts/devGuide/navigation.md index 8d0646dcc3..5317fb961d 100644 --- a/docs/_markbind/layouts/devGuide/navigation.md +++ b/docs/_markbind/layouts/devGuide/navigation.md @@ -5,5 +5,5 @@ * [Developer Guide]({{baseUrl}}/devGuide/devGuide.html) * [Maintainer Guide]({{baseUrl}}/devGuide/maintainerGuide.html) * [Contributing]({{baseUrl}}/devGuide/contributing.html) - + * [Code of Conduct]({{baseUrl}}/devGuide/contributing/code-of-conduct.html) diff --git a/docs/_markbind/layouts/userGuide/header.md b/docs/_markbind/layouts/userGuide/header.md index 57f169c4b7..9d621f94e7 100644 --- a/docs/_markbind/layouts/userGuide/header.md +++ b/docs/_markbind/layouts/userGuide/header.md @@ -1,4 +1,4 @@ -
+
diff --git a/docs/_markbind/navigation/devGuideSections.md b/docs/_markbind/navigation/devGuideSections.md index 49b5e0f336..5317fb961d 100644 --- a/docs/_markbind/navigation/devGuideSections.md +++ b/docs/_markbind/navigation/devGuideSections.md @@ -4,4 +4,6 @@ * [Developer Guide]({{baseUrl}}/devGuide/devGuide.html) * [Maintainer Guide]({{baseUrl}}/devGuide/maintainerGuide.html) +* [Contributing]({{baseUrl}}/devGuide/contributing.html) + * [Code of Conduct]({{baseUrl}}/devGuide/contributing/code-of-conduct.html) diff --git a/docs/about.md b/docs/about.md index d5dcbaae5b..6813e4bdc8 100644 --- a/docs/about.md +++ b/docs/about.md @@ -11,22 +11,36 @@ The **project team**: * [**Aaron Chong Jun Hao**](https://github.com/acjh): _Project Mentor_ since Jul 2018, _Team Lead_ for Aug 2017 - Jun 2018 +* [**Ang Ze Yu**](https://www.github.com/ang-zeyu): _Project Member_ since Jan 2020 * [**Chng Zhi Xuan**](https://github.com/Chng-Zhi-Xuan): _Project Member_ since May 2018 * [**Chua Yun Zhi Nicholas**](https://github.com/nicholaschuayunzhi): _Project Member_ since Jan 2018 * [**Damith C. Rajapakse**](https://www.comp.nus.edu.sg/~damithch/): _Project Mentor_ since Aug 2016 +* [**Daryl Tan**](https://www.github.com/openorclose): _Project Member_ since May 2019 * [**Jamos Tay**](https://github.com/jamos-tay): _Project Member_ since Aug 2018 -* [**Lin Si Jie**](https://www.github.com/sijie123): _Project Member_ since Jan 2019 -* [**Lu Yang Kenneth**](https://github.com/luyangkenneth): _Project Member_ since Jan 2019 -* [**Maddi Aadyaa**](https://www.github.com/amad-person): _Project Member_ since Jan 2019 -* [**Marvin Chin Kun Song**](https://www.github.com/marvinchin): _Project Member_ since Jan 2019 +* [**Labayna Neil Brian Narido**](https://www.github.com/nbriannl): _Project Member_ since Jan 2020 +* [**Marvin Chin Kun Song**](https://www.github.com/marvinchin): _Project Mentor_ since Jan 2020, _Project Member_ for Jan 2019 - Aug 2019 * [**Tan Wang Leng**](https://github.com/yamgent): Current _Team Lead_ since Jul 2018, _Project Member_ for May 2018 - Jun 2018 -* [**Tan Zhen Yong**](https://www.github.com/Xenonym): _Project Member_ since Jan 2019 +* [**Tan Yuanhong**](https://www.github.com/le0tan): _Project Member_ since Jan 2020 +* [**Yash Chowdhary**](https://www.github.com/yash-chowdhary): _Project Member_ since Jan 2020 +* [**Yip Seng Yeun**](https://www.github.com/alyip98): _Project Member_ since May 2019 + +Contributors: +* [**Ahmed Bahajjaj**](https://www.github.com/madanalogy): _Contributor_ since Jan 2020 +* [**Daryl Chan**](https://www.github.com/dvrylc): _Contributor_ since Jan 2020 +* [**Jeremy Tan Jie Rui**](https://www.github.com/Parcly-Taxel): _Contributor_ since Jan 2020 +* [**Joshua Wong**](https://www.github.com/hcwong): _Contributor_ since Jan 2020 +* [**Phang Chun Rong**](https://www.github.com/crphang): _Contributor_ since Jan 2020 +* [**Tejas Bhuwania**](https://www.github.com/yash-chowdhary): _Contributor_ since Jan 2020 Past Members: * [**Daniel Berzin Chua Yuan Siang**](https://github.com/danielbrzn): _Project Member_ for Jan 2018 - Aug 2018 * [**Jia Zhixin**](https://github.com/nusjzx): _Project Member_ for May 2018 - Aug 2018 * [**Jiang Sheng**](https://github.com/Gisonrg): _Founding Member_ and _Team Lead_ for Aug 2016 - Jul 2017 +* [**Lin Si Jie**](https://www.github.com/sijie123): _Project Member_ for Jan 2019 - Aug 2019 +* [**Lu Yang Kenneth**](https://github.com/luyangkenneth): _Project Member_ for Jan 2019 - Aug 2019 +* [**Maddi Aadyaa**](https://www.github.com/amad-person): _Project Member_ for Jan 2019 - Aug 2019 * [**Rachael Sim Hwee Ling**](https://github.com/rachx): _Project Member_ for Jan 2018 - Dec 2018 +* [**Tan Zhen Yong**](https://www.github.com/Xenonym): _Project Member_ for Jan 2019 - Aug 2019 :glyphicon-send: You can **email us** at `markbind` at `comp.nus.edu.sg` diff --git a/docs/devGuide/contributing.md b/docs/devGuide/contributing.md index 8130ef5d73..ad62363f52 100644 --- a/docs/devGuide/contributing.md +++ b/docs/devGuide/contributing.md @@ -14,7 +14,7 @@ This project and everyone participating in it are governed by our [Code of Condu ## Things to do before getting started -Make sure you have the project set up and ready. We have guides detailing what we [expect our contributors to know]({{baseurl}}/devguide/devguide#requirement), our required [environment]({{baseurl}}/devGuide.html#environment) and our [development process]({{baseurl}}/devGuide.html#development-process). +Make sure you have the project set up and ready. We have guides detailing what we [expect our contributors to know]({{baseUrl}}/devGuide/devGuide.html#requirement), our required [environment]({{baseUrl}}/devGuide/devGuide.html#environment) and our [development process]({{baseUrl}}/devGuide/devGuide.html#development-process). ## How to contribute @@ -55,7 +55,7 @@ Unsure where to begin contributing to MarkBind? We recommend that you start off by visiting the [Getting Started](https://markbind.org/userGuide/gettingStarted.html) section in the User Guide and try out MarkBind as a user. Exploring and understanding the various features it provides. -If you have not done so yet, we also recommend visiting the [Developer Guide]({{baseurl}}/devGuide/index.html) to learn about the [structure of the project]({{baseurl}}/devGuide/index.html#project-structure), how to set up the [developer environment]({{baseurl}}/devGuide/index.html#development-process), and how to run [tests]({{baseurl}}/devGuide/devGuide.html#testing). +If you have not done so yet, we also recommend visiting the [Developer Guide]({{baseUrl}}/devGuide/index.html) to learn about the [structure of the project]({{baseUrl}}/devGuide/index.html#project-structure), how to set up the [developer environment]({{baseUrl}}/devGuide/index.html#development-process), and how to run [tests]({{baseUrl}}/devGuide/devGuide.html#testing). When you're ready, you can start by looking through these issues marked good first issue. diff --git a/docs/dg-site.json b/docs/dg-site.json index 6f17569272..a42b6b70be 100644 --- a/docs/dg-site.json +++ b/docs/dg-site.json @@ -3,7 +3,7 @@ "titlePrefix": "MarkBind", "pages": [ { - "glob": ["**/*.mbd", "*.md", "devGuide/*.md"] + "glob": ["**/*.mbd", "*.md", "devGuide/*.md", "devGuide/*/*.md"] }, { "src": "index.md", diff --git a/docs/userGuide/syntax/headers.mbdf b/docs/userGuide/syntax/headers.mbdf index 9cd3fef74f..983273a366 100644 --- a/docs/userGuide/syntax/headers.mbdf +++ b/docs/userGuide/syntax/headers.mbdf @@ -23,6 +23,17 @@ In the page that you want to include the header: ``` +You can fix the header to be always on the top by add `class="header-fixed"` attribute to the `
` tag of your header file: + +{{ icon_example }} +**`_markbind/headers/`**`header.md`: +```html +
+ +
+ +``` + Notes: - Any inline headers will be removed by MarkBind to ensure compatibility with header files. - If a [Layout]({{ baseUrl }}/userGuide/tweakingThePageStructure.html#page-layouts) is specified, the header file specified in the `` will override the header within the Layout. diff --git a/docs/userGuide/usingPlugins.md b/docs/userGuide/usingPlugins.md index e154828d93..a825b84c65 100644 --- a/docs/userGuide/usingPlugins.md +++ b/docs/userGuide/usingPlugins.md @@ -117,14 +117,21 @@ Plugins can implement the methods `getLinks` and `getScripts` to add additional - `frontMatter`: The frontMatter of the page being processed, in case any frontMatter data is required. - `utils`: Object containing the following utility functions - `buildStylesheet(href)`: Builds a stylesheet link element with the specified `href`. - - Should return an array of string data containing link elements to be added. + - Should return an array of strings containing link elements to be added. - `getScripts(content, pluginContext, frontMatter, utils)`: Called to get script elements to be added after the body of the page. - `content`: The rendered HTML. - `pluginContext`: User provided parameters for the plugin. This can be specified in the `site.json`. - `frontMatter`: The frontMatter of the page being processed, in case any frontMatter data is required. - `utils`: Object containing the following utility functions - `buildScript(src)`: Builds a script element with the specified `src`. - - Should return an array of string data containing script elements to be added. + - Should return an array of strings containing script elements to be added. + + + +You can set an absolute or relative file path as the `src` or `href` attribute in your `'], + getLinks: (content, pluginContext, frontMatter, utils) => [utils.buildStylesheet(TEST_STYLESHEET_FILE)], + getScripts: (content, pluginContext, frontMatter, utils) => [ + // Explicitly resolve to test absolute file paths + utils.buildScript(path.resolve(__dirname, TEST_SCRIPT_FILE)), + '', + ], }; diff --git a/test/functional/test_site/_markbind/plugins/testMarkbindPluginScript.js b/test/functional/test_site/_markbind/plugins/testMarkbindPluginScript.js new file mode 100644 index 0000000000..06af89334a --- /dev/null +++ b/test/functional/test_site/_markbind/plugins/testMarkbindPluginScript.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-alert +alert('External plugin script file loaded!'); diff --git a/test/functional/test_site/_markbind/plugins/testMarkbindPluginStylesheet.css b/test/functional/test_site/_markbind/plugins/testMarkbindPluginStylesheet.css new file mode 100644 index 0000000000..6d91334c05 --- /dev/null +++ b/test/functional/test_site/_markbind/plugins/testMarkbindPluginStylesheet.css @@ -0,0 +1,3 @@ +strong { + color: #138bf0; +} diff --git a/test/functional/test_site/expected/_markbind/plugins/testMarkbindPlugin.js b/test/functional/test_site/expected/_markbind/plugins/testMarkbindPlugin.js index e696ada3a8..5b2af6261a 100644 --- a/test/functional/test_site/expected/_markbind/plugins/testMarkbindPlugin.js +++ b/test/functional/test_site/expected/_markbind/plugins/testMarkbindPlugin.js @@ -1,4 +1,8 @@ const cheerio = module.parent.require('cheerio'); +const path = require('path'); + +const TEST_STYLESHEET_FILE = 'testMarkbindPluginStylesheet.css'; +const TEST_SCRIPT_FILE = 'testMarkbindPluginScript.js'; module.exports = { preRender: (content, pluginContext) => @@ -8,7 +12,10 @@ module.exports = { $('#test-markbind-plugin').append(`${pluginContext.post}`); return $.html(); }, - getLinks: (content, pluginContext, frontMatter, utils) => [utils.buildStylesheet('STYLESHEET_LINK')], - getScripts: (content, pluginContext, frontMatter, utils) => - [utils.buildScript('SCRIPT_LINK'), ''], + getLinks: (content, pluginContext, frontMatter, utils) => [utils.buildStylesheet(TEST_STYLESHEET_FILE)], + getScripts: (content, pluginContext, frontMatter, utils) => [ + // Explicitly resolve to test absolute file paths + utils.buildScript(path.resolve(__dirname, TEST_SCRIPT_FILE)), + '', + ], }; diff --git a/test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginScript.js b/test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginScript.js new file mode 100644 index 0000000000..06af89334a --- /dev/null +++ b/test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginScript.js @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-alert +alert('External plugin script file loaded!'); diff --git a/test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginStylesheet.css b/test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginStylesheet.css new file mode 100644 index 0000000000..6d91334c05 --- /dev/null +++ b/test/functional/test_site/expected/_markbind/plugins/testMarkbindPluginStylesheet.css @@ -0,0 +1,3 @@ +strong { + color: #138bf0; +} diff --git a/test/functional/test_site/expected/bugs/index.html b/test/functional/test_site/expected/bugs/index.html index 80288776b9..02e6f1500b 100644 --- a/test/functional/test_site/expected/bugs/index.html +++ b/test/functional/test_site/expected/bugs/index.html @@ -15,7 +15,8 @@ - + + @@ -63,9 +64,9 @@ const enableSearch = true - + diff --git a/test/functional/test_site/expected/index.html b/test/functional/test_site/expected/index.html index b0e4daac7d..cd35b57563 100644 --- a/test/functional/test_site/expected/index.html +++ b/test/functional/test_site/expected/index.html @@ -20,7 +20,8 @@ - + + @@ -49,12 +50,12 @@