@markbattistella/docsify-autoheaders 4.1.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4 @@
1
+ # browsers we support
2
+ last 4 versions
3
+ > 0.5%
4
+ ie >= 11
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
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 `<!-- autoHeaders: -->` 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
package/README.md CHANGED
@@ -1,129 +1,98 @@
1
1
  <div align="center">
2
2
 
3
- # docsify-autoHeaders
4
-
5
- ![Github2npm](https://github.com/markbattistella/docsify-autoHeaders/workflows/gh2npm/badge.svg?event=registry_package) ![npm (scoped)](https://img.shields.io/npm/v/@markbattistella/docsify-autoheaders) ![GitHub](https://img.shields.io/github/license/markbattistella/docsify-autoheaders) ![npm bundle size (scoped)](https://img.shields.io/bundlephobia/minzip/@markbattistella/docsify-autoheaders)
6
-
7
- ---
8
-
9
- [![](https://img.shields.io/badge/%20-@markbattistella-blue?logo=paypal&style=for-the-badge)](https://www.paypal.me/markbattistella/6AUD)
10
- [![](https://img.shields.io/badge/%20-buymeacoffee-black?logo=buy-me-a-coffee&style=for-the-badge)](https://www.buymeacoffee.com/markbattistella)
11
-
12
- [![](https://img.shields.io/badge/demo-@markbattistella/docsify--autoHeaders-1E5749?style=for-the-badge)](https://markbattistella.github.io/docsify-autoHeaders/)
3
+ # docsify.js auto-headers
13
4
 
14
5
  </div>
15
6
 
16
- ---
17
-
18
- # Auto number headings
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 utilizing this plugin, you can easily manage and navigate through your documentation headers, ensuring consistency and improved readability.
19
8
 
20
- This plugin is designed to create heading numbers in your pages if you are creating a reference guide.
9
+ ## Demo pages
21
10
 
22
- It stops you from having to manually number the heading, and then have to then trawl through every heading afterwards to change the numbering system again.
23
-
24
- It allows you to either have all the headings in one page, or if you split them over many `markdown` documents then specify what the heading number it should be starting at.
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`.<br>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: `<!-- autoHeader:11.22.33.44.55.66 -->`.<br>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:`.<br>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: `<!-- autoHeader:Z.Y -->`.<br>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: `<!-- autoHeader:1-2-3 -->`.<br>Assuming the original configuration is used, the splitter is `.` and the levels are `H1`-`H6`. |
25
18
 
26
19
  ## Installation
27
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
+
28
23
  ### Update `index.html` file
29
24
 
30
25
  Assuming you have a working [docsify](https://docsify.js.org/) framework set up, it is easy to use the plugin.
31
26
 
32
- 1. Add the following script tag to your `index.html` via either CDN or downloading it and using it locally:
27
+ 1. Add one of the following script tags to your `index.html` via either CDN or downloading it and using it locally:
33
28
 
34
29
  ```html
35
30
  <!-- unpkg.com -->
36
- <script src="https://unpkg.com/@markbattistella/docsify-autoheaders@latest"></script>
31
+ <script src="https://unpkg.com/@markbattistella/docsify-auto-headers@latest"></script>
37
32
 
38
33
  <!-- jsDelivr -->
39
- <script src="https://cdn.jsdelivr.net/npm/@markbattistella/docsify-autoheaders@latest"></script>
34
+ <script src="https://cdn.jsdelivr.net/npm/@markbattistella/docsify-auto-headers@latest"></script>
40
35
 
41
36
  <!-- locally -->
42
- <script src="docsify-autoheaders.min.js"></script>
37
+ <script src="docsify-auto-headers.min.js"></script>
43
38
  ```
44
39
 
45
- 1. In docsify setup configure the plugin (see [configuration](#configuration) for setup):
40
+ 1. In docsify setup, configure the plugin:
46
41
 
47
42
  ```js
48
43
  <script>
49
44
  window.$docsify = {
50
45
  autoHeaders: {
51
- separator: String, // how numbers should be separated
52
- levels: String, // heading levels h[1-6]
53
- scope: String, // plugin search scope
54
- debug: Boolean // show console.log messages
55
- }
56
- };
57
- </script>
58
- ```
59
-
60
- ### npm install
61
46
 
62
- Or if you're using `npm` to manage your dependencies:
47
+ // Separator for header numbering (e.g., '.', '-', ')')
48
+ separator: '.',
63
49
 
64
- ```sh
65
- npm i @markbattistella/docsify-autoheaders
66
- ```
50
+ // Boolean indicating if headers should be added to the sidebar
51
+ sidebar: false,
67
52
 
68
- ### Configuration
53
+ // Number of header levels to include (1 to 6) or an object with start and finish properties
54
+ levels: 6,
55
+ // levels: { start: 1, finish: 6 }
69
56
 
70
- There are some options available for the `docsify-autoHeaders`:
71
-
72
- | setting | options |
73
- |-----------|-----------------------------------------------------------------|
74
- | separator | how the numbers are separated - `decimal`, `dash`, or `bracket` |
75
- | levels | heading levels to target `1-6` |
76
- | scope | the element to narrow it down to. `#main` is the default scope |
77
- | debug | `true` or `false` if you want to see `console.log` info |
78
-
79
- ### Usage
80
-
81
- At the top of your file add the following snippet:
82
-
83
- ```md
84
- @autoHeader:#
85
- ```
86
-
87
- At the end of the identifier `(marked with #)`, add the starting heading number. If you don't have a valid entry then it won't auto number.
57
+ // Boolean to enable or disable debug messages
58
+ debug: false
59
+ }
60
+ };
61
+ </script>
62
+ ```
88
63
 
89
- It accepts only numbers.
64
+ ## Configuration
90
65
 
91
- You can have a starting header at `0` using:
66
+ There are several options available for the docsify-auto-headers plugin:
92
67
 
93
- ```md
94
- @autoHeader:0
95
- ```
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` |
96
75
 
97
- ## Customising individual numbers
76
+ ## Usage
98
77
 
99
- You can also manually set the starting number of each of the levels by using the following format:
78
+ The plugin can be configured to apply scoped heading counts in either the sidebar or the main content, depending on your setup.
100
79
 
101
- ```md
102
- @autoheaders:3.5.6.6.1.12
80
+ ### Sidebar
103
81
 
104
- ##### New heading
105
- ```
82
+ If the `sidebar` option is enabled, the headers will be included in the sidebar and processed before rendering the markdown.
106
83
 
107
- Respectively starting the first level 6 heading (H6) at:
84
+ ### Main Content
108
85
 
109
- 3.5.6.6.2.1 New heading
86
+ If the `sidebar` option is disabled, the headers will be processed and applied directly to the HTML after rendering.
110
87
 
111
88
  ## Contributing
112
89
 
113
- 1. Clone the repo:
114
-
115
- `git clone https://github.com/markbattistella/docsify-autoHeaders.git`
116
-
117
- 1. Create your feature branch:
118
-
119
- `git checkout -b my-feature`
120
-
121
- 1. Commit your changes:
90
+ 1. Clone the repo:<br>`git clone https://github.com/markbattistella/docsify-auto-headers.git`
122
91
 
123
- `git commit -am 'Add some feature'`
92
+ 1. Create your feature branch:<br>`git checkout -b my-feature`
124
93
 
125
- 1. `Push` to the branch:
94
+ 1. Commit your changes:<br>`git commit -am 'Add some feature'`
126
95
 
127
- `git push origin my-new-feature`
96
+ 1. `Push` to the branch:<br>`git push origin my-new-feature`
128
97
 
129
98
  1. Submit the `pull` request
@@ -0,0 +1,579 @@
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 <!-- autoHeader: --> 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<Object>|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:|<!--\s+autoHeader:)([\d.a-zA-Z\-:,~]*)(?:\s+-->)?/;
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
+ })();
@@ -0,0 +1,2 @@
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<arguments.length;e++){var t=null!=arguments[e]?arguments[e]:{};e%2?ownKeys(Object(t),!0).forEach(function(e){_defineProperty(r,e,t[e])}):Object.getOwnPropertyDescriptors?Object.defineProperties(r,Object.getOwnPropertyDescriptors(t)):ownKeys(Object(t)).forEach(function(e){Object.defineProperty(r,e,Object.getOwnPropertyDescriptor(t,e))})}return r}function _defineProperty(e,r,t){return(r=_toPropertyKey(r))in e?Object.defineProperty(e,r,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[r]=t,e}function _toPropertyKey(e){e=_toPrimitive(e,"string");return"symbol"==_typeof(e)?e:e+""}function _toPrimitive(e,r){if("object"!=_typeof(e)||!e)return e;var t=e[Symbol.toPrimitive];if(void 0===t)return("string"===r?String:Number)(e);t=t.call(e,r||"default");if("object"!=_typeof(t))return t;throw new TypeError("@@toPrimitive must return a primitive value.")}function _createForOfIteratorHelper(e,r){var t,n,o,a,i="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(i)return o=!(n=!0),{s:function(){i=i.call(e)},n:function(){var e=i.next();return n=e.done,e},e:function(e){o=!0,t=e},f:function(){try{n||null==i.return||i.return()}finally{if(o)throw t}}};if(Array.isArray(e)||(i=_unsupportedIterableToArray(e))||r&&e&&"number"==typeof e.length)return i&&(e=i),a=0,{s:r=function(){},n:function(){return a>=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<r;t++)n[t]=e[t];return n}function _iterableToArrayLimit(e,r){var t=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=t){var n,o,a,i,u=[],s=!0,c=!1;try{if(a=(t=t.call(e)).next,0===r){if(Object(t)!==t)return;s=!1}else for(;!(s=(n=a.call(t)).done)&&(u.push(n.value),u.length!==r);s=!0);}catch(e){c=!0,o=e}finally{try{if(!s&&null!=t.return&&(i=t.return(),Object(i)!==i))return}finally{if(c)throw o}}return u}}function _arrayWithHoles(e){if(Array.isArray(e))return e}function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}!function(){function e(e,r){function y(e,r){return e&&console.warn("Docsify Auto Headers:\n>> ".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)return y(r.debug,c)}if(!t(n,1,6)||!t(o,1,6))return y(r.debug,l);for(var a={},i=1;i<=6;i++)a["h".concat(i)]={inScope:t(i,n,o)};return a}function d(e,r){var t=e.split(r.separator).map(function(e){return e.trim()});if(6<t.length)return y(r.debug,i);var n=t.every(function(e){return/^\d+$/.test(e)}),o=t.every(function(e){return/^[a-z]+$/.test(e)}),e=t.every(function(e){return/^[A-Z]+$/.test(e)});if(!n&&!o&&!e)return y(r.debug,f);for(;t.length<6;)t.push(n?"1":o?"a":"A");var a=n?"numeric":o?"alphabetic-lower":"alphabetic-upper";return t.map(function(e){return{counter:function(e,r){if("numeric"===r)return parseInt(e,10);e=e.toUpperCase();for(var t=0,n=0;n<e.length;n++)t=(t*=26)+(e.charCodeAt(n)-65+1);return t}(e,a),type:a}})}function n(e,r,t,n){var i,u,s,c,o=g(e),a=o.currentCounts,l=o.scopedTagNames;return"html"===n?(_toConsumableArray((t=(new DOMParser).parseFromString(t,"text/html").body).querySelectorAll("h1, h2, h3, h4, h5, h6")).forEach(function(e){return function(e,r){var t=this.currentCounts,n=this.scopedTagNames,o=e.tagName.toLowerCase(),a=["h1","h2","h3","h4","h5","h6"],i=t.get(o);i.current+=1,i.skipDownstreamReset||a.slice(a.indexOf(o)+1).forEach(function(e){t.has(e)&&(t.get(e).current=0)}),i.skipDownstreamReset=!1,n.has(o)&&(i=a.slice(0,a.indexOf(o)+1).map(function(e){var e=t.get(e),r=e.current,e=e.type;return v(r,e)}).join(r.separator)+r.separator,e.innerHTML="".concat(i," ").concat(e.innerHTML))}.call({currentCounts:a,scopedTagNames:l},e,r)}),t.innerHTML):"markdown"===n?(o=t,i=r,n=g(n=e),u=n.currentCounts,s=n.scopedTagNames,c=["h1","h2","h3","h4","h5","h6"],o.replace(/^(#{1,6})\s+(.*)$/gm,function(e,r,t){var n=r.length,o="h".concat(n),a=u.get(o);return a.current+=1,a.skipDownstreamReset||c.slice(n).forEach(function(e){u.has(e)&&(u.get(e).current=0)}),a.skipDownstreamReset=!1,s.has(o)?(a=c.slice(0,n).map(function(e){var e=u.get(e),r=e.current,e=e.type;return v(r,e)}).join(i.separator)+i.separator,"".concat(r," ").concat(a," ").concat(t)):e})):void 0}var p,t,o="Configuration settings are not set correctly. Please review the autoHeaders parameters and documentation.",a="The sidebar setting for autoHeaders only accepts a boolean of true or false. Please check you've entered this data correctly.",u="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.",s="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.",c="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.",l="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.",i="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",f="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.",b="The current markdown file is missing the @autoHeader: or \x3c!-- autoHeader: --\x3e signifier",m=!0,v=function(e,r){if("numeric"===r)return e+"";e--;for(var t="";0<=e;){t=String.fromCharCode(65+e%26)+t;e=Math.floor(e/26)-1}return"alphabetic-lower"===r?t.toLowerCase():t},g=function(e){e=Object.entries(e);return{currentCounts:new Map(e.map(function(e){var e=_slicedToArray(e,2),r=e[0],e=e[1],t=e.counter,e=e.type,t=parseInt(t,10);return[r,{current:Number.isFinite(t)?t-1:0,type:e,skipDownstreamReset:!0}]})),scopedTagNames:new Set(e.filter(function(e){e=_slicedToArray(e,2);e[0];return e[1].inScope}).map(function(e){return _slicedToArray(e,1)[0]}))}},w=(t=_).separator&&void 0!==t.levels?{separator:{decimal:".",dot:".",dash:"-",hyphen:"-",bracket:")",parenthesis:")"}[t.separator]||t.separator,levels:t.levels||6,sidebar:!!t.sidebar,debug:!!t.debug}:y(t.debug,o);w&&(p={separator:w.separator,levels:h(w.levels,w),sidebar:(t=w.sidebar,o=w,"boolean"!=typeof t?y(o.debug,a):t),debug:w.debug},e.beforeEach(function(e){if(m=!0){r=p;var r,t=(t=(n=e).match(/^\s*(?:@autoHeader:|<!--\s+autoHeader:)([\d.a-zA-Z\-:,~]*)(?:\s+-->)?/))?(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))}();
package/package.json CHANGED
@@ -1,28 +1,47 @@
1
1
  {
2
- "name": "@markbattistella/docsify-autoheaders",
3
- "version": "4.1.0",
4
- "description": "Auto header numbering for docsify.js",
5
- "main": "dist/docsify-autoHeaders.min.js",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/markbattistella/docsify-autoHeaders.git"
9
- },
10
- "author": "Mark Battistella",
11
- "license": "MIT",
12
- "bugs": {
13
- "url": "https://github.com/markbattistella/docsify-autoHeaders/issues"
14
- },
15
- "homepage": "https://github.com/markbattistella/docsify-autoHeaders#readme",
16
- "keywords": [
17
- "auto numbering",
18
- "headings",
19
- "docsify",
20
- "plugin"
21
- ],
22
- "directories": {
23
- "doc": "docs"
24
- },
25
- "scripts": {
26
- "launch": "docsify serve ./docs -o"
27
- }
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"
28
47
  }
package/.gitattributes DELETED
@@ -1,7 +0,0 @@
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
@@ -1,443 +0,0 @@
1
- /*
2
- * docsify-autoHeaders.js v4.1.0
3
- * (https://markbattistella.github.io/docsify-autoHeaders/)
4
- * Copyright (c) 2021 Mark Battistella (@markbattistella)
5
- * Licensed under MIT
6
- */
7
-
8
- //
9
- // MARK: - javascript policy
10
- //
11
- 'use strict';
12
-
13
-
14
-
15
- //
16
- // MARK: - default values
17
- //
18
- const autoHeaderOptions = {
19
- separator: '',
20
- custom: '',
21
- levels: '',
22
- scope: '',
23
- debug: false
24
- }
25
-
26
-
27
-
28
- //
29
- // MARK: - get the index.html options
30
- //
31
- function getAutoHeadersOptions( autoHeaderOptions ) {
32
-
33
- // check for empty config
34
- if(
35
- !autoHeaderOptions.separator ||
36
- !autoHeaderOptions.levels ||
37
- !autoHeaderOptions.scope
38
- ) {
39
- return console.error(
40
- 'ERROR: config settings not set'
41
- )
42
- }
43
-
44
- // blank separator
45
- let separator = (
46
- autoHeaderOptions.separator == 'other' ?
47
- autoHeaderOptions.custom ?
48
- autoHeaderOptions.custom : '_'
49
- : ''
50
- );
51
-
52
- // output the correct from words
53
- switch( autoHeaderOptions.separator ) {
54
-
55
- case 'decimal' :
56
- separator = '.';
57
- break;
58
-
59
- case 'dash' :
60
- separator = '-';
61
- break;
62
-
63
- case 'bracket' :
64
- separator = ')';
65
- break;
66
-
67
- case 'other' :
68
- separator = separator;
69
- break;
70
-
71
- default:
72
- return;
73
- }
74
-
75
- // get other settings
76
- let levels = (
77
- autoHeaderOptions.levels ?
78
- autoHeaderOptions.levels : 6
79
- );
80
-
81
- let scope = (
82
- autoHeaderOptions.scope ?
83
- autoHeaderOptions.scope : "main"
84
- );
85
-
86
- let debug = (
87
- autoHeaderOptions.debug === true ?
88
- true : false
89
- );
90
-
91
- // return the array
92
- return [
93
- separator,
94
- levels,
95
- scope,
96
- debug
97
- ];
98
- }
99
-
100
-
101
-
102
- //
103
- // MARK: - main function
104
- //
105
- function autoHeaders( hook, vm ) {
106
-
107
- //
108
- // MARK: - safety
109
- //
110
- if( getAutoHeadersOptions( autoHeaderOptions ) === undefined ) {
111
- return;
112
- }
113
-
114
-
115
- //
116
- // MARK: - variables
117
- //
118
-
119
- let getHeadingNumber = null;
120
-
121
- // get the options variables
122
- const getAutoHeadersOptionsArray = getAutoHeadersOptions(
123
- autoHeaderOptions
124
- ),
125
-
126
- // create new variables
127
- optionsSeparator = getAutoHeadersOptionsArray[ 0 ],
128
- optionsLevel = getAutoHeadersOptionsArray[ 1 ],
129
- optionsScope = getAutoHeadersOptionsArray[ 2 ],
130
- optionsDebug = getAutoHeadersOptionsArray[ 3 ],
131
-
132
- // get the heading range from options
133
- setHeadingRange = ( headingInputValue ) => {
134
-
135
- // variables
136
- let output = '';
137
-
138
- // 1. check if is a string
139
- if(
140
- typeof optionsLevel === 'string'
141
- ) {
142
-
143
- // set it as H1 to
144
- output = `H1-${ headingInputValue }`;
145
-
146
- // 2. check if is object and not null
147
- } else if(
148
- typeof optionsLevel === 'object' &&
149
- optionsLevel !== null
150
- ) {
151
-
152
- // error catching
153
-
154
- // -- start has to be less than finish
155
- if( headingInputValue.start > headingInputValue.finish ) {
156
- return console.log( 'ERROR: heading start level cannot be greater than finish level' );
157
- }
158
-
159
- // -- start and finish need to be between 1-6 incl.
160
- if(
161
- ( headingInputValue.start < 1 ) ||
162
- ( headingInputValue.start > 6 ) ||
163
- ( headingInputValue.finish < 1 ) ||
164
- ( headingInputValue.finish > 6 )
165
- ) {
166
- return console.log( 'ERROR: heading levels need to be between 1-6' );
167
- }
168
-
169
- // set the range
170
- output = `H${ headingInputValue.start }-${ headingInputValue.finish }`;
171
- }
172
-
173
- return output;
174
- },
175
-
176
- // save as constant
177
- optionsLevelRange = setHeadingRange( optionsLevel );
178
-
179
-
180
- //
181
- // MARK: - check if the document starts with the signifier
182
- //
183
-
184
- // get the `@autoHeader:` data
185
- hook.beforeEach( function( content ) {
186
-
187
- // get the first 12 characters
188
- const getFirstCharacters = content.slice( 0, 12 );
189
-
190
- // check if beginning with the plugin key
191
- if( getFirstCharacters === "@autoHeader:" ) {
192
-
193
- // get the first line of data
194
- const getFirstLine = content.split( "\n" )[0];
195
-
196
- // get everything after the `:`
197
- getHeadingNumber = getFirstLine.split( ":" )[1];
198
-
199
- // there is no data to continue
200
- if(
201
- !getHeadingNumber ||
202
- getHeadingNumber == null ||
203
- getHeadingNumber == ''
204
- ) {
205
-
206
- // set the headerNumber to null
207
- getHeadingNumber = null;
208
-
209
- // transform the data
210
- } else {
211
-
212
- // make an array from the separator
213
- getHeadingNumber = getHeadingNumber.split( optionsSeparator );
214
-
215
- // dont work with too many items in the array
216
- if( getHeadingNumber.length > 6 ) {
217
-
218
- // set the headerNumber to null
219
- getHeadingNumber = null;
220
-
221
- } else {
222
-
223
- // pad in the extra array items
224
- getHeadingNumber = getHeadingNumber.concat(
225
- new Array( 6 ) // add a new array upto 6 items
226
- .fill( 0 ) // fill it with zeros
227
- )
228
- .slice( 0, 6 ) // cut off after 6 items
229
- .map( x => +x ); // map the Strings to Int
230
- }
231
- }
232
-
233
- // remove the line
234
- var cleanedContent = content.replace( getFirstLine, '' );
235
-
236
- // return the cleaned content
237
- return cleanedContent;
238
-
239
- } else {
240
-
241
- // set the headerNumber to null
242
- getHeadingNumber = null;
243
- }
244
- });
245
-
246
-
247
-
248
- //
249
- // MARK: - add the heading numbers
250
- //
251
-
252
- hook.doneEach( function() {
253
-
254
- //
255
- // 1. scope checking
256
- //
257
-
258
- // set the scope of the auto numbering
259
- const contentScope = document.querySelector( optionsScope );
260
-
261
- // if scope doesnt exist
262
- // and we are dubugging
263
- if( !contentScope && optionsDebug ) {
264
-
265
- // log the error
266
- return console.error(
267
- 'ERROR: the "scope" entry is not valid'
268
- );
269
-
270
- }
271
-
272
-
273
-
274
- //
275
- // 2. do we have the headers array
276
- //
277
-
278
- if( getHeadingNumber === null ) {
279
-
280
- // log the error
281
- return optionsDebug ? console.error(
282
- 'ERROR: the "start" number is empty or null'
283
- ) : '';
284
-
285
- } else {
286
-
287
- // 2. validate the array is all numeric
288
- if( getHeadingNumber.every( isNaN ) ) {
289
-
290
- // log the error
291
- return optionsDebug ? console.error(
292
- 'ERROR: the values provided are not numeric'
293
- ) : '';
294
-
295
- } else {
296
-
297
- //
298
- // validated constants
299
- //
300
-
301
- let validHeadingNumber = '';
302
-
303
- // get the headings into array
304
- const contentHeaders = contentScope.querySelectorAll(
305
- 'h1, h2, h3, h4, h5, h6'
306
- ),
307
-
308
- // check if the array items are positive numbers
309
- positiveNumber = ( element ) => ( element >= 0 );
310
-
311
- // 3. are the numbers all positive
312
- if( getHeadingNumber.every( positiveNumber ) ) {
313
-
314
- // 4. build the functionality
315
-
316
-
317
- // generate the constants
318
- // -- minus 1 since we add immediately in the loop
319
- const startingNumbers = [
320
- 0, // null
321
- getHeadingNumber[ 0 ] - 1, // h1
322
- getHeadingNumber[ 1 ] - 1, // h2
323
- getHeadingNumber[ 2 ] - 1, // h3
324
- getHeadingNumber[ 3 ] - 1, // h4
325
- getHeadingNumber[ 4 ] - 1, // h5
326
- getHeadingNumber[ 5 ] - 1, // h6
327
- ];
328
-
329
- // track the first run
330
- let firstRun = [
331
- true, // null
332
- true, // h1 run yet
333
- true, // h2 run yet
334
- true, // h3 run yet
335
- true, // h4 run yet
336
- true, // h5 run yet
337
- true // h6 run yet
338
- ];
339
-
340
- // loop through all the elements inside scope
341
- for( var contentItem in contentHeaders ) {
342
-
343
-
344
- // this element from item number
345
- var element = (
346
- contentHeaders[ contentItem ]
347
- ),
348
- numberText = '';
349
-
350
- // limit the heading tag number in search
351
- const headingRegex = new RegExp(
352
- `^H([${ optionsLevelRange }])$`
353
- );
354
-
355
- // does the element match a heading regex
356
- // -- return to beginning of loop
357
- if(
358
- !element ||
359
- !element.tagName ||
360
- !element.tagName.match( headingRegex )
361
- ) {
362
- continue;
363
- }
364
-
365
- // return the heading level number
366
- var elementLevel = RegExp.$1;
367
-
368
- // add `1` to the array numbers
369
- startingNumbers[ elementLevel ]++;
370
-
371
-
372
- // reset all level below except for the first run
373
- if( !firstRun[ elementLevel ] ) {
374
-
375
- // callback
376
- resetBelowLevels( elementLevel );
377
-
378
- }
379
-
380
- // set the first run to false
381
- firstRun[ elementLevel ] = false;
382
-
383
- // loop through the headings
384
- for(
385
- var levelNumber = 1;
386
- levelNumber <= 6;
387
- levelNumber++
388
- ) {
389
-
390
- // if the loop number
391
- // is less than the element number
392
- // then generate the numbering text
393
- if( levelNumber <= elementLevel ) {
394
- numberText += startingNumbers[ levelNumber ] + optionsSeparator
395
-
396
- } else {
397
-
398
- // go back to top
399
- continue;
400
-
401
- }
402
-
403
- }
404
-
405
- // add the number outside the heading
406
- // -- keep the anchor links :)
407
- element.innerHTML = numberText + ' ' + element.innerHTML.replace(/^[0-9\.\s]+/, '' );
408
-
409
- }
410
-
411
- // callback function
412
- function resetBelowLevels( currentLevel ) {
413
-
414
- // currentLevel is string
415
- // convert it to number
416
- for( let i = +currentLevel + 1; i <= 6; i++ ) {
417
- startingNumbers[ i ] = 0;
418
- }
419
- }
420
-
421
- } else {
422
-
423
- // log the error
424
- return optionsDebug ? console.error(
425
- 'ERROR: the values are not positive integers'
426
- ) : '';
427
-
428
- }
429
- }
430
- }
431
- });
432
- }
433
-
434
-
435
- // find heading plugin options
436
- window.$docsify.autoHeaders = Object.assign(
437
- autoHeaderOptions,
438
- window.$docsify.autoHeaders
439
- );
440
- window.$docsify.plugins = [].concat(
441
- autoHeaders,
442
- window.$docsify.plugins
443
- );
@@ -1,7 +0,0 @@
1
- /*
2
- * docsify-autoHeaders.js v4.1.0
3
- * (https://markbattistella.github.io/docsify-autoHeaders/)
4
- * Copyright (c) 2021 Mark Battistella (@markbattistella)
5
- * Licensed under MIT
6
- */
7
- "use strict";function _typeof(e){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var autoHeaderOptions={separator:"",custom:"",levels:"",scope:"",debug:!1};function getAutoHeadersOptions(e){if(!e.separator||!e.levels||!e.scope)return console.error("ERROR: config settings not set");var t="other"==e.separator?e.custom||"_":"";switch(e.separator){case"decimal":t=".";break;case"dash":t="-";break;case"bracket":t=")";break;case"other":break;default:return}return[t,e.levels||6,e.scope||"main",!0===e.debug]}function autoHeaders(e,t){var u,r,f,o,p,d,y;void 0!==getAutoHeadersOptions(autoHeaderOptions)&&(u=null,r=getAutoHeadersOptions(autoHeaderOptions),f=r[0],o=r[1],p=r[2],d=r[3],y=function(e){var t="";if("string"==typeof o)t="H1-".concat(e);else if("object"===_typeof(o)&&null!==o){if(e.start>e.finish)return console.log("ERROR: heading start level cannot be greater than finish level");if(e.start<1||6<e.start||e.finish<1||6<e.finish)return console.log("ERROR: heading levels need to be between 1-6");t="H".concat(e.start,"-").concat(e.finish)}return t}(o),e.beforeEach(function(e){if("@autoHeader:"===e.slice(0,12)){var t=e.split("\n")[0];return u=!(u=t.split(":")[1])||null==u||""==u||6<(u=u.split(f)).length?null:u.concat(new Array(6).fill(0)).slice(0,6).map(function(e){return+e}),e.replace(t,"")}u=null}),e.doneEach(function(){var e=document.querySelector(p);if(!e&&d)return console.error('ERROR: the "scope" entry is not valid');if(null===u)return d?console.error('ERROR: the "start" number is empty or null'):"";if(u.every(isNaN))return d?console.error("ERROR: the values provided are not numeric"):"";var t=e.querySelectorAll("h1, h2, h3, h4, h5, h6");if(!u.every(function(e){return 0<=e}))return d?console.error("ERROR: the values are not positive integers"):"";var r,o=[0,u[0]-1,u[1]-1,u[2]-1,u[3]-1,u[4]-1,u[5]-1],n=[!0,!0,!0,!0,!0,!0,!0];for(r in t){var a=t[r],s="",i=new RegExp("^H([".concat(y,"])$"));if(a&&a.tagName&&a.tagName.match(i)){var c=RegExp.$1;o[c]++,n[c]||function(e){for(var t=+e+1;t<=6;t++)o[t]=0}(c),n[c]=!1;for(var l=1;l<=6;l++)l<=c&&(s+=o[l]+f);a.innerHTML=s+" "+a.innerHTML.replace(/^[0-9\.\s]+/,"")}}}))}window.$docsify.autoHeaders=Object.assign(autoHeaderOptions,window.$docsify.autoHeaders),window.$docsify.plugins=[].concat(autoHeaders,window.$docsify.plugins);