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
1 change: 1 addition & 0 deletions docs/_markbind/layouts/devGuide/navigation.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* [Setting up]({{baseUrl}}/devGuide/settingUp.html)
* [Workflow]({{baseUrl}}/devGuide/workflow.html)
* [Design]({{baseUrl}}/devGuide/design.html)
* [Writing Plugins]({{baseUrl}}/devGuide/writingPlugins.html)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Writing plugins is not just for devs. Even regular users are supposedly can write plugins. Give at least a link from the UG as well.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added. I think the main content should stay in the devguide though, since the large majority of users won't be writing such plugins.

* [Project management]({{baseUrl}}/devGuide/projectManagement.html)
* Appendices :expanded:
* [Style guides]({{baseUrl}}/devGuide/styleGuides.html)
Expand Down
171 changes: 171 additions & 0 deletions docs/devGuide/writingPlugins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<variable name="title">Writing Plugins</variable>
<frontmatter>
title: "{{ title }}"
layout: devGuide
pageNav: default
</frontmatter>

# {{ title }}

MarkBind plugins are `js` scripts that are loaded and run during the page generation. MarkBind allows plugins to modify a page's content during the page generation process, amongst other things.

This page details the available interfaces you may use to write plugins.

## Rendering

![MarkBind Rendering]({{baseUrl}}/images/rendering.png)

MarkBind provides two entry points for modifying the page, pre-render and post-render. These are controlled by implementing the `preRender()` and `postRender()` functions in the plugin:

- `preRender(content, pluginContext, frontMatter)`: Called before MarkBind renders the source from Markdown to HTML.
- `content`: The raw Markdown of any Markdown file (`.md`, `.mbd`, etc.).
- `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.
- `postRender(content, pluginContext, frontMatter)`: Called after the HTML is rendered, before writing it to a file.
- `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.

MarkBind will call these functions with the respective content, and retrieve a string data that is used for the next step of the page generation process.

An example of a plugin is shown below. The plugin shows two ways of appending a paragraph of text to a specific `div` in the Markdown files:

```js
// myPlugin.js

const cheerio = module.parent.require('cheerio');

module.exports = {
preRender: (content, pluginContext, frontMatter) => content.replace('[Pre-render Placeholder]', `${pluginContext.pre}`),
postRender: (content, pluginContext, frontMatter) => {
const $ = cheerio.load(content, { xmlMode: false });
// Modify the page...
$('#my-div').append(pluginContext.post);
return $.html();
},
};
```

```js
// site.json

{
...
"plugins": [
"myPlugin"
],
"pluginsContext": {
"myPlugin": {
"pre": "<p>Hello</p>",
"post": "<p>Goodbye</p>"
}
}
}
```

```md
// index.md

...
<div id="my-div">
[Pre-render Placeholder]
</div>
```

## Assets

Plugins can implement the methods `getLinks` and `getScripts` to add additional assets to the page.

- `getLinks(content, pluginContext, frontMatter, utils)`: Called to get link elements to be added to the head 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
- `buildStylesheet(href)`: Builds a stylesheet link element with the specified `href`.
- 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 strings containing script elements to be added.

<box type="success" header="Local assets">
<md>
You can set an absolute or relative file path as the `src` or `href` attribute in your `<script>` or `<link>` tags.
MarkBind will copy these assets into the output directory and change the `src` or `href` attributes automatically!
</md>
</box>

An example of a plugin which adds links and scripts to the page:

```js
// myPlugin.js

module.exports = {
getLinks: (content, pluginContext, frontMatter, utils) => [utils.buildStylesheet('STYLESHEET_LINK')],
getScripts: (content, pluginContext, frontMatter, utils) =>
[utils.buildScript('SCRIPT_LINK'), '<script>alert("hello")</script>'],
};

```

This will add the following link and script elements to the page:
- `<link rel="stylesheet" href="STYLESHEET_LINK">`
- `<script src="SCRIPT_LINK"></script>`
- `<script>alert("hello")</script>`

## Live reload

By default, MarkBind treats `.html`, `.md`, `.mbd`, and `.mbdf` as source files, and will rebuild any
pages changed when serving the page.

During the `preRender` and `postRender` stages however, plugins may do custom processing using some other
source file types, as parsed from the raw Markdown, typically requiring rebuilding the site.

Hence, to add custom source files to watch, you can implement the `getSources()` method.

`getSources(content, pluginContext, frontMatter)`: Called _before_ a Markdown file's `preRender` function is called.
- `content`: The raw Markdown of the current Markdown file (`.md`, `.mbd`, etc.).
- `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.

It should return an object, consisting of _at least one of the following fields_:
- `tagMap`: An array consisting of `['tag name', 'source attribute name']` key value pairs.
- MarkBind will automatically search for matching tags with the source attributes, and watch them.
- For relative file paths, _if the tag is part of some included content_ ( eg. `<include />` tags ), it will be resolved against the included page. Otherwise, it is resolved against the page being processed.
- `sources`: An array of source file paths to watch, where relative file paths are resolved only against the page being processed.
- You can also directly return an array of source file paths to watch. ( ie. the `sources` field ) ___(deprecated)___

Example usage of `getSources` from the PlantUML plugin, which allows insertion of PlantUML diagrams using `<puml src="..." >` tags.
This allows files specified by the `src` attributes of `<puml>` tags to be watched:

```js
{
...
getSources: () => ({
tagMap: [['puml', 'src']],
})
}
```

## Special tags

By default, content in html tags are parsed as html and markdown.

However, you might want to create a plugin that has certain special tags containing conflicting syntax
you do not wish to be parsed as html or markdown.

You can implement the `getSpecialTags` method to blacklist the content in these special tags from parsing,
removing such potential conflicts.

- `getSpecialTags(pluginContext)`: Called during initial site generation to blacklist special tags.
- `pluginContext`: User provided parameters for the plugin. This can be specified in the `site.json`.
- Should return an array of string tag names to be blacklisted, with each tag name being at least 2 characters long.

<box type="important">

Note however, that variable interpolation syntax {% raw %}`{{ variable_name }}`{% endraw %} will act as per normal.
Meaning, the user would still be able to use variables in your special tags!
</box>
8 changes: 4 additions & 4 deletions docs/userGuide/cliCommands.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Usage: markbind <command>
Root directory. Default is the current directory.<br>
{{ icon_example }} `./myWebsite`

<panel header="**Options** :fas-cogs:" type="minimal">
<panel header="**Options** :fas-cogs:" type="minimal" expanded>

**Options** :fas-cogs:

Expand Down Expand Up @@ -76,7 +76,7 @@ Usage: markbind <command>
Root directory. The default is the directory where this command was executed.<br>
{{ icon_example }} `./myWebsite`

<panel header="**Options** :fas-cogs:" type="minimal">
<panel header="**Options** :fas-cogs:" type="minimal" expanded>

**Options** :fas-cogs:

Expand Down Expand Up @@ -135,7 +135,7 @@ The caveat is that not building all pages during the initial process, or not reb
Read source files from the `[root]` directory and put the generated files in the specified `[output]` directory<br>
{{ icon_example }} `./myWebsite ../myOutDir`

<panel header="**Options** :fas-cogs:" type="minimal">
<panel header="**Options** :fas-cogs:" type="minimal" expanded>

**Options** :fas-cogs:

Expand Down Expand Up @@ -165,7 +165,7 @@ The caveat is that not building all pages during the initial process, or not reb

**Description:** Deploys the site to the repo's Github pages by pushing everything in the generated site (default dir: `_site`) to the `gh-pages` branch of the current git working directory's remote repo.

<panel header="**Options** :fas-cogs:" type="minimal">
<panel header="**Options** :fas-cogs:" type="minimal" expanded>

**Options** :fas-cogs:

Expand Down
119 changes: 40 additions & 79 deletions docs/userGuide/components/advanced.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,66 @@
## Advanced Tips and Tricks

### Rich formatting in headings/titles
<variable name="slot_info_trigger"><trigger for="on-slots" trigger="click"><strong>^\[S\]^</strong></trigger></variable>
<variable name="slot_type_info_trigger"><trigger for="on-slots" trigger="click">Slot</trigger></variable>

Using the normal syntax, you are only able to use markdown formatting on headings. If you would like more styling options, you can define an element within the component that acts as your heading. This is done by adding a <md>`slot`</md> attribute with the correct name to that element.
### Richer formatting of attributes using slots

<tip-box border-left-color="#00B0F0">
<i style="font-style: normal; font-weight: bold; color: dimgray">Example</i><br>
<panel expanded>
<p slot="header" class="card-title">
<i><strong>
<span style="color:#FF0000;">R </span>
<span style="color:#FF7F00;">A </span>
<span style="color:#FFFF00;">I </span>
<span style="color:#00FF00;">N </span>
<span style="color:#0000FF;">B </span>
<span style="color:#4B0082;">O </span>
<span style="color:#9400D3;">W </span>
</strong></i>
</p>
As shown in this panel, using the header slot allows you to customize the Panel's header using HTML.
</panel>
</tip-box>
<div id="slots">

<tip-box border-left-color="#00B0F0">
<i style="font-style: normal; font-weight: bold; color: dimgray">Example</i><br>
<trigger for="modal:tip-example" trigger="click">Click here to show Modal.</trigger>

<modal id="modal:tip-example">
<div slot="modal-header" class="modal-title text-center">
<span style="font-size:20pt"><span style="color:red;">BIG</span> header</span>
</div>
Modal allows you to style both header and footer individually, with style classes and inline styles.
<div slot="modal-footer" class="text-center">
<span style="font-size:10pt">Tiny <span style="color:green;">footer</span></span>
</div>
</modal>
</tip-box>
Most component attributes allow a richer form of formatting using slots, denoted by an attribute<strong>^\[S\]^</strong> superscript in the respective components' tables.
In other cases, when the option is of type "Slot", only the slot option is available.

<tip-box border-left-color="black">
<i style="font-style: normal; font-weight: bold; color: dimgray">Markup</i>
You can define such a slot within the component by adding a `slot="attribute name"` attribute to any element within the slot.

```html
{{ icon_example }}

<include src="codeAndOutput.md" boilerplate>
<variable name="code">
<panel expanded>
<p slot="header" class="card-title">
<i><strong>
<span style="color:#FF0000;">R </span>
<span style="color:#FF7F00;">A </span>
<span style="color:#FFFF00;">I </span>
<span style="color:#00FF00;">N </span>
<span style="color:#0000FF;">B </span>
<span style="color:#4B0082;">O </span>
<span style="color:#9400D3;">W </span>
<span style="color:#FF0000;">R</span>
<span style="color:#FF7F00;">A</span>
<span style="color:#FFFF00;">I</span>
<span style="color:#00FF00;">N</span>
<span style="color:#0000FF;">B</span>
<span style="color:#4B0082;">O</span>
<span style="color:#9400D3;">W</span>
</strong></i>
</p>
As shown in this panel, using the header slot allows you to customize the Panel's header using HTML.
As shown in this panel, using the header slot
allows you to customize the Panel's header using HTML.
</panel>
</variable>
<variable name="highlightStyle">html</variable>
</include>
</div>

<modal header="Richer formatting of attributes using slots" id="on-slots" large>
<include src="advanced.md#slots" />
</modal>

**Other examples of slots in use**

{{ icon_example }} Custom modal header

<include src="codeAndOutput.md" boilerplate>
<variable name="code">
<trigger for="modal:tip-example" trigger="click">Click here to show Modal.</trigger>

<modal id="modal:tip-example">
<div slot="modal-header" class="modal-title text-center">
<div slot="header" class="modal-title text-center">
<span style="font-size:20pt"><span style="color:red;">BIG</span> header</span>
</div>
Modal allows you to style both header and footer individually, with style classes and inline styles.
<div slot="modal-footer" class="text-center">
<div slot="footer" class="text-center">
<span style="font-size:10pt">Tiny <span style="color:green;">footer</span></span>
</div>
</modal>
```
</tip-box>
<br>
</variable>
<variable name="highlightStyle">html</variable>
</include>

**Box Slot Options:**

Slot name | Default class |
--- | --- |
icon | depends on box's `type` attribute |

{{ icon_example }} Override the default icon for a certain type of box.

Expand Down Expand Up @@ -120,31 +105,7 @@ icon | depends on box's `type` attribute |
use thumbnail as the icon
</box>

**Panel Slot Options:**
Slot name | Default class | Notes
--- | --- | ---
header | `card-title` | Aligning text to the center of the panel is not possible, as the header element does not take up the entire container.

**Modal Slot Options:**
When using slots for Modals, you need to add a single blank line before each `<modal>` tag, in order for the customization to render correctly.

Slot name | Default class | Notes
--- | --- | ---
header <hr style="margin-top:0.2rem; margin-bottom:0" /> <small>`modal-header` <br> (deprecated)</small> | `modal-title` |
footer <hr style="margin-top:0.2rem; margin-bottom:0" /> <small>`modal-footer` <br> (deprecated)</small> | `modal-footer` | Specifying `modal-footer` will override the `ok-text` attribute, and the OK button will not render.

**Popover Slot Options:**
Slot name | Default class
--- | --- | ---
header <hr style="margin-top:0.2rem; margin-bottom:0" /> <small>`title` <br> (deprecated)</small> | `popover-header`
content | `popover-body`

**Dropdown Slot Options:**
Slot name | Default class
--- | ---
header | `dropdown-toggle`

### Inserting custom classes into components
### Inserting custom classes into components {.mt-4 .mb-3}

Every component documented in our user guide allows you to insert your own defined CSS classes.
This is done by adding the `add-class` attribute to a component along with the desired class names.
Expand Down
2 changes: 1 addition & 1 deletion docs/userGuide/formattingContents.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<span class="lead" id="overview">

**MarkBind supports a wide collection of Markdown-like basic content formatting syntax** such as text stying, tables, lists, images, links, etc.
**MarkBind supports a wide collection of Markdown-like basic content formatting syntax** such as text styling, tables, lists, images, links, etc.

</span>

Expand Down
1 change: 1 addition & 0 deletions docs/userGuide/fullSyntaxReference.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
links : ['Links', ['basic', 'reader-facing']],
footnotes: ['Footnotes', ['basic', 'reader-facing']],
images : ['Images', ['basic', 'reader-facing']],
attributes: ['Classes, Attributes & Identifiers', ['basic', 'reader-facing']],
tables : ['Tables', ['basic', 'reader-facing']],
emoji : ['Emoji', ['basic', 'reader-facing']],
icons : ['Icons', ['basic', 'reader-facing']],
Expand Down
Loading