Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions docs/userGuide/contentAuthoring.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,50 @@ Front matter can also be included from a separate file, just like how content ca

This will result in `index.md` having the title `Binary Search Tree` and the specified keywords in order for it to be looked up through the search bar.

### Inserting content into a page's head element

While authoring your website, you may want to have your own CSS or Javascript files to be included in a page.

- Start by creating a head file with Markdown extension (`.md`) in the `_markbind/head` folder.
- More than one head file can be created for different pages.

- Author your `<style>` elements for CSS files and `<link>` elements for Javascript files using HTML as shown below.
- Ensure that your URLs start from the root directory, by using <code>{<span></span>{baseUrl}}/</code> when you are referencing your files.

```css
/* In yourCSSFolder/subfolder/myCustomStyle.css */
p {
background: lightskyblue;
}
```

```js
// In yourScriptFolder/myCustomScript.js
alert("Welcome to my website!");
```

```html
<!-- In _markbind/head/compiledRef.md -->
<link rel="stylesheet" href="{{baseUrl}}/yourCSSFolder/subfolder/myCustomStyle.css">

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{{baseUrl}} is not getting rendered (see Netlify preview).

<script src="{{baseUrl}}/yourScriptFolder/myCustomScript.js"></script>
```

- Specify the head file in pages that uses it, by specifying the [front matter](#front-matter) `head` attribute.

```html
<!-- In the page you want the head file to be in -->
<frontmatter>
head: compiledRef.md
</frontmatter>
```

The head file contents will be placed near the end of the page's head tag.
Your head file will override existing Bootstrap and MarkBind CSS styles if there is an overlap of selectors.

Note:
- You may specify raw `.js` or `.css` files as your head file if you wish to do so.
- Only one head file can be specified in a page, and you **must** include its file extension.

### Site Navigation

A site navigation bar is a fixed menu on the left side of your page contents, that allows the viewer to navigate your site pages.
Expand Down
23 changes: 22 additions & 1 deletion lib/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const MarkBind = require('./markbind/lib/parser');
const md = require('./markbind/lib/markdown-it');

const FOOTERS_FOLDER_PATH = '_markbind/footers';
const HEAD_FOLDER_PATH = '_markbind/head';
const NAVIGATION_FOLDER_PATH = '_markbind/navigation';

const FLEX_BODY_DIV_ID = 'flex-body';
Expand Down Expand Up @@ -59,9 +60,10 @@ function Page(pageConfig) {
this.resultPath = pageConfig.resultPath;

this.frontMatter = {};
this.headFileReferences = '';
this.headings = {};
this.includedFiles = {};
this.headingIndexingLevel = pageConfig.headingIndexingLevel;
this.includedFiles = {};
}

/**
Expand Down Expand Up @@ -160,6 +162,7 @@ Page.prototype.prepareTemplateData = function () {
baseUrl: this.baseUrl,
content: this.content,
faviconUrl: this.faviconUrl,
headFileReferences: this.headFileReferences,
title: prefixedTitle,
};
};
Expand Down Expand Up @@ -293,6 +296,22 @@ Page.prototype.insertSiteNav = function (pageData) {
+ '</div>';
};

Page.prototype.collectHeadFiles = function () {
const { head } = this.frontMatter;
if (!head) {
return;
}
const headFilePath = path.join(this.rootPath, HEAD_FOLDER_PATH, head);
const headFileContent = fs.readFileSync(headFilePath, 'utf8');
// Set head file as an includedFile
this.includedFiles[headFilePath] = true;
// Map variables
const newBaseUrl = calculateNewBaseUrl(this.sourcePath, this.rootPath, this.baseUrlMap) || '';
const userDefinedVariables = this.userDefinedVariablesMap[path.join(this.rootPath, newBaseUrl)];
const headFileMappedData = nunjucks.renderString(headFileContent, userDefinedVariables);
this.headFileReferences = headFileMappedData.trim();
};

Page.prototype.generate = function (builtFiles) {
this.includedFiles = {};
this.includedFiles[this.sourcePath] = true;
Expand Down Expand Up @@ -325,6 +344,8 @@ Page.prototype.generate = function (builtFiles) {
const baseUrl = newBaseUrl ? `${this.baseUrl}/${newBaseUrl}` : this.baseUrl;
const hostBaseUrl = this.baseUrl;

this.collectHeadFiles();
this.headFileReferences = nunjucks.renderString(this.headFileReferences, { baseUrl, hostBaseUrl });
this.content = nunjucks.renderString(this.content, { baseUrl, hostBaseUrl });
return fs.outputFileAsync(this.resultPath, this.template(this.prepareTemplateData()));
})
Expand Down
9 changes: 9 additions & 0 deletions lib/Site.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const FAVICON_DEFAULT_PATH = 'favicon.ico';
const FONT_AWESOME_PATH = 'asset/font-awesome.csv';
const FOOTER_PATH = '_markbind/footers/footer.md';
const GLYPHICONS_PATH = 'asset/glyphicons.csv';
const HEAD_FOLDER_PATH = '_markbind/head';
const INDEX_MARKDOWN_FILE = 'index.md';
const PAGE_TEMPLATE_NAME = 'page.ejs';
const SITE_CONFIG_NAME = 'site.json';
Expand Down Expand Up @@ -170,6 +171,7 @@ Site.initSite = function (rootPath) {
const boilerplatePath = path.join(rootPath, BOILERPLATE_FOLDER_NAME);
const configPath = path.join(rootPath, SITE_CONFIG_NAME);
const footerPath = path.join(rootPath, FOOTER_PATH);
const headFolderPath = path.join(rootPath, HEAD_FOLDER_PATH);
const indexPath = path.join(rootPath, INDEX_MARKDOWN_FILE);
const siteNavPath = path.join(rootPath, SITE_NAV_PATH);
const userDefinedVariablesPath = path.join(rootPath, USER_VARIABLES_PATH);
Expand Down Expand Up @@ -210,6 +212,13 @@ Site.initSite = function (rootPath) {
}
return fs.outputFileAsync(footerPath, FOOTER_DEFAULT);
})
.then(() => fs.accessAsync(headFolderPath))
.catch(() => {
if (fs.existsSync(headFolderPath)) {
return Promise.resolve();
}
return fs.mkdirSync(headFolderPath);
})
.then(() => fs.accessAsync(siteNavPath))
.catch(() => {
if (fs.existsSync(siteNavPath)) {
Expand Down
1 change: 1 addition & 0 deletions lib/template/page.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<link rel="stylesheet" href="<%- asset.highlight %>">
<link rel="stylesheet" href="<%- asset.markbind %>">
<link rel="stylesheet" href="<%- asset.siteNavCss %>">
<%- headFileReferences %>
<% if (faviconUrl) { %><link rel="icon" href="<%- faviconUrl %>"><% } %>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions test/test_site/_markbind/head/myCustomHead.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<script src="{{baseUrl}}\headFiles\customScript.js"></script>
1 change: 1 addition & 0 deletions test/test_site/expected/bugs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<link rel="stylesheet" href="..\markbind\css\github.min.css">
<link rel="stylesheet" href="..\markbind\css\markbind.css">
<link rel="stylesheet" href="..\markbind\css\site-nav.css">

<link rel="icon" href="/test_site/favicon.png">
</head>
<body>
Expand Down
2 changes: 2 additions & 0 deletions test/test_site/expected/headFiles/customScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line no-console
console.info('custom script inserted into head successfully!');
1 change: 1 addition & 0 deletions test/test_site/expected/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<link rel="stylesheet" href="markbind\css\github.min.css">
<link rel="stylesheet" href="markbind\css\markbind.css">
<link rel="stylesheet" href="markbind\css\site-nav.css">
<script src="/test_site\headFiles\customScript.js"></script>
<link rel="icon" href="/test_site/favicon.png">
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions test/test_site/expected/siteData.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"title": "Hello World",
"footer": "footer.md",
"siteNav": "site-nav.md",
"head": "myCustomHead.md",
"src": "index.md"
},
{
Expand Down
1 change: 1 addition & 0 deletions test/test_site/expected/sub_site/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<link rel="stylesheet" href="..\markbind\css\github.min.css">
<link rel="stylesheet" href="..\markbind\css\markbind.css">
<link rel="stylesheet" href="..\markbind\css\site-nav.css">

<link rel="icon" href="/test_site/favicon.png">
</head>
<body>
Expand Down
2 changes: 2 additions & 0 deletions test/test_site/headFiles/customScript.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// eslint-disable-next-line no-console
console.info('custom script inserted into head successfully!');
1 change: 1 addition & 0 deletions test/test_site/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
title: Hello World
footer: footer.md
siteNav: site-nav.md
head: myCustomHead.md
</frontmatter>

<include src="components/header.md" />
Expand Down
10 changes: 8 additions & 2 deletions test/unit/Site.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ test('Site Init in existing directory generates correct assets', async () => {
await Site.initSite('');
const paths = Object.keys(fs.vol.toJSON());
const originalNumFiles = Object.keys(json).length;
const expectedNumBuilt = 6;
const expectedNumBuilt = 7;
expect(paths.length).toEqual(originalNumFiles + expectedNumBuilt);

// _boilerplates
Expand All @@ -84,6 +84,9 @@ test('Site Init in existing directory generates correct assets', async () => {
// footer.md
expect(fs.readFileSync(path.resolve('_markbind/footers/footer.md'), 'utf8')).toEqual(FOOTER_MD_DEFAULT);

// head folder
expect(fs.existsSync(path.resolve('_markbind/head'), 'utf8')).toEqual(true);

// site-nav.md
expect(fs.readFileSync(path.resolve('_markbind/navigation/site-nav.md'), 'utf8'))
.toEqual(SITE_NAV_MD_DEFAULT);
Expand All @@ -107,7 +110,7 @@ test('Site Init in directory which does not exist generates correct assets', asy
await Site.initSite('newDir');
const paths = Object.keys(fs.vol.toJSON());
const originalNumFiles = Object.keys(json).length;
const expectedNumBuilt = 6;
const expectedNumBuilt = 7;

expect(paths.length).toEqual(originalNumFiles + expectedNumBuilt);

Expand All @@ -117,6 +120,9 @@ test('Site Init in directory which does not exist generates correct assets', asy
expect(fs.readFileSync(path.resolve('newDir/_markbind/footers/footer.md'), 'utf8'))
.toEqual(FOOTER_MD_DEFAULT);

// head folder
expect(fs.existsSync(path.resolve('newDir/_markbind/head'), 'utf8')).toEqual(true);

// site-nav.md
expect(fs.readFileSync(path.resolve('newDir/_markbind/navigation/site-nav.md'), 'utf8'))
.toEqual(SITE_NAV_MD_DEFAULT);
Expand Down