├── .browserslistrc ├── .gitattributes ├── .github ├── scripts │ └── release.js └── workflows │ ├── release.yml │ └── static.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENCE ├── README.md ├── dist ├── docsify-auto-headers.js └── docsify-auto-headers.min.js ├── docs ├── .nojekyll ├── data │ ├── 404.md │ ├── cookies.md │ ├── cover.md │ ├── navbar.md │ ├── policy.md │ ├── style.min.css │ └── terms.md ├── index.html └── pages │ ├── _page1.md │ ├── _page2.md │ ├── _page3.md │ ├── _page4.md │ └── _page5.md ├── package-lock.json └── package.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | # browsers we support 2 | last 4 versions 3 | > 0.5% 4 | ie >= 11 5 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | dist/* linguist-vendored=true 4 | *.js linguist-vendored=true 5 | *.md linguist-detectable=false 6 | *.html linguist-detectable=false 7 | *.css linguist-detectable=false 8 | -------------------------------------------------------------------------------- /.github/scripts/release.js: -------------------------------------------------------------------------------- 1 | const jsonfile = require('jsonfile'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | 5 | const packageFile = './package.json'; 6 | const packageData = jsonfile.readFileSync(packageFile); 7 | const currentVersion = packageData.version; 8 | const authorName = packageData.author; 9 | const versionParts = currentVersion.split('.'); 10 | let [major, minor, patch] = versionParts.map(Number); 11 | const incrementType = process.argv[2]; 12 | switch (incrementType) { 13 | case '-minor': 14 | minor += 1; 15 | patch = 0; 16 | break; 17 | case '-patch': 18 | patch += 1; 19 | break; 20 | case '-major': 21 | major += 1; 22 | minor = 0; 23 | patch = 0; 24 | break; 25 | default: 26 | console.log('Invalid increment type. Please use -minor, -patch, or -major.'); 27 | process.exit(1); 28 | } 29 | const newVersion = `${major}.${minor}.${patch}`; 30 | packageData.version = newVersion; 31 | jsonfile.writeFileSync(packageFile, packageData, { spaces: 2 }); 32 | 33 | const filesToUpdate = [ 34 | "./dist/docsify-auto-headers.js", 35 | "./dist/docsify-auto-headers.min.js" 36 | ]; 37 | 38 | filesToUpdate.forEach(filePath => { 39 | const fileName = getFileName(filePath); 40 | const header = `/*! ${fileName} ${newVersion} | (c) ${authorName} */\n`; 41 | const fileContent = fs.readFileSync(filePath, 'utf8'); 42 | const headerRegex = /^\/\*![\s\S]*?\*\//; // Regular expression to match the header comment 43 | const contentWithoutHeader = fileContent.replace(headerRegex, ''); 44 | const updatedContent = header + contentWithoutHeader.trimStart(); 45 | fs.writeFileSync(filePath, updatedContent, 'utf8'); 46 | console.log(`Header added to ${filePath}.`); 47 | }); 48 | 49 | console.log('Header added successfully to all files.'); 50 | 51 | const changelogPath = './CHANGELOG.md'; 52 | const changelogContent = generateChangelog(newVersion, incrementType); 53 | fs.writeFileSync(changelogPath, changelogContent, 'utf8'); 54 | console.log('Changelog generated successfully.'); 55 | 56 | function generateChangelog(version, incrementType) { 57 | const currentDate = new Date().toDateString(); 58 | const changeDescription = getChangeDescription(incrementType); 59 | 60 | // Read the existing changelog content if it exists 61 | let existingChangelog = ''; 62 | if (fs.existsSync(changelogPath)) { 63 | existingChangelog = fs.readFileSync(changelogPath, 'utf8'); 64 | } 65 | const newChangelogEntry = `\n## ${version} - ${currentDate}\n\n${changeDescription}\n`; 66 | return newChangelogEntry + existingChangelog; 67 | } 68 | 69 | function getChangeDescription(incrementType) { 70 | switch (incrementType) { 71 | case '-minor': 72 | return '### Added\n\n- Add your change description here.'; 73 | case '-patch': 74 | return '### Fixed\n\n- Fix your change description here.'; 75 | case '-major': 76 | return '### Breaking Changes\n\n- Describe any breaking changes here.'; 77 | default: 78 | return ''; 79 | } 80 | } 81 | 82 | function getFileName(filePath) { 83 | const fileNameWithExtension = path.basename(filePath); 84 | const fileName = fileNameWithExtension.split('.')[0]; 85 | return fileName; 86 | } 87 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | release: 4 | types: [published] 5 | 6 | jobs: 7 | tag: 8 | name: Add/update 'latest' tag 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout repository 12 | uses: actions/checkout@v2 13 | - name: Run latest-tag 14 | uses: EndBug/latest-tag@v1 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | 18 | publish-npm: 19 | name: Publish on NPM 20 | runs-on: ubuntu-latest 21 | needs: tag 22 | steps: 23 | - uses: actions/checkout@v2 24 | - name: Set up Node.js for NPM 25 | uses: actions/setup-node@v1 26 | with: 27 | registry-url: 'https://registry.npmjs.org' 28 | - run: npm install 29 | - run: npm publish 30 | env: 31 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 32 | 33 | publish-gpr: 34 | name: Publish on GPR 35 | runs-on: ubuntu-latest 36 | needs: tag 37 | steps: 38 | - uses: actions/checkout@v2 39 | - name: Set up Node.js for GPR 40 | uses: actions/setup-node@v1 41 | with: 42 | registry-url: 'https://npm.pkg.github.com/' 43 | scope: '@markbattistella' 44 | - run: npm install 45 | - run: npm publish 46 | env: 47 | NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/static.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy static content to Pages 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 19 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 20 | concurrency: 21 | group: "pages" 22 | cancel-in-progress: false 23 | 24 | jobs: 25 | # Single deploy job since we're just deploying 26 | deploy: 27 | environment: 28 | name: github-pages 29 | url: ${{ steps.deployment.outputs.page_url }} 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: Checkout 33 | uses: actions/checkout@v4 34 | - name: Setup Pages 35 | uses: actions/configure-pages@v5 36 | - name: Upload artifact 37 | uses: actions/upload-pages-artifact@v3 38 | with: 39 | # Upload entire repository 40 | path: './docs' 41 | - name: Deploy to GitHub Pages 42 | id: deployment 43 | uses: actions/deploy-pages@v4 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .markdownlint.json 3 | .DS_Store 4 | *babel.js 5 | .gitattributes 6 | node_modules -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .markdownlint.json 3 | .github 4 | .gitattributes 5 | .DS_Store 6 | *babel.js 7 | docs -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | ## 5.0.1 - Tue Jun 11 2024 3 | 4 | ### Fixed 5 | 6 | - Documentation fixes. 7 | 8 | ## 5.0.0 - Tue Jun 11 2024 9 | 10 | ### Breaking Changes 11 | 12 | - Bumped to new major version 13 | - Fixing the documentation 14 | - Added new functionality for `@autoHeaders:` and `` signifiers 15 | - Allows numbering in sidebar and main body 16 | - Better handling of debugging 17 | - Numbering can use letters and not just numbers 18 | - Many more fatures and cleanups 19 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mark Battistella 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | # docsify.js auto-headers 4 | 5 |
6 | 7 | This plugin enhances your Docsify documentation by automatically generating numbered headers for your markdown files. It allows you to configure the header levels, numbering format, and inclusion in the sidebar. By utilising this plugin, you can easily manage and navigate through your documentation headers, ensuring consistency and improved readability. 8 | 9 | ## Demo pages 10 | 11 | | Page link | Description | 12 | |-|-| 13 | | [![](https://img.shields.io/badge/page_1--blue?style=for-the-badge)](pages/_page1) | The `autoHeader` of this page is: `@autoHeader:1`.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. | 14 | | [![](https://img.shields.io/badge/page_2--blue?style=for-the-badge)](pages/_page2) | The `autoHeader` of this page is: ``.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. | 15 | | [![](https://img.shields.io/badge/page_3--blue?style=for-the-badge)](pages/_page3) | The `autoHeader` of this page is: `@autoHeader:`.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. | 16 | | [![](https://img.shields.io/badge/page_4--blue?style=for-the-badge)](pages/_page4) | The `autoHeader` of this page is: ``.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. | 17 | | [![](https://img.shields.io/badge/page_5--blue?style=for-the-badge)](pages/_page5) | The `autoHeader` of this page is: ``.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. | 18 | 19 | ## Installation 20 | 21 | !> Note: There are breaking changes in the configuration from `v4.x` to `v5.x`. Please take the time to read all the documentation before upgrading 22 | 23 | ### Update `index.html` file 24 | 25 | Assuming you have a working [docsify](https://docsify.js.org/) framework set up, it is easy to use the plugin. 26 | 27 | 1. Add one of the following script tags to your `index.html` via either CDN or downloading it and using it locally: 28 | 29 | ```html 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | ``` 39 | 40 | 1. In docsify setup, configure the plugin: 41 | 42 | ```js 43 | 62 | ``` 63 | 64 | ## Configuration 65 | 66 | There are several options available for the docsify-auto-headers plugin: 67 | 68 | | Setting | Type | Options | 69 | |-------------|---------|-------------------------------------| 70 | | `separator` | String | e.g., `.`, `-`, `)` | 71 | | `sidebar` | Boolean | `true` or `false` | 72 | | `levels` | Number | `1` to `6` | 73 | | | Object | `{ start: Number, finish: Number }` | 74 | | `debug` | Boolean | `true` or `false` | 75 | 76 | ## Usage 77 | 78 | The plugin can be configured to apply scoped heading counts in either the sidebar or the main content, depending on your setup. 79 | 80 | ### Adding the signifier 81 | 82 | When you want to use the heading numbering on a markdown file you can add either of the following signifiers to the **first line** of the document: 83 | 84 | ```md 85 | @autoHeader: 86 | ``` 87 | 88 | ```md 89 | 90 | ``` 91 | 92 | After the colon (`:`) you can add in the number that will begin for heading level 1 (`H1`). 93 | 94 | ### Sidebar 95 | 96 | If the `sidebar` option is enabled, the headers will be included in the sidebar and processed before rendering the markdown. 97 | 98 | ### Main Content 99 | 100 | If the `sidebar` option is disabled, the headers will be processed and applied directly to the HTML after rendering. 101 | 102 | ## Contributing 103 | 104 | 1. Clone the repo:
`git clone https://github.com/markbattistella/docsify-auto-headers.git` 105 | 106 | 1. Create your feature branch:
`git checkout -b my-feature` 107 | 108 | 1. Commit your changes:
`git commit -am 'Add some feature'` 109 | 110 | 1. `Push` to the branch:
`git push origin my-new-feature` 111 | 112 | 1. Submit the `pull` request 113 | -------------------------------------------------------------------------------- /dist/docsify-auto-headers.js: -------------------------------------------------------------------------------- 1 | /*! docsify-auto-headers 5.0.1 | (c) Mark Battistella */ 2 | ; (() => { 3 | 4 | 'use strict'; 5 | 6 | /** 7 | * Default settings for the Docsify Auto Headers plugin. 8 | * @type {Object} 9 | * @property {string} separator - The separator for header numbering (e.g., '.', '-', ')'). 10 | * @property {boolean} sidebar - Boolean indicating if headers should be added to the sidebar. 11 | * @property {number|Object} levels - Number of header levels to include (1 to 6) or an object with start and finish properties. 12 | * @property {boolean} debug - Boolean to enable or disable debug messages. 13 | */ 14 | const docsifyAutoHeadersDefaults = { 15 | 16 | // Separator for header numbering (e.g., '.', '-', ')') 17 | separator: '.', 18 | 19 | // Boolean indicating if headers should be added to the sidebar 20 | sidebar: false, 21 | 22 | // Number of header levels to include (1 to 6) or an object with start and finish properties 23 | levels: 6, 24 | 25 | // Boolean to enable or disable debug messages 26 | debug: false 27 | }; 28 | 29 | /** 30 | * Main function for the Docsify Auto Headers plugin. 31 | * @param {Object} hook - Docsify hook object. 32 | * @param {Object} vm - Docsify virtual machine object. 33 | */ 34 | const autoHeaders = (hook, vm) => { 35 | 36 | /** 37 | * Predefined error messages for invalid configurations. 38 | * @type {Object} 39 | * @property {string} invalidConfiguration - General error message for invalid configuration. 40 | * @property {string} invalidConfigurationSidebar - Error message for invalid sidebar configuration. 41 | * @property {string} invalidConfigurationLevels - Error message for invalid levels configuration. 42 | * @property {string} nonNumericHeadingLevel - Error message when levels start or finish values are not numbers. 43 | * @property {string} badNumericOrderHeadingLevel - Error message when start value is greater than finish value. 44 | * @property {string} outOfRangeHeadingLevel - Error message when start or finish values are out of range (1-6). 45 | * @property {string} moreThanSixElements - Error message when more than 6 elements are found in the signifier. 46 | * @property {string} invalidParsedElements - Error message when elements in the signifier are not purely numeric or alphabetic. 47 | * @property {string} signifierNotFound - Error message when the auto header signifier is missing in the markdown file. 48 | */ 49 | const errorMessage = { 50 | invalidConfiguration: 51 | 'Configuration settings are not set correctly. Please review the autoHeaders parameters and documentation.', 52 | invalidConfigurationSidebar: 53 | 'The sidebar setting for autoHeaders only accepts a boolean of true or false. Please check you\'ve entered this data correctly.', 54 | invalidConfigurationLevels: 55 | 'The levels settings for autoHeaders only accepts a number from 1-6 or an object with the start and finish options. Please check you\'ve entered this data correctly.', 56 | nonNumericHeadingLevel: 57 | 'The levels setting has been configured with a start and finish option. However, the values for one of these is not a number. Please check you\'ve entered this data correctly.', 58 | badNumericOrderHeadingLevel: 59 | 'The levels setting has been configured with a start and finish option. However, the start value is greater than the finish. Please check you\'ve entered this data correctly.', 60 | outOfRangeHeadingLevel: 61 | 'The levels setting has been configured with a start and finish option. However, the values for one of these is not from 1-6. Please check you\'ve entered this data correctly.', 62 | moreThanSixElements: 63 | 'The elements found in the signifier have equated to more than 6 headings. Please check the configuration of your markdown that you have no more than 6 numbers', 64 | invalidParsedElements: 65 | 'The elements found in the signifier are not numbers only or alphabet only. Please check the configuration of your markdown that all the items are numeric or alphabetic.', 66 | signifierNotFound: 67 | 'The current markdown file is missing the @autoHeader: or signifier', 68 | }; 69 | 70 | /** 71 | * Boolean flag indicating whether the processing should continue. 72 | * @type {boolean} 73 | */ 74 | let shouldContinue = true; 75 | 76 | /** 77 | * Logs an error message if a configuration is invalid. 78 | * @param {boolean} shouldShow - Boolean indicating if the error message should be shown. 79 | * @param {string} message - The error message to log. 80 | * @returns {null} Always returns null after logging the error. 81 | */ 82 | const logErrorMessage = (shouldShow, message) => { 83 | if (shouldShow) { 84 | console.warn(`Docsify Auto Headers:\n>> ${message}`); 85 | } 86 | shouldContinue = false; 87 | return null; 88 | }; 89 | 90 | /** 91 | * Sets the default options for the plugin by merging user-defined options with defaults. 92 | * @param {Object} options - User-defined options to override the default settings. 93 | * @param {string} options.separator - The separator for header numbering. 94 | * @param {number|Object} options.levels - Number of header levels to include or an object with start and finish properties. 95 | * @param {boolean} options.sidebar - Boolean indicating if headers should be added to the sidebar. 96 | * @param {boolean} options.debug - Boolean to enable or disable debug messages. 97 | * @returns {Object|null} The final options object or null if there is an invalid configuration. 98 | */ 99 | const setDefaultOptions = (options) => { 100 | if (!options.separator || options.levels === undefined) { 101 | return logErrorMessage(options.debug, errorMessage.invalidConfiguration); 102 | } 103 | 104 | // Map user-friendly separator names to actual separator characters 105 | const separatorMap = { 106 | 'decimal': '.', 107 | 'dot': '.', 108 | 'dash': '-', 109 | 'hyphen': '-', 110 | 'bracket': ')', 111 | 'parenthesis': ')' 112 | }; 113 | // Determine the separator to use 114 | const separator = separatorMap[options.separator] || options.separator; 115 | 116 | // Set the levels, defaulting to 6 if not provided 117 | const levels = options.levels || 6; 118 | 119 | // Ensure sidebar and debug options are booleans 120 | const sidebar = !!options.sidebar; 121 | 122 | const debug = !!options.debug; 123 | 124 | return { separator, levels, sidebar, debug }; 125 | }; 126 | 127 | /** 128 | * Validates and retrieves the sidebar setting. 129 | * @param {boolean} input - The sidebar setting input. 130 | * @param {Object} options - The current plugin options. 131 | * @param {boolean} options.debug - Boolean to enable or disable debug messages. 132 | * @returns {boolean|null} The validated sidebar setting or null if the input is invalid. 133 | */ 134 | const validateThenGetSidebar = (input, options) => { 135 | if (typeof input !== 'boolean') { 136 | return logErrorMessage(options.debug, errorMessage.invalidConfigurationSidebar); 137 | } 138 | return input; 139 | }; 140 | 141 | /** 142 | * Validates and retrieves the heading range setting. 143 | * @param {number|Object} input - The levels setting input, which can be a number or an object with start and finish properties. 144 | * @param {Object} options - The current plugin options. 145 | * @param {boolean} options.debug - Boolean to enable or disable debug messages. 146 | * @returns {Object|null} The validated heading range configuration or null if the input is invalid. 147 | */ 148 | const validateThenGetHeadingRange = (input, options) => { 149 | if (typeof input !== 'number' && (typeof input !== 'object' || input === null)) { 150 | return logErrorMessage(options.debug, errorMessage.invalidConfigurationLevels); 151 | } 152 | 153 | // Helper function to check if a value is within a specified range 154 | const isInRange = (value, min, max) => value >= min && value <= max; 155 | let start, finish; 156 | 157 | if (typeof input === 'number') { 158 | start = 1; 159 | finish = input; 160 | } else if (typeof input === 'object') { 161 | ({ start, finish } = input); 162 | if (typeof start !== 'number' || typeof finish !== 'number') { 163 | return logErrorMessage(options.debug, errorMessage.nonNumericHeadingLevel); 164 | } 165 | if (start > finish) { 166 | return logErrorMessage(options.debug, errorMessage.badNumericOrderHeadingLevel); 167 | } 168 | } 169 | 170 | if (!isInRange(start, 1, 6) || !isInRange(finish, 1, 6)) { 171 | return logErrorMessage(options.debug, errorMessage.outOfRangeHeadingLevel); 172 | } 173 | 174 | const headings = {}; 175 | for (let i = 1; i <= 6; i++) { 176 | headings[`h${i}`] = { inScope: isInRange(i, start, finish) }; 177 | } 178 | return headings; 179 | }; 180 | 181 | /** 182 | * Converts a number to a header string based on the type (numeric or alphabetic). 183 | * @param {number} num - The number to convert. 184 | * @param {string} type - The type of conversion ("numeric", "alphabetic-lower", "alphabetic-upper"). 185 | * @returns {string} The converted header string. 186 | */ 187 | const numberToHeader = (num, type) => { 188 | if (type === "numeric") { 189 | return num + ""; // Convert number to string 190 | } else { 191 | num--; // Adjust the number for zero-based index 192 | let result = ''; 193 | while (num >= 0) { 194 | let remainder = num % 26; 195 | result = String.fromCharCode(65 + remainder) + result; 196 | num = Math.floor(num / 26) - 1; 197 | } 198 | 199 | // Convert result to lowercase if type is "alphabetic-lower" 200 | return (type === "alphabetic-lower") ? result.toLowerCase() : result; 201 | } 202 | }; 203 | 204 | /** 205 | * Converts a header string to a number based on the type. 206 | * @param {string} header - The header string to convert. 207 | * @param {string} type - The type of conversion ("numeric", "alphabetic-lower", "alphabetic-upper"). 208 | * @returns {number} The converted number. 209 | */ 210 | const headerToNumber = (header, type) => { 211 | if (type === "numeric") { 212 | return parseInt(header, 10); // Convert header string to number 213 | } else { 214 | header = header.toUpperCase(); // Convert header to uppercase for alphabetic conversion 215 | let result = 0; 216 | for (let i = 0; i < header.length; i++) { 217 | result *= 26; 218 | result += header.charCodeAt(i) - 65 + 1; 219 | } 220 | return result; 221 | } 222 | }; 223 | 224 | /** 225 | * Parses the starting values for headers from the signifier. 226 | * @param {string} headerNumbers - The header numbers string. 227 | * @param {Object} options - The current plugin options. 228 | * @param {string} options.separator - The separator for header numbering. 229 | * @param {boolean} options.debug - Boolean to enable or disable debug messages. 230 | * @returns {Array|null} The parsed header values as an array of objects or null if the input is invalid. 231 | */ 232 | const parseHeadingStartingValues = (headerNumbers, options) => { 233 | // Helper functions to check if a string is all numeric or alphabetic 234 | const isAllNumeric = (str) => /^\d+$/.test(str); 235 | const isAllAlphabeticLower = (str) => /^[a-z]+$/.test(str); 236 | const isAllAlphabeticUpper = (str) => /^[A-Z]+$/.test(str); 237 | 238 | // Split the header numbers string by the separator and trim each element 239 | let elements = headerNumbers.split(options.separator).map(el => el.trim()); 240 | if (elements.length > 6) { 241 | return logErrorMessage(options.debug, errorMessage.moreThanSixElements); 242 | } 243 | 244 | // Check if all elements are numeric or alphabetic 245 | const isNumeric = elements.every(isAllNumeric); 246 | const isAlphabeticLower = elements.every(isAllAlphabeticLower); 247 | const isAlphabeticUpper = elements.every(isAllAlphabeticUpper); 248 | if (!isNumeric && !isAlphabeticLower && !isAlphabeticUpper) { 249 | return logErrorMessage(options.debug, errorMessage.invalidParsedElements); 250 | } 251 | 252 | // Ensure there are at least 6 elements by filling with default values 253 | while (elements.length < 6) { 254 | elements.push(isNumeric ? '1' : (isAlphabeticLower ? 'a' : 'A')); 255 | } 256 | 257 | // Determine the type of header values 258 | let type = isNumeric ? 'numeric' : (isAlphabeticLower ? 'alphabetic-lower' : 'alphabetic-upper'); 259 | 260 | // Convert the elements to header values 261 | return elements.map(x => ({ counter: headerToNumber(x, type), type })); 262 | }; 263 | 264 | /** 265 | * Validates the presence of the auto header signifier in the markdown. 266 | * @param {string} markdown - The markdown content. 267 | * @param {Object} options - The current plugin options. 268 | * @param {boolean} options.debug - Boolean to enable or disable debug messages. 269 | * @returns {Object|null} An object containing the heading signifier and the remaining markdown content, or null if the signifier is not found. 270 | */ 271 | const validateAutoHeaderSignifier = (markdown, options) => { 272 | // Regular expression pattern to match the auto header signifier 273 | const autoHeaderPattern = /^\s*(?:@autoHeader:|)?/; 274 | const match = markdown.match(autoHeaderPattern); 275 | 276 | if (!match) { 277 | return logErrorMessage(options.debug, errorMessage.signifierNotFound); 278 | } 279 | 280 | // Remove the matched signifier from the markdown 281 | markdown = markdown.substring(match[0].length); 282 | 283 | return { 284 | headingSignifier: match[1], // The captured signifier 285 | markdown, // The remaining markdown content 286 | }; 287 | }; 288 | 289 | /** 290 | * Creates count context objects for header levels. 291 | * @param {Object} levels - The header levels configuration. 292 | * @returns {Object} An object containing the current counts and scoped tag names. 293 | */ 294 | const createCountContextObjects = (levels) => { 295 | const configEntries = Object.entries(levels); 296 | 297 | // Create a map of current counts for each header level 298 | const currentCounts = new Map( 299 | configEntries.map(([key, { counter, type }]) => { 300 | counter = parseInt(counter, 10); // Convert counter to an integer 301 | counter = Number.isFinite(counter) ? counter - 1 : 0; // Decrement counter or set to 0 if not a number 302 | return [key, { current: counter, type, skipDownstreamReset: true }]; 303 | }) 304 | ); 305 | 306 | // Create a set of tag names that are in scope 307 | const scopedTagNames = new Set( 308 | configEntries.filter(([_, { inScope }]) => inScope).map(([key]) => key) 309 | ); 310 | 311 | return { currentCounts, scopedTagNames }; 312 | }; 313 | 314 | /** 315 | * Applies the current count to the heading node. 316 | * @param {HTMLElement} headingNode - The heading node to update. 317 | * @param {Object} options - The current plugin options. 318 | * @param {string} options.separator - The separator for header numbering. 319 | */ 320 | const applyCurrentCountThroughBoundContext = function (headingNode, options) { 321 | const { currentCounts, scopedTagNames } = this; 322 | const headingName = headingNode.tagName.toLowerCase(); 323 | const headingNameList = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; 324 | const counts = currentCounts.get(headingName); 325 | 326 | counts.current += 1; // Increment the current count for the heading level 327 | if (!counts.skipDownstreamReset) { 328 | // Reset counts for heading levels below the current level 329 | headingNameList.slice(headingNameList.indexOf(headingName) + 1).forEach(name => { 330 | if (currentCounts.has(name)) { 331 | const nextMinorCount = currentCounts.get(name); 332 | nextMinorCount.current = 0; 333 | } 334 | }); 335 | } 336 | counts.skipDownstreamReset = false; // Reset the flag 337 | 338 | if (scopedTagNames.has(headingName)) { 339 | // Generate the counter value for the heading 340 | const counterValue = headingNameList 341 | .slice(0, headingNameList.indexOf(headingName) + 1) 342 | .map(name => { 343 | const { current, type } = currentCounts.get(name); 344 | return numberToHeader(current, type); 345 | }) 346 | .join(options.separator) + options.separator; 347 | 348 | headingNode.innerHTML = `${counterValue} ${headingNode.innerHTML}`; // Update the heading's innerHTML 349 | } 350 | }; 351 | 352 | /** 353 | * Parses the markdown content and updates headers based on the settings. 354 | * @param {string} markdown - The markdown content to parse. 355 | * @param {Object} options - The current plugin options. 356 | * @param {string} options.separator - The separator for header numbering. 357 | * @param {Object} levels - The header levels configuration. 358 | * @returns {string} The updated markdown content with applied header counts. 359 | */ 360 | const parseMarkdown = (markdown, options, levels) => { 361 | const { currentCounts, scopedTagNames } = createCountContextObjects(levels); 362 | const headingPattern = /^(#{1,6})\s+(.*)$/gm; // Regex pattern to match markdown headers 363 | const headingNameList = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; 364 | 365 | let result = markdown.replace(headingPattern, (match, hashes, text) => { 366 | const headingLevel = hashes.length; 367 | const headingName = `h${headingLevel}`; 368 | const counts = currentCounts.get(headingName); 369 | 370 | counts.current += 1; // Increment the current count for the heading level 371 | if (!counts.skipDownstreamReset) { 372 | // Reset counts for heading levels below the current level 373 | headingNameList.slice(headingLevel).forEach(name => { 374 | if (currentCounts.has(name)) { 375 | const nextMinorCount = currentCounts.get(name); 376 | nextMinorCount.current = 0; 377 | } 378 | }); 379 | } 380 | counts.skipDownstreamReset = false; // Reset the flag 381 | 382 | if (scopedTagNames.has(headingName)) { 383 | // Generate the counter value for the heading 384 | const counterValue = headingNameList 385 | .slice(0, headingLevel) 386 | .map(name => { 387 | const { current, type } = currentCounts.get(name); 388 | return numberToHeader(current, type); 389 | }) 390 | .join(options.separator) + options.separator; 391 | return `${hashes} ${counterValue} ${text}`; // Update the header in the markdown 392 | } else { 393 | return match; // Return the original match if the heading is not in scope 394 | } 395 | }); 396 | return result; // Return the updated markdown content 397 | }; 398 | 399 | /** 400 | * Applies scoped heading counts to the input content. 401 | * @param {Object} levels - The header levels configuration. 402 | * @param {Object} options - The current plugin options. 403 | * @param {string} options.separator - The separator for header numbering. 404 | * @param {string} input - The input content (HTML or markdown). 405 | * @param {string} type - The type of input content ('html' or 'markdown'). 406 | * @returns {string} The updated content with applied header counts. 407 | */ 408 | const applyScopedHeadingCounts = (levels, options, input, type) => { 409 | const { currentCounts, scopedTagNames } = createCountContextObjects(levels); 410 | 411 | if (type === 'html') { 412 | // Parse the input HTML content 413 | const parser = new DOMParser(); 414 | input = parser.parseFromString(input, 'text/html').body; 415 | 416 | // Get all heading elements 417 | const headingList = [...input.querySelectorAll('h1, h2, h3, h4, h5, h6')]; 418 | 419 | // Apply the current count to each heading element 420 | headingList.forEach( 421 | heading => applyCurrentCountThroughBoundContext.call( 422 | { currentCounts, scopedTagNames }, 423 | heading, 424 | options 425 | ) 426 | ); 427 | 428 | return input.innerHTML; // Return the updated HTML content 429 | } else if (type === 'markdown') { 430 | // Parse and update the markdown content 431 | return parseMarkdown(input, options, levels); 432 | } 433 | }; 434 | 435 | // Set the default options by merging user-defined options with default settings 436 | const defaultOptions = setDefaultOptions(docsifyAutoHeadersDefaults); 437 | if (!defaultOptions) return; 438 | 439 | // Initialize options with validated settings 440 | /** 441 | * The plugin options after validation and initialization. 442 | * @type {Object} 443 | * @property {string} separator - The separator for header numbering. 444 | * @property {Object} levels - The validated header levels configuration. 445 | * @property {boolean} sidebar - Boolean indicating if headers should be added to the sidebar. 446 | * @property {boolean} debug - Boolean to enable or disable debug messages. 447 | */ 448 | let options = { 449 | separator: defaultOptions.separator, 450 | levels: validateThenGetHeadingRange(defaultOptions.levels, defaultOptions), 451 | sidebar: validateThenGetSidebar(defaultOptions.sidebar, defaultOptions), 452 | debug: defaultOptions.debug, 453 | }; 454 | 455 | /** 456 | * Docsify hook to process markdown before it is rendered. 457 | * @param {Function} hook - Docsify hook function. 458 | * @param {Object} options - The current plugin options. 459 | */ 460 | hook.beforeEach(markdown => { 461 | shouldContinue = true; 462 | if (!shouldContinue) return markdown; 463 | 464 | // Validate the presence of the auto header signifier in the markdown 465 | const result = validateAutoHeaderSignifier(markdown, options); 466 | if (!result) { 467 | return markdown; 468 | } 469 | let headingSignifier; 470 | ({ headingSignifier, markdown } = result); // Destructure the result 471 | 472 | // Validate and retrieve the heading range setting 473 | const headingRanges = validateThenGetHeadingRange(defaultOptions.levels, options); 474 | if (!headingRanges) { 475 | return markdown; 476 | } 477 | 478 | // Parse the starting values for headers from the signifier 479 | const startingHeadingValues = parseHeadingStartingValues( 480 | headingSignifier, 481 | options 482 | ); 483 | if (!startingHeadingValues) { 484 | return markdown; 485 | } 486 | 487 | // Create the heading configuration 488 | const headingConfiguration = {}; 489 | for (const [index, key] of Object.keys(headingRanges).entries()) { 490 | headingConfiguration[key] = { 491 | ...headingRanges[key], 492 | ...startingHeadingValues[index], 493 | }; 494 | } 495 | 496 | // Update the options with the new heading configuration 497 | options.levels = headingConfiguration; 498 | 499 | return markdown; // Return the updated markdown 500 | }); 501 | 502 | 503 | // Check if the sidebar option is enabled 504 | if (options.sidebar) { 505 | 506 | /** 507 | * Docsify hook to process markdown before it is rendered if the sidebar option is enabled. 508 | * @param {Function} hook - Docsify hook function. 509 | * @param {Object} options - The current plugin options. 510 | */ 511 | hook.beforeEach((markdown, next) => { 512 | let output; 513 | 514 | try { 515 | // Apply scoped heading counts to the markdown content 516 | output = applyScopedHeadingCounts( 517 | options.levels, 518 | options, 519 | markdown, 520 | 'markdown' 521 | ); 522 | if (!shouldContinue) output = markdown; 523 | 524 | } catch (error) { 525 | output = markdown; 526 | console.warn(error.message); 527 | } finally { 528 | next(output); // Pass the updated markdown to the next hook 529 | } 530 | }); 531 | } else { 532 | 533 | /** 534 | * Docsify hook to process HTML after it is rendered if the sidebar option is disabled. 535 | * @param {Function} hook - Docsify hook function. 536 | * @param {Object} options - The current plugin options. 537 | */ 538 | hook.afterEach((html, next) => { 539 | let output; 540 | 541 | try { 542 | // Apply scoped heading counts to the HTML content 543 | output = applyScopedHeadingCounts( 544 | options.levels, 545 | options, 546 | html, 547 | 'html' 548 | ); 549 | if (!shouldContinue) output = html; 550 | 551 | } catch (error) { 552 | output = html; 553 | console.warn(error.message); 554 | } finally { 555 | next(output); // Pass the updated HTML to the next hook 556 | } 557 | }); 558 | } 559 | }; 560 | 561 | /** 562 | * Initializes the plugin if running in a browser environment. 563 | * This adds the autoHeaders plugin to the Docsify instance. 564 | */ 565 | if (window) { 566 | window.$docsify = window.$docsify || {}; 567 | 568 | // Merge user-defined settings with default settings 569 | window.$docsify.autoHeaders = Object.assign( 570 | docsifyAutoHeadersDefaults, 571 | window.$docsify.autoHeaders 572 | ); 573 | 574 | // Add the autoHeaders function to the list of Docsify plugins 575 | window.$docsify.plugins = ( 576 | window.$docsify.plugins || [] 577 | ).concat(autoHeaders); 578 | } 579 | })(); 580 | -------------------------------------------------------------------------------- /dist/docsify-auto-headers.min.js: -------------------------------------------------------------------------------- 1 | /*! docsify-auto-headers 5.0.1 | (c) Mark Battistella */ 2 | "use strict";function ownKeys(r,e){var t,n=Object.keys(r);return Object.getOwnPropertySymbols&&(t=Object.getOwnPropertySymbols(r),e&&(t=t.filter(function(e){return Object.getOwnPropertyDescriptor(r,e).enumerable})),n.push.apply(n,t)),n}function _objectSpread(r){for(var e=1;e=e.length?{done:!0}:{done:!1,value:e[a++]}},e:function(e){throw e},f:r};throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _toConsumableArray(e){return _arrayWithoutHoles(e)||_iterableToArray(e)||_unsupportedIterableToArray(e)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _iterableToArray(e){if("undefined"!=typeof Symbol&&null!=e[Symbol.iterator]||null!=e["@@iterator"])return Array.from(e)}function _arrayWithoutHoles(e){if(Array.isArray(e))return _arrayLikeToArray(e)}function _slicedToArray(e,r){return _arrayWithHoles(e)||_iterableToArrayLimit(e,r)||_unsupportedIterableToArray(e,r)||_nonIterableRest()}function _nonIterableRest(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,r){var t;if(e)return"string"==typeof e?_arrayLikeToArray(e,r):"Map"===(t="Object"===(t={}.toString.call(e).slice(8,-1))&&e.constructor?e.constructor.name:t)||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?_arrayLikeToArray(e,r):void 0}function _arrayLikeToArray(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t> ".concat(r)),m=!1,null}function h(e,r){if("number"!=typeof e&&("object"!==_typeof(e)||null===e))return y(r.debug,u);function t(e,r,t){return r<=e&&e<=t}var n,o;if("number"==typeof e)n=1,o=e;else if("object"===_typeof(e)){if(n=e.start,o=e.finish,"number"!=typeof n||"number"!=typeof o)return y(r.debug,s);if(o)?/))?(n=n.substring(t[0].length),{headingSignifier:t[1],markdown:n}):y(r.debug,b);if(t){var n=t.headingSignifier,o=(e=t.markdown,h(w.levels,p));if(o){var a=d(n,p);if(a){var i,u={},s=_createForOfIteratorHelper(Object.keys(o).entries());try{for(s.s();!(i=s.n()).done;){var c=_slicedToArray(i.value,2),l=c[0],f=c[1];u[f]=_objectSpread(_objectSpread({},o[f]),a[l])}}catch(e){s.e(e)}finally{s.f()}p.levels=u}}}}return e}),p.sidebar?e.beforeEach(function(r,e){var t;try{t=n(p.levels,p,r,"markdown"),m||(t=r)}catch(e){t=r,console.warn(e.message)}finally{e(t)}}):e.afterEach(function(r,e){var t;try{t=n(p.levels,p,r,"html"),m||(t=r)}catch(e){t=r,console.warn(e.message)}finally{e(t)}}))}var _={separator:".",sidebar:!1,levels:6,debug:!1};window&&(window.$docsify=window.$docsify||{},window.$docsify.autoHeaders=Object.assign(_,window.$docsify.autoHeaders),window.$docsify.plugins=(window.$docsify.plugins||[]).concat(e))}(); -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markbattistella/docsify-auto-headers/e4ef6581c6143eda7a3673da63eb8e39c564a7dc/docs/.nojekyll -------------------------------------------------------------------------------- /docs/data/404.md: -------------------------------------------------------------------------------- 1 | # Sorry this document is not found 2 | 3 | Please [log an issue](https://github.com/markbattistella/docsify-auto-headers/issues/new?labels=bug,documentation&title=404+error+page) so I can fix it :thumbsup: 4 | -------------------------------------------------------------------------------- /docs/data/cookies.md: -------------------------------------------------------------------------------- 1 | # Cookie Policy 2 | 3 | This is the Cookie Policy for docsify-autoHeaders site, accessible from [our site](https://markbattistella.github.io/docsify-autoHeaders/). 4 | 5 | ## What Are Cookies 6 | 7 | As is common practice with almost all professional websites this site uses cookies, which are tiny files that are downloaded to your computer, to improve your experience. This page describes what information they gather, how we use it and why we sometimes need to store these cookies. We will also share how you can prevent these cookies from being stored however this may downgrade or 'break' certain elements of the sites functionality. 8 | 9 | ## How We Use Cookies 10 | 11 | We use cookies for a variety of reasons detailed below. Unfortunately in most cases there are no industry standard options for disabling cookies without completely disabling the functionality and features they add to this site. It is recommended that you leave on all cookies if you are not sure whether you need them or not in case they are used to provide a service that you use. 12 | 13 | ## Disabling Cookies 14 | 15 | You can prevent the setting of cookies by adjusting the settings on your browser (see your browser Help for how to do this). Be aware that disabling cookies will affect the functionality of this and many other websites that you visit. Disabling cookies will usually result in also disabling certain functionality and features of the this site. Therefore it is recommended that you do not disable cookies. 16 | 17 | ## The Cookies We Set 18 | 19 | - **Site preferences cookies** 20 | 21 | In order to provide you with a great experience on this site we provide the functionality to set your preferences for how this site runs when you use it. In order to remember your preferences we need to set cookies so that this information can be called whenever you interact with a page is affected by your preferences. 22 | 23 | ## Third Party Cookies 24 | 25 | In some special cases we also use cookies provided by trusted third parties. The following section details which third party cookies you might encounter through this site. 26 | 27 | - This site uses Google Analytics which is one of the most widespread and trusted analytics solution on the web for helping us to understand how you use the site and ways that we can improve your experience. These cookies may track things such as how long you spend on the site and the pages that you visit so we can continue to produce engaging content. 28 | 29 | For more information on Google Analytics cookies, see the official Google Analytics page. 30 | 31 | - From time to time we test new features and make subtle changes to the way that the site is delivered. When we are still testing new features these cookies may be used to ensure that you receive a consistent experience whilst on the site whilst ensuring we understand which optimisations our users appreciate the most. 32 | 33 | ## More Information 34 | 35 | Hopefully that has clarified things for you and as was previously mentioned if there is something that you aren't sure whether you need or not it's usually safer to leave cookies enabled in case it does interact with one of the features you use on our site. 36 | 37 | However if you are still looking for more information then you can contact us through one of our preferred contact methods: 38 | 39 | - By [logging an issue](https://github.com/markbattistella/docsify-auto-headers/issues) in the repo 40 | -------------------------------------------------------------------------------- /docs/data/cover.md: -------------------------------------------------------------------------------- 1 | # **autoHeaders** plugin
for docsify.js 2 | 3 | Automate your heading numbers 4 | 5 | [Find it on Github](https://github.com/markbattistella/docsify-auto-headers) 6 | [Read more](#docsifyjs-auto-headers) 7 | [npm link](https://www.npmjs.com/package/@markbattistella/docsify-autoHeaders) 8 | -------------------------------------------------------------------------------- /docs/data/navbar.md: -------------------------------------------------------------------------------- 1 | - [Page 1](/pages/_page1.md) 2 | - [Page 2](/pages/_page2.md) 3 | - [Page 3](/pages/_page3.md) 4 | - [Page 4](/pages/_page4.md) 5 | - [Page 5](/pages/_page5.md) 6 | -------------------------------------------------------------------------------- /docs/data/policy.md: -------------------------------------------------------------------------------- 1 | # Privacy Policy 2 | 3 | This Publishing Policy governs the manner in which this site collects, uses, maintains and discloses information collected from users (each, a "User") of the website ("Site"). This publishing policy applies to the Site and all products and services offered. 4 | 5 | ## Personal identification information 6 | 7 | We may collect personal identification information from Users in a variety of ways, including, but not limited to, when Users visit our site, register on the site, place an order, subscribe to the newsletter, fill out a form, and in connection with other activities, services, features or resources we make available on our Site. Users may be asked for, as appropriate, name, email address, mailing address, phone number, credit card information. Users may, however, visit our Site anonymously. We will collect personal identification information from Users only if they voluntarily submit such information to us. Users can always refuse to supply personally identification information, except that it may prevent them from engaging in certain Site related activities. 8 | 9 | ## Non-personal identification information 10 | 11 | We may collect non-personal identification information about Users whenever they interact with our Site. Non-personal identification information may include the browser name, the type of computer and technical information about Users means of connection to our Site, such as the operating system and the Internet service providers utilised and other similar information. 12 | 13 | ## Web browser cookies 14 | 15 | Our Site may use "cookies" to enhance User experience. User's web browser places cookies on their hard drive for record-keeping purposes and sometimes to track information about them. User may choose to set their web browser to refuse cookies, or to alert you when cookies are being sent. If they do so, note that some parts of the Site may not function properly. 16 | 17 | ## How we use collected information 18 | 19 | This site collects and uses Users personal information for the following purposes: 20 | 21 | ### To personalise user experience 22 | 23 | We may use information in the aggregate to understand how our Users as a group use the services and resources provided on our Site. 24 | 25 | ### To improve our Site 26 | 27 | We continually strive to improve our website offerings based on the information and feedback we receive from you. 28 | 29 | ### To improve customer service 30 | 31 | Your information helps us to more effectively respond to your customer service requests and support needs. 32 | 33 | ### To process transactions 34 | 35 | We may use the information Users provide about themselves when placing an order only to provide service to that order. We do not share this information with outside parties except to the extent necessary to provide the service. 36 | 37 | ### To administer a content, promotion, survey or other Site feature 38 | 39 | To send Users information they agreed to receive about topics we think will be of interest to them. 40 | 41 | ### To send periodic emails 42 | 43 | The email address Users provide for order processing, will only be used to send them information and updates pertaining to their order. It may also be used to respond to their inquiries, and/or other requests or questions. If User decides to opt-in to our mailing list, they will receive emails that may include company news, updates, related product or service information, etc. If at any time the User would like to unsubscribe from receiving future emails, we include detailed unsubscribe instructions at the bottom of each email. 44 | 45 | ## How we protect your information 46 | 47 | We adopt appropriate data collection, storage and processing practices and security measures to protect against unauthorised access, alteration, disclosure or destruction of your personal information, username, password, transaction information and data stored on our Site. 48 | 49 | Sensitive and private data exchange between the Site and its Users happens over a SSL secured communication channel and is encrypted and protected with digital signatures. 50 | 51 | ## Sharing your personal information 52 | 53 | We do not sell, trade, or rent Users personal identification information to others. We may share generic aggregated demographic information not linked to any personal identification information regarding visitors and users with our business partners, trusted affiliates and advertisers for the purposes outlined above.We may use third party service providers to help us operate our business and the Site or administer activities on our behalf, such as sending out newsletters or surveys. We may share your information with these third parties for those limited purposes provided that you have given us your permission. 54 | 55 | ## Third party websites 56 | 57 | Users may find advertising or other content on our Site that link to the sites and services of our partners, suppliers, advertisers, sponsors, licensors and other third parties. We do not control the content or links that appear on these sites and are not responsible for the practices employed by websites linked to or from our Site. In addition, these sites or services, including their content and links, may be constantly changing. These sites and services may have their own privacy policies and customer service policies. Browsing and interaction on any other website, including websites which have a link to our Site, is subject to that website's own terms and policies. 58 | 59 | ## Advertising 60 | 61 | Ads appearing on our site may be delivered to Users by advertising partners, who may set cookies. These cookies allow the ad server to recognise your computer each time they send you an online advertisement to compile non personal identification information about you or others who use your computer. This information allows ad networks to, among other things, deliver targeted advertisements that they believe will be of most interest to you. This publishing policy does not cover the use of cookies by any advertisers. 62 | 63 | ## Changes to this publishing policy 64 | 65 | The site has the discretion to update this publishing policy at any time. When we do, we will revise the updated date at the bottom of this page. We encourage Users to frequently check this page for any changes to stay informed about how we are helping to protect the personal information we collect. You acknowledge and agree that it is your responsibility to review this publishing policy periodically and become aware of modifications. 66 | 67 | ## Your acceptance of these terms 68 | 69 | By using this Site, you signify your acceptance of this policy and terms of service. If you do not agree to this policy, please do not use our Site. Your continued use of the Site following the posting of changes to this policy will be deemed your acceptance of those changes. 70 | 71 | ## Contacting us 72 | 73 | If you have any questions about this Publishing Policy, the practices of this site, or your dealings with this site, please contact us. 74 | 75 | This document was last updated on the 1st of January 2020 76 | -------------------------------------------------------------------------------- /docs/data/style.min.css: -------------------------------------------------------------------------------- 1 | /*! (c) Mark Battistella | @markbattistella */ 2 | 3 | :root { 4 | --colour-mono: 0, 0%; 5 | --colour-green: 168, 76%; 6 | --colour-blue: 145, 63%; 7 | --colour-pink: var(--colour-green); 8 | } 9 | 10 | *, :after, :before { 11 | box-sizing:border-box 12 | } 13 | 14 | ::selection { 15 | --mono: 100%; 16 | background: hsl( var( --colour-green ), 50% ); 17 | color: hsl( var( --colour-mono ), var( --mono ) ); 18 | text-shadow: none; 19 | border-width: 0; 20 | text-decoration: none; 21 | border-color: transparent; 22 | border-radius: 0; 23 | } 24 | 25 | 26 | 27 | /* body */ 28 | body { 29 | --mono: 95%; 30 | background: hsl( var( --colour-mono ), var( --mono ) ); 31 | color: hsl( var( --colour-mono ), calc( 100% - var( --mono ) ) ) !important; 32 | font-size: 18px; 33 | } 34 | 35 | .sidebar { 36 | --mono: 95%; 37 | background: hsl( var( --colour-mono ), var( --mono ) ) !important; 38 | } 39 | 40 | .sidebar, .sidebar a { 41 | --mono: 95%; 42 | color: hsl( var( --colour-mono ), calc( 100% - var( --mono ) ) ) !important; 43 | } 44 | 45 | a { 46 | --mono: 95%; 47 | color: hsl( var( --colour-mono ), calc( 100% - var( --mono ) ) ) !important; 48 | } 49 | 50 | img { 51 | border-radius: 0.2em; 52 | margin-bottom: 1em !important 53 | } 54 | 55 | strong { color: inherit !important; } 56 | 57 | 58 | /* cover */ 59 | section.cover, section.cover a { 60 | color: hsl( var( --colour-mono ), 100% ) !important; 61 | border: 0 !important; 62 | } 63 | 64 | section.cover { 65 | text-align: center !important; 66 | } 67 | 68 | section.cover.show:before { 69 | background: linear-gradient( 70 | 180deg, 71 | hsl( var( --colour-green ), 50% ) 0%, 72 | hsl( var( --colour-blue ), 50% ) 100% 73 | ); 74 | } 75 | 76 | section.cover .cover-main { 77 | max-width: 100% 78 | } 79 | 80 | section.cover p:last-child a:first-child { 81 | width: 100% !important; 82 | background-color: hsl( var(--colour-mono), 100% ) !important; 83 | font-weight: bold; 84 | color: hsl( var( --colour-mono ), 0% ) !important; 85 | } 86 | 87 | section.cover p:last-child a:not(:first-child) { 88 | text-decoration: none !important; 89 | display: inline-block; 90 | background-color: hsla( var(--colour-mono), 100%, 0.1 ); 91 | padding: 0 2em; 92 | line-height: 2.5em; 93 | } 94 | 95 | 96 | 97 | /* headings */ 98 | h1, h2, h3, h4, h5, h6 { 99 | font-weight: 700 !important; 100 | color: hsl( var(--colour-mono), 100% ) !important; 101 | } 102 | 103 | article#main h1, 104 | article#main h2, 105 | article#main h3, 106 | article#main h4, 107 | article#main h5, 108 | article#main h6 { 109 | border: none; 110 | padding: 0; 111 | margin: 0; 112 | text-transform: uppercase; 113 | line-height: 2; 114 | } 115 | 116 | 117 | article#main h4, 118 | article#main h5, 119 | article#main h6 { 120 | font-size: var(--heading-h4-font-size); 121 | } 122 | 123 | /* buttons */ 124 | .demo-buttons a { 125 | text-decoration: none !important; 126 | display: inline-block; 127 | background-color: hsla( var(--colour-mono), 100%, 0.1 ); 128 | padding: 0 2em; 129 | line-height: 2.5em; 130 | } 131 | 132 | /* body */ 133 | article#main { 134 | padding-bottom: 5em 135 | } 136 | 137 | .markdown-section ol ol, 138 | .markdown-section ul ol { 139 | list-style-type: lower-alpha 140 | } 141 | 142 | .markdown-section ol ol ol, 143 | .markdown-section ul ol ol { 144 | list-style-type: lower-roman 145 | } 146 | 147 | .markdown-section code { 148 | --mono: 90%; 149 | background: hsla( var( --colour-mono ), calc( 100% - var( --mono ) ), 0.1) !important; 150 | color: hsl( var( --colour-mono ), calc( 100% - var( --mono ) ) ) !important; 151 | } 152 | 153 | .markdown-section p.tip code { 154 | --mono: 90%; 155 | } 156 | 157 | .markdown-section table { 158 | --mono: 95%; 159 | display: table; 160 | table-layout: fixed; 161 | width: 100%; 162 | border-radius: 1em !important; 163 | } 164 | 165 | .markdown-section table tr { 166 | background: none !important; 167 | } 168 | 169 | .markdown-section table th, 170 | .markdown-section table td { 171 | --mono: 90%; 172 | color: hsl( var( --colour-mono ), calc( 100% - var( --mono ) ) ) !important; 173 | } 174 | 175 | :not(pre)>code[class*=lang-], pre[data-lang] { 176 | background: none; 177 | } 178 | 179 | 180 | @media (prefers-color-scheme: dark) { 181 | body, 182 | a, 183 | .sidebar, 184 | .sidebar a, 185 | .markdown-section code, 186 | .markdown-section table, 187 | .markdown-section table th, 188 | .markdown-section table td 189 | { --mono: 10% !important; } 190 | } 191 | -------------------------------------------------------------------------------- /docs/data/terms.md: -------------------------------------------------------------------------------- 1 | # Terms of Service 2 | 3 | ## User's Acknowledgment and Acceptance of Terms 4 | 5 | This website ("Us" or "We") provides the site and various related services (collectively, the "Site") to you, the user, subject to your compliance with all the terms, conditions, and notices contained or referenced herein (the "Terms of Services"), as well as any other written agreement between Us and you. In addition, when using particular services or materials on this site, users shall be subject to any posted rules applicable to such services or materials that may contain terms and conditions in addition to those in these Terms of Services. All such guidelines or rules are hereby incorporated by reference into these Terms of Service. 6 | 7 | These Terms of Service are effective as of July 1st, 2020. We expressly reserve the right to change these Terms of Service from time to time without notice to you. You acknowledge and agree that it is your responsibility to review this site and these Terms of Service from time to time and to familiarise yourself with any modifications. Your continued use of this site after such modifications will constitute acknowledgement of the modified Terms of Service and agreement to abide and be bound by the modified Terms of Service. 8 | 9 | As used in these Terms of Service, references to our "Affiliates" include our owners, subsidiaries, affiliated companies, officers, directors, suppliers, partners, sponsors, and advertisers, and includes (without limitation) all parties involved in creating, producing, and/or delivering this site and/or its contents. 10 | 11 | ## Description of Services 12 | 13 | We make various services available on this site including, but not limited to, articles, tutorials, products, videos, reviews, and other like services. You are responsible for providing, at your own expense, all equipment necessary to use the services, including a computer, modem, and Internet access (including payment of all fees associated with such access). 14 | 15 | We reserve the sole right to either modify or discontinue the site, including any of the site’s features, at any time with or without notice to you. We will not be liable to you or any third party should we exercise such right. Any new features that augment or enhance the then-current services on this site shall also be subject to these Terms of Service. 16 | 17 | ## Conduct on Site 18 | 19 | Your use of the site is subject to all applicable laws and regulations, and you are solely responsible for the substance of your communications through the site. By posting information in or otherwise using any communications service, chat room, message board, newsgroup, software library, or other interactive service that may be available to you on or through this site, you agree that you will not upload, share, post, or otherwise distribute or facilitate distribution of any content -- including text, communications, software, images, sounds, data, or other information that: 20 | 21 | - is unlawful, threatening, abusive, harassing, defamatory, libellous, deceptive, fraudulent, invasive of another’s privacy, tortious, contains explicit or graphic descriptions or accounts of sexual acts (including but not limited to sexual language of a violent or threatening nature directed at another individual or group of individuals), or otherwise violates our rules or policies; 22 | 23 | - victimises, harasses, degrades, or intimidates an individual or group of individuals on the basis of religion, gender, sexual orientation, race, ethnicity, age, or disability; 24 | 25 | - infringes on any patent, trademark, trade secret, copyright, right of publicity, or other proprietary right of any party; 26 | 27 | - constitutes unauthorised or unsolicited advertising, junk or bulk email (also known as "spamming"), chain letters, any other form of unauthorised solicitation, or any form of lottery or gambling; 28 | 29 | - contains software viruses or any other computer code, files, or programs that are designed or intended to disrupt, damage, or limit the functioning of any software, hardware, or telecommunications equipment or to damage or obtain unauthorised access to any data or other information of any third party; or 30 | 31 | - impersonates any person or entity, including any of our employees or representatives. 32 | 33 | We neither endorse nor assume any liability for the contents of any material uploaded or submitted by third party users of the site. We generally do not pre-screen, monitor, or edit the content posted by users of communications services, chat rooms, message boards, newsgroups, software libraries, or other interactive services that may be available on or through this site. However, we and our agents have the right at their sole discretion to remove any content that, in our judgment, does not comply with these Terms of Service and any other rules of user conduct for our site, or is otherwise harmful, objectionable, or inaccurate. We are not responsible for any failure or delay in removing such content. You hereby consent to such removal and waive any claim against us arising out of such removal of content. See "Use of Your Materials" below for a description of the procedures to be followed in the event that any party believes that content posted on this site infringes on any patent, trademark, trade secret, copyright, right of publicity, or other proprietary right of any party. 34 | 35 | In addition, you may not use your account to breach security of another account or attempt to gain unauthorised access to another network or server. Not all areas of the site may be available to you or other authorised users of the site. You shall not interfere with anyone else’s use and enjoyment of the site or other similar services. Users who violate systems or network security may incur criminal or civil liability. 36 | 37 | You agree that we may at any time, and at our sole discretion, terminate your membership, account, or other affiliation with our site without prior notice to you for violating any of the above provisions. In addition, you acknowledge that we will cooperate fully with investigations of violations of systems or network security at other sites, including cooperating with law enforcement authorities in investigating suspected criminal violations. 38 | 39 | ## Third Party Sites and Information 40 | 41 | This site may link you to other sites on the Internet or otherwise include references to information, documents, software, materials and/or services provided by other parties. These sites may contain information or material that some people may find inappropriate or offensive. These other sites and parties are not under our control, and you acknowledge that we are not responsible for the accuracy, copyright compliance, legality, decency, or any other aspect of the content of such sites, nor are we responsible for errors or omissions in any references to other parties or their products and services. The inclusion of such a link or reference is provided merely as a convenience and does not imply endorsement of, or association with, the site or party by us, or any warranty of any kind, either express or implied. 42 | 43 | ## Intellectual Property Information 44 | 45 | Copyright © 2010 Mark Battistella. All Rights Reserved. 46 | 47 | For purposes of these Terms of Service, "Content" is defined as any information, data, communications, software, photos, video, graphics, music, sounds, and other material and services that can be viewed by users on our site. This includes message boards, chat, and other original content. 48 | 49 | By accepting these Terms of Service, you acknowledge and agree that all content presented to you on this site is protected by copyrights, trademarks, service marks, patents or other proprietary rights and laws, and is the sole property of Mark Battistella and/or its Affiliates. You are only permitted to use the content as expressly authorised by us or the specific content provider. Except for a single copy made for personal use only, you may not copy, reproduce, modify, republish, upload, post, transmit, or distribute any documents or information from this site in any form or by any means without prior written permission from us or the specific content provider, and you are solely responsible for obtaining permission before reusing any copyrighted material that is available on this site. Any unauthorised use of the materials appearing on this site may violate copyright, trademark and other applicable laws and could result in criminal or civil penalties. 50 | 51 | Neither we or our Affiliates warrant or represent that your use of materials displayed on, or obtained through, this site will not infringe the rights of third parties. See "User’s Materials" below for a description of the procedures to be followed in the event that any party believes that content posted on this site infringes on any patent, trademark, trade secret, copyright, right of publicity, or other proprietary right of any party. 52 | 53 | The following are registered trademarks, trademarks or service marks of Mark Battistella or its Affiliates: "Mark Battistella", "mbfilms", the "MB" logo. All custom graphics, icons, logos and service names are registered trademarks, trademarks or service marks of Mark Battistella or its Affiliates. All other trademarks or service marks are property of their respective owners. Nothing in these Terms of Service grants you any right to use any trademark, service mark, logo, and/or the name of Mark Battistella or its Affiliates. 54 | 55 | ## Unauthorised Use of Materials 56 | 57 | Subject to our Policy, any communication or material that you transmit to this site or to us, whether by electronic mail, post, or other means, for any reason, will be treated as non-confidential and non-proprietary. While you retain all rights in such communications or material, you grant us and our agents and affiliates a non-exclusive, paid-up, perpetual, and worldwide right to copy, distribute, display, perform, publish, translate, adapt, modify, and otherwise use such material for any purpose regardless of the form or medium (now known or not currently known) in which it is used. 58 | 59 | Please do not submit confidential or proprietary information to us unless we have mutually agreed in writing otherwise. We are also unable to accept your unsolicited ideas or proposals, so please do not submit them to us in any circumstance. 60 | 61 | We respect the intellectual property of others, and we ask you to do the same. If you or any user of this site believes its copyright, trademark or other property rights have been infringed by a posting on this site, you or the user should send notification to our Support Contact immediately. To be effective, the notification must include: 62 | 63 | 1. Identify in sufficient detail the copyrighted work that you believe has been infringed upon or other information sufficient to specify the copyrighted work being infringed). 64 | 65 | 2. Identify the material that you claim is infringing the copyrighted work listed in item #1 above. 66 | 67 | 3. Provide information reasonably sufficient to permit us to contact you (email address is preferred). 68 | 69 | 4. Provide information, if possible, sufficient to permit us to notify the owner/administrator of the allegedly infringing webpage or other content (email address is preferred). 70 | 71 | 5. Send this information via our contact form 72 | 73 | You acknowledge and agree that upon receipt of a notice of a claim of copyright infringement, we may immediately remove the identified materials from our site without liability to you or any other party and that the claims of the complaining party and the party that originally posted the materials will be referred to the Australian Copyright Office for adjudication as provided in the Digital Millennium Copyright Act. 74 | 75 | ## Disclaimer of Warranties 76 | 77 | ALL MATERIALS AND SERVICES ON THIS SITE ARE PROVIDED ON AN "AS IS" AND "AS AVAILABLE" BASIS WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR THE WARRANTY OF NON-INFRINGEMENT. WITHOUT LIMITING THE FOREGOING, WE MAKE NO WARRANTY THAT (A) THE SERVICES AND MATERIALS WILL MEET YOUR REQUIREMENTS, (B) THE SERVICES AND MATERIALS WILL BE UNINTERRUPTED, TIMELY, SECURE, OR ERROR-FREE, (C) THE RESULTS THAT MAY BE OBTAINED FROM THE USE OF THE SERVICES OR MATERIALS WILL BE EFFECTIVE, ACCURATE OR RELIABLE, OR (D) THE QUALITY OF ANY PRODUCTS, SERVICES, OR INFORMATION PURCHASED OR OBTAINED BY YOU FROM THE SITE FROM US OR OUR AFFILIATES WILL MEET YOUR EXPECTATIONS OR BE FREE FROM MISTAKES, ERRORS OR DEFECTS. 78 | 79 | THIS SITE COULD INCLUDE TECHNICAL OR OTHER MISTAKES, INACCURACIES OR TYPOGRAPHICAL ERRORS. WE MAY MAKE CHANGES TO THE MATERIALS AND SERVICES AT THIS SITE, INCLUDING THE PRICES AND DESCRIPTIONS OF ANY PRODUCTS LISTED HEREIN, AT ANY TIME WITHOUT NOTICE. THE MATERIALS OR SERVICES AT THIS SITE MAY BE OUT OF DATE, AND WE MAKE NO COMMITMENT TO UPDATE SUCH MATERIALS OR SERVICES. 80 | 81 | THE USE OF THE SERVICES OR THE DOWNLOADING OR OTHER ACQUISITION OF ANY MATERIALS THROUGH THIS SITE IS DONE AT YOUR OWN DISCRETION AND RISK AND WITH YOUR AGREEMENT THAT YOU WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR LOSS OF DATA THAT RESULTS FROM SUCH ACTIVITIES. 82 | 83 | Through your use of the site, you may have the opportunities to engage in commercial transactions with other users and vendors. You acknowledge that all transactions relating to any merchandise or services offered by any party, including, but not limited to the purchase terms, payment terms, warranties, guarantees, maintenance and delivery terms relating to such transactions, are agreed to solely between the seller or purchaser of such merchandise and services and you. 84 | WE MAKE NO WARRANTY REGARDING ANY TRANSACTIONS EXECUTED THROUGH, OR IN CONNECTION WITH THIS SITE, AND YOU UNDERSTAND AND AGREE THAT SUCH TRANSACTIONS ARE CONDUCTED ENTIRELY AT YOUR OWN RISK. ANY WARRANTY THAT IS PROVIDED IN CONNECTION WITH ANY PRODUCTS, SERVICES, MATERIALS, OR INFORMATION AVAILABLE ON OR THROUGH THIS SITE FROM A THIRD PARTY IS PROVIDED SOLELY BY SUCH THIRD PARTY, AND NOT BY US OR ANY OTHER OF OUR AFFILIATES. 85 | 86 | Content available through this site often represents the opinions and judgments of an information provider, site user, or other person or entity not connected with us. We do not endorse, nor are we responsible for the accuracy or reliability of, any opinion, advice, or statement made by anyone other than an authorised Mark Battistella spokesperson speaking in his/her official capacity. Please refer to the specific editorial policies posted on various sections of this site for further information, which policies are incorporated by reference into these Terms of Service. 87 | 88 | You understand and agree that temporary interruptions of the services available through this site may occur as normal events. You further understand and agree that we have no control over third party networks you may access in the course of the use of this site, and therefore, delays and disruption of other network transmissions are completely beyond our control. 89 | 90 | You understand and agree that the services available on this site are provided "AS IS" and that we assume no responsibility for the timeliness, deletion, mis-delivery or failure to store any user communications or personalisation settings. 91 | 92 | SOME STATES OR JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES, SO SOME OF THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU. 93 | 94 | ## Limitation of Liability 95 | 96 | IN NO EVENT SHALL WE OR OUR AFFILIATES BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY SPECIAL, PUNITIVE, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER, INCLUDING, WITHOUT LIMITATION, THOSE RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND ON ANY THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OF THIS SITE OR OF ANY WEB SITE REFERENCED OR LINKED TO FROM THIS SITE. 97 | 98 | FURTHER, WE SHALL NOT BE LIABLE IN ANY WAY FOR THIRD PARTY GOODS AND SERVICES OFFERED THROUGH THIS SITE OR FOR ASSISTANCE IN CONDUCTING COMMERCIAL TRANSACTIONS THROUGH THIS SITE, INCLUDING WITHOUT LIMITATION THE PROCESSING OF ORDERS. 99 | 100 | SOME JURISDICTIONS PROHIBIT THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU. 101 | 102 | ## Indemnification 103 | 104 | Upon a request by us, you agree to defend, indemnify, and hold us and our Affiliates harmless from all liabilities, claims, and expenses, including attorney’s fees, that arise from your use or misuse of this site. We reserve the right, at our own expense, to assume the exclusive defence and control of any matter otherwise subject to indemnification by you, in which event you will cooperate with us in asserting any available defences. 105 | 106 | ## Security and Password 107 | 108 | You are solely responsible for maintaining the confidentiality of your password and account and for any and all statements made and acts or omissions that occur through the use of your password and account. Therefore, you must take steps to ensure that others do not gain access to your password and account. Our personnel will never ask you for your password. You may not transfer or share your account with anyone, and we reserve the right to immediately terminate your account if you do transfer or share your account. 109 | 110 | ## Participation in Promotions 111 | 112 | From time to time, this site may include advertisements offered by third parties. You may enter into correspondence with or participate in promotions of the advertisers showing their products on this site. Any such correspondence or promotions, including the delivery of and the payment for goods and services, and any other terms, conditions, warranties or representations associated with such correspondence or promotions, are solely between you and the advertiser. We assume no liability, obligation or responsibility for any part of any such correspondence or promotion. 113 | 114 | ## E-mail, Messaging, Blogging, and Chat Services 115 | 116 | We may make email, messaging, blogging, or chat services (collectively, "Communications") available to users of our site, either directly or through a third-party provider. We make available separate supplemental agreements characterising the relationship between you and us that, except where expressly noted or contradictory, includes these Terms. 117 | 118 | We will not inspect or disclose the contents of private Communications except with the consent of the sender or the recipient, or in the narrowly-defined situations provided under the Electronic Communications Privacy Act, or as other required by law or by court or governmental order. Further information is available in our Policy. 119 | 120 | We may employ automated monitoring devices or techniques to protect our users from mass unsolicited communications (also known as "spam") and/or other types of electronic communications that we deem inconsistent with our business purposes. However, such devices or techniques are not perfect, and we will not be responsible for any legitimate communication that is blocked, or for any unsolicited communication that is not blocked. 121 | 122 | Mailboxes may have a limited storage capacity. If you exceed the maximum permitted storage space, we may employ automated devices that delete or block email messages that exceed the limit. We will not be responsible for such deleted or blocked messages. 123 | 124 | ## International Use 125 | 126 | Although this site may be accessible worldwide, we make no representation that materials on this site are appropriate or available for use in locations outside Australia, and accessing them from territories where their contents are illegal is prohibited. Those who choose to access this site from other locations do so on their own initiative and are responsible for compliance with local laws. Any offer for any product, service, and/or information made in connection with this site is void where prohibited. 127 | 128 | ## Termination of Use 129 | 130 | You agree that we may, in our sole discretion, terminate or suspend your access to all or part of the site with or without notice and for any reason, including, without limitation, breach of these Terms of Service. Any suspected fraudulent, abusive or illegal activity may be grounds for terminating your relationship and may be referred to appropriate law enforcement authorities. 131 | 132 | Upon termination or suspension, regardless of the reasons therefore, your right to use the services available on this site immediately ceases, and you acknowledge and agree that we may immediately deactivate or delete your account and all related information and files in your account and/or bar any further access to such files or this site. We shall not be liable to you or any third party for any claims or damages arising out of any termination or suspension or any other actions taken by us in connection with such termination or suspension. 133 | 134 | ## Governing Law 135 | 136 | This site (excluding any linked sites) is controlled by us from our offices within Victoria, Australia. It can be accessed from other countries around the world. As each of these places has laws that may differ from those of Australia, by accessing this site both of us agree that the statutes and laws of the State of Victoria, without regard to the conflicts of laws principles thereof and the United Nations Convention on the International Sales of Goods, will apply to all matters relating to the use of this site and the purchase of products and services available through this site. Each of us agrees and hereby submits to the exclusive personal jurisdiction and venue any court of competent jurisdiction within the State of Victoria with respect to such matters. 137 | 138 | ## Notices 139 | 140 | All notices to a party shall be in writing and shall be made via email. Notices to us must be sent to the attention of Customer Service via our contact form. We may broadcast notices or messages through the site to inform you of changes to the site or other matters of importance, and such broadcasts shall constitute notice to you at the time of sending. 141 | 142 | ## Entire Agreement 143 | 144 | These terms and conditions constitute the entire agreement and understanding between us concerning the subject matter of this agreement and supersedes all prior agreements and understandings of the parties with respect to that subject matter. These Terms of Service may not be altered, supplemented, or amended by the use of any other document(s). Any attempt to alter, supplement or amend this document or to enter an order for products or services which are subject to additional or altered terms and conditions shall be null and void, unless otherwise agreed to in a written agreement signed by you and us. To the extent that anything in or associated with this site is in conflict or inconsistent with these Terms of Service, these Terms of Service shall take precedence. 145 | 146 | ## Miscellaneous 147 | 148 | In any action to enforce these Terms of Service, the prevailing party will be entitled to costs and attorneys fees. Any cause of action brought by you against us or our Affiliates must be instituted with one year after the cause of action arises or be deemed forever waived and barred. 149 | 150 | You may not assign your rights and obligations under these Terms of Service to any party, and any purported attempt to do so will be null and void. We may free assign our rights and obligations under these Terms of Service. 151 | 152 | You agree not to sell, resell, reproduce, duplicate, copy or use for any commercial purposes any portion of this site, or use of or access to this site. 153 | 154 | In addition to any excuse provided by applicable law, we shall be excused from liability for non-delivery or delay in delivery of products and services available through our site arising from any event beyond our reasonable control, whether or not foreseeable by either party, including but not limited to, labor disturbance, war, fire, accident, adverse weather, inability to secure transportation, governmental act or regulation, and other causes or events beyond our reasonable control, whether or not similar to those which are enumerated above. 155 | 156 | If any part of these Terms of Service is held invalid or unenforceable, that portion shall be construed in a manner consistent with applicable law to reflect, as nearly as possible, the original intentions of the parties, and the remaining portions shall remain in full force and effect. 157 | 158 | Any failure by us to enforce or exercise any provision of these Terms of Service or related rights shall not constitute a waiver of that right or provision. 159 | 160 | ## Contact Information 161 | 162 | Except as explicitly noted on this site, the services available through this site are offered by the site located in Melbourne, Australia. If you notice that any user is violating these Terms of Service, please contact us via our contact form. -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | docsify: autoHeaders 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 | 17 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /docs/pages/_page1.md: -------------------------------------------------------------------------------- 1 | @autoHeader:1 2 | 3 | ?> This is **Page 1**.
The `autoHeader` of this page is: `@autoHeader:1`.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. 4 | 5 | # Heading 1 6 | 7 | ## Heading 2 8 | 9 | ### Heading 3 10 | 11 | #### Heading 4 12 | 13 | ##### Heading 5 14 | 15 | ###### Heading 6 16 | 17 | --- 18 | 19 | ## Heading 2 20 | 21 | ### Heading 3 22 | 23 | ## Heading 2 24 | 25 | ### Heading 3 26 | 27 | #### Heading 4 28 | 29 | ##### Heading 5 30 | 31 | ###### Heading 6 32 | 33 | ###### Heading 6 34 | 35 | --- 36 | 37 | ```text 38 | Expected output: 39 | 40 | 1. HEADING 1 41 | 1.1. HEADING 2 42 | 1.1.1. HEADING 3 43 | 1.1.1.1. HEADING 4 44 | 1.1.1.1.1. HEADING 5 45 | 1.1.1.1.1.1. HEADING 6 46 | 47 | 1.2. HEADING 2 48 | 1.2.1. HEADING 3 49 | 1.3. HEADING 2 50 | 1.3.1. HEADING 3 51 | 1.3.1.1. HEADING 4 52 | 1.3.1.1.1. HEADING 5 53 | 1.3.1.1.1.1. HEADING 6 54 | 1.3.1.1.1.2. HEADING 6 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/pages/_page2.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ?> This is **Page 2**.
The `autoHeader` of this page is: ``.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. 4 | 5 | # Heading 1 6 | 7 | ## Heading 2 8 | 9 | ### Heading 3 10 | 11 | #### Heading 4 12 | 13 | ##### Heading 5 14 | 15 | ###### Heading 6 16 | 17 | --- 18 | 19 | ## Heading 2 20 | 21 | ### Heading 3 22 | 23 | ## Heading 2 24 | 25 | ### Heading 3 26 | 27 | #### Heading 4 28 | 29 | ##### Heading 5 30 | 31 | ###### Heading 6 32 | 33 | ###### Heading 6 34 | 35 | --- 36 | 37 | ```text 38 | Expected output: 39 | 40 | 11. HEADING 1 41 | 11.22. HEADING 2 42 | 11.22.33. HEADING 3 43 | 11.22.33.44. HEADING 4 44 | 11.22.33.44.55. HEADING 5 45 | 11.22.33.44.55.66. HEADING 6 46 | 47 | 11.23. HEADING 2 48 | 11.23.1. HEADING 3 <-- note: restarts at 1 when resetting (22 --> 23) 49 | 11.24. HEADING 2 50 | 11.24.1. HEADING 3 51 | 11.24.1.1. HEADING 4 52 | 11.24.1.1.1. HEADING 5 53 | 11.24.1.1.1.1. HEADING 6 54 | 11.24.1.1.1.2. HEADING 6 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/pages/_page3.md: -------------------------------------------------------------------------------- 1 | @autoHeader: 2 | 3 | ?> This is **Page 3**.
The `autoHeader` of this page is: `@autoHeader:`.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. 4 | 5 | # Heading 1 6 | 7 | ## Heading 2 8 | 9 | ### Heading 3 10 | 11 | #### Heading 4 12 | 13 | ##### Heading 5 14 | 15 | ###### Heading 6 16 | 17 | --- 18 | 19 | ## Heading 2 20 | 21 | ### Heading 3 22 | 23 | ## Heading 2 24 | 25 | ### Heading 3 26 | 27 | #### Heading 4 28 | 29 | ##### Heading 5 30 | 31 | ###### Heading 6 32 | 33 | ###### Heading 6 34 | 35 | --- 36 | 37 | ```text 38 | Expected output: 39 | 40 | @autoHeader: <-- note: invalid input, should exit script and not remove 41 | 42 | HEADING 1 <-- note: invalid signifier, means no numbering should occur 43 | HEADING 2 44 | HEADING 3 45 | HEADING 4 46 | HEADING 5 47 | HEADING 6 48 | 49 | HEADING 2 50 | HEADING 3 51 | HEADING 2 52 | HEADING 3 53 | HEADING 4 54 | HEADING 5 55 | HEADING 6 56 | HEADING 6 57 | ``` 58 | -------------------------------------------------------------------------------- /docs/pages/_page4.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ?> This is **Page 4**.
The `autoHeader` of this page is: ``.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. 4 | 5 | # Heading 1 6 | 7 | ## Heading 2 8 | 9 | ### Heading 3 10 | 11 | #### Heading 4 12 | 13 | ##### Heading 5 14 | 15 | ###### Heading 6 16 | 17 | --- 18 | 19 | ## Heading 2 20 | 21 | ### Heading 3 22 | 23 | ## Heading 2 24 | 25 | ### Heading 3 26 | 27 | #### Heading 4 28 | 29 | ##### Heading 5 30 | 31 | ###### Heading 6 32 | 33 | ###### Heading 6 34 | 35 | --- 36 | 37 | ```text 38 | Expected output: 39 | 40 | Z. HEADING 1 41 | Z.Y. HEADING 2 42 | Z.Y.A. HEADING 3 43 | Z.Y.A.A. HEADING 4 44 | Z.Y.A.A.A. HEADING 5 45 | Z.Y.A.A.A.A. HEADING 6 46 | 47 | Z.Z. HEADING 2 48 | Z.Z.A. HEADING 3 <-- note: restarts at A when resetting (22 --> 23) 49 | Z.ZA. HEADING 2 50 | Z.AA.A. HEADING 3 51 | Z.AA.A.A. HEADING 4 52 | Z.AA.A.A.A. HEADING 5 53 | Z.AA.A.A.A.A. HEADING 6 54 | Z.AA.A.A.A.B. HEADING 6 55 | ``` -------------------------------------------------------------------------------- /docs/pages/_page5.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ?> This is **Page 5**.
The `autoHeader` of this page is: ``.
Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. 4 | 5 | # Heading 1 6 | 7 | ## Heading 2 8 | 9 | ### Heading 3 10 | 11 | #### Heading 4 12 | 13 | ##### Heading 5 14 | 15 | ###### Heading 6 16 | 17 | --- 18 | 19 | ## Heading 2 20 | 21 | ### Heading 3 22 | 23 | ## Heading 2 24 | 25 | ### Heading 3 26 | 27 | #### Heading 4 28 | 29 | ##### Heading 5 30 | 31 | ###### Heading 6 32 | 33 | ###### Heading 6 34 | 35 | --- 36 | 37 | ```text 38 | Expected output: 39 | 40 | <-- note: invalid input, should exit 41 | script and not remove, though 42 | since it is a HTML comment it 43 | will not show up in render 44 | 45 | HEADING 1 <-- note: invalid signifier, means no 46 | HEADING 2 numbering should occur 47 | HEADING 3 48 | HEADING 4 49 | HEADING 5 50 | HEADING 6 51 | 52 | HEADING 2 53 | HEADING 3 54 | HEADING 2 55 | HEADING 3 56 | HEADING 4 57 | HEADING 5 58 | HEADING 6 59 | HEADING 6 60 | ``` 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@markbattistella/docsify-autoheaders", 3 | "version": "5.0.1", 4 | "description": "Auto header numbering for docsify.js", 5 | "main": "./dist/docsify-auto-headers.min.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/markbattistella/docsify-auto-headers.git" 9 | }, 10 | "scripts": { 11 | "launch": "docsify serve ./docs -o", 12 | "update": "ncu -u && npm update && npm install", 13 | "babel": "npx babel ./dist/docsify-auto-headers.js -o ./dist/docsify-auto-headers.babel.js", 14 | "uglify": "uglifyjs ./dist/docsify-auto-headers.babel.js --verbose -c -m -o ./dist/docsify-auto-headers.min.js", 15 | "minify": "npm run babel && npm run uglify", 16 | "patch": "node ./.github/scripts/release.js -patch", 17 | "minor": "node ./.github/scripts/release.js -minor", 18 | "major": "node ./.github/scripts/release.js -major" 19 | }, 20 | "devDependencies": { 21 | "@babel/cli": "^7.24.7", 22 | "@babel/core": "^7.24.7", 23 | "@babel/preset-env": "^7.24.7", 24 | "docsify-cli": "^4.4.4", 25 | "jsonfile": "^6.1.0" 26 | }, 27 | "babel": { 28 | "presets": [ 29 | "@babel/env" 30 | ] 31 | }, 32 | "unpkg": "./dist/docsify-auto-headers.min.js", 33 | "jsdelivr": "./dist/docsify-auto-headers.min.js", 34 | "keywords": [ 35 | "auto numbering", 36 | "headings", 37 | "header", 38 | "docsify", 39 | "plugin" 40 | ], 41 | "author": "Mark Battistella", 42 | "license": "MIT", 43 | "bugs": { 44 | "url": "https://github.com/markbattistella/docsify-auto-headers/issues" 45 | }, 46 | "homepage": "https://autoheader.docsify.markbattistella.com" 47 | } 48 | --------------------------------------------------------------------------------