@markbattistella/docsify-autoheaders 4.0.0 → 5.0.0
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.
- package/.browserslistrc +4 -0
- package/CHANGELOG.md +12 -0
- package/README.md +62 -18
- package/dist/docsify-auto-headers.js +579 -0
- package/dist/docsify-auto-headers.min.js +2 -0
- package/package.json +45 -23
- package/.gitattributes +0 -7
- package/dist/docsify-autoHeaders.js +0 -313
- package/dist/docsify-autoHeaders.min.js +0 -6
package/.browserslistrc
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
## 5.0.0 - Tue Jun 11 2024
|
|
3
|
+
|
|
4
|
+
### Breaking Changes
|
|
5
|
+
|
|
6
|
+
- Bumped to new major version
|
|
7
|
+
- Fixing the documentation
|
|
8
|
+
- Added new functionality for `@autoHeaders:` and `<!-- autoHeaders: -->` signifiers
|
|
9
|
+
- Allows numbering in sidebar and main body
|
|
10
|
+
- Better handling of debugging
|
|
11
|
+
- Numbering can use letters and not just numbers
|
|
12
|
+
- Many more fatures and cleanups
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
|
|
3
|
-
# docsify-
|
|
3
|
+
# docsify-auto-headers
|
|
4
4
|
|
|
5
5
|
   
|
|
6
6
|
|
|
@@ -9,8 +9,6 @@
|
|
|
9
9
|
[](https://www.paypal.me/markbattistella/6AUD)
|
|
10
10
|
[](https://www.buymeacoffee.com/markbattistella)
|
|
11
11
|
|
|
12
|
-
[](https://markbattistella.github.io/docsify-autoHeaders/)
|
|
13
|
-
|
|
14
12
|
</div>
|
|
15
13
|
|
|
16
14
|
---
|
|
@@ -44,18 +42,19 @@ Assuming you have a working [docsify](https://docsify.js.org/) framework set up,
|
|
|
44
42
|
|
|
45
43
|
1. In docsify setup configure the plugin (see [configuration](#configuration) for setup):
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
```js
|
|
46
|
+
<script>
|
|
47
|
+
window.$docsify = {
|
|
48
|
+
autoHeaders: {
|
|
49
|
+
separator: String, // how numbers should be separated
|
|
50
|
+
custom: String, // if `separator` is set to other then specify own here
|
|
51
|
+
levels: String | Object, // heading levels h[1-6]
|
|
52
|
+
scope: String, // plugin search scope
|
|
53
|
+
debug: Boolean // show console.log messages
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
</script>
|
|
57
|
+
```
|
|
59
58
|
|
|
60
59
|
### npm install
|
|
61
60
|
|
|
@@ -71,8 +70,10 @@ There are some options available for the `docsify-autoHeaders`:
|
|
|
71
70
|
|
|
72
71
|
| setting | options |
|
|
73
72
|
|-----------|-----------------------------------------------------------------|
|
|
74
|
-
| separator | how the numbers are separated
|
|
75
|
-
|
|
|
73
|
+
| separator | how the numbers are separated: `decimal`, `dash`, `bracket`, or `other` |
|
|
74
|
+
| custom | if `separator` is set to `other` then you can specify the custom styled separator here |
|
|
75
|
+
| levels | String: heading levels to target `1-6` |
|
|
76
|
+
| | Object: start and finish for custom range |
|
|
76
77
|
| scope | the element to narrow it down to. `#main` is the default scope |
|
|
77
78
|
| debug | `true` or `false` if you want to see `console.log` info |
|
|
78
79
|
|
|
@@ -106,7 +107,50 @@ You can also manually set the starting number of each of the levels by using the
|
|
|
106
107
|
|
|
107
108
|
Respectively starting the first level 6 heading (H6) at:
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
```md
|
|
111
|
+
3.5.6.6.2.1 New heading
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Start and finish range
|
|
115
|
+
|
|
116
|
+
You can also target specific heading levels for the numbering which works well if you want to skip H1.
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
<script>
|
|
120
|
+
window.$docsify = {
|
|
121
|
+
autoHeaders: {
|
|
122
|
+
separator: 'other',
|
|
123
|
+
custom: '--',
|
|
124
|
+
levels: {
|
|
125
|
+
start: '2',
|
|
126
|
+
finish: '4'
|
|
127
|
+
},
|
|
128
|
+
scope: '#main',
|
|
129
|
+
debug: false
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
</script>
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
```md
|
|
136
|
+
@autoheaders:1.2.3.4.5.6
|
|
137
|
+
|
|
138
|
+
# Level 1 heading
|
|
139
|
+
|
|
140
|
+
## Level 2 heading
|
|
141
|
+
|
|
142
|
+
### Level 3 heading
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```md
|
|
146
|
+
Level 1 heading
|
|
147
|
+
|
|
148
|
+
1-- Level 2 heading
|
|
149
|
+
|
|
150
|
+
1--2-- Level 3 heading
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
!> **Note:** though it skips H1, the numbering starts at the first integer from the `@autoheaders:1.2.3.4.5.6` data. The above example should be read as `@autoheaders:1.2.3`
|
|
110
154
|
|
|
111
155
|
## Contributing
|
|
112
156
|
|
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/*! docsify-auto-headers 5.0.0 | (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.0 | (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,25 +1,47 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
2
|
+
"name": "@markbattistella/docsify-autoheaders",
|
|
3
|
+
"version": "5.0.0",
|
|
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"
|
|
25
47
|
}
|
package/.gitattributes
DELETED
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
/*! docsify-autoHeaders.js v4.0.0 | (c) Mark Battistella */
|
|
2
|
-
'use strict';
|
|
3
|
-
|
|
4
|
-
function getHeading( headingOptions ) {
|
|
5
|
-
|
|
6
|
-
// if it is empty
|
|
7
|
-
if(
|
|
8
|
-
!headingOptions.separator &&
|
|
9
|
-
!headingOptions.levels &&
|
|
10
|
-
!headingOptions.scope
|
|
11
|
-
) {
|
|
12
|
-
return 'No config set'
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// blank separator
|
|
16
|
-
var separator = '';
|
|
17
|
-
|
|
18
|
-
// output the correct from words
|
|
19
|
-
switch (headingOptions.separator) {
|
|
20
|
-
case 'decimal':
|
|
21
|
-
separator = '.'
|
|
22
|
-
break;
|
|
23
|
-
case 'dash':
|
|
24
|
-
separator = '-'
|
|
25
|
-
break;
|
|
26
|
-
case 'bracket':
|
|
27
|
-
separator = ')'
|
|
28
|
-
break;
|
|
29
|
-
default:
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// get othe settings
|
|
34
|
-
let levels = headingOptions.levels ? headingOptions.levels : 6;
|
|
35
|
-
let scope = headingOptions.scope ? headingOptions.scope : "main";
|
|
36
|
-
let debug = headingOptions.debug == true ? 1 : 0;
|
|
37
|
-
|
|
38
|
-
// return the array
|
|
39
|
-
return [ separator, levels, scope, debug ];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// defaults - and setup
|
|
43
|
-
const headingOptions = {
|
|
44
|
-
separator: '',
|
|
45
|
-
levels: '',
|
|
46
|
-
scope: ''
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
// the function
|
|
51
|
-
function autoHeaders( hook, vm ) {
|
|
52
|
-
|
|
53
|
-
// make the variables global
|
|
54
|
-
var getHeadingNumber = null;
|
|
55
|
-
|
|
56
|
-
// get the variables from the config
|
|
57
|
-
const headingOptionsArray = getHeading( headingOptions ),
|
|
58
|
-
|
|
59
|
-
// create variables
|
|
60
|
-
arraySeparator = headingOptionsArray[0],
|
|
61
|
-
arrayLevel = headingOptionsArray[1],
|
|
62
|
-
arrayScope = headingOptionsArray[2],
|
|
63
|
-
arrayDebug = headingOptionsArray[3];
|
|
64
|
-
|
|
65
|
-
// reset counter
|
|
66
|
-
function resetBelowLevels( currentLevel ) {
|
|
67
|
-
// currentLevel is string so need to convert it to number
|
|
68
|
-
for( let i = +currentLevel + 1; i <= 6; i++ ) {
|
|
69
|
-
startingNumbers[i] = 0;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
// MARK: - check if the document starts with the signifier
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
// get the `@autoHeader:` data
|
|
78
|
-
hook.beforeEach( function( content ) {
|
|
79
|
-
|
|
80
|
-
// get the first 12 characters
|
|
81
|
-
const getFirstCharacters = content.slice( 0, 12 );
|
|
82
|
-
|
|
83
|
-
// check if beginning with the plugin key
|
|
84
|
-
if( getFirstCharacters === "@autoHeader:" ) {
|
|
85
|
-
|
|
86
|
-
// get the first line of data
|
|
87
|
-
const getFirstLine = content.split( "\n" )[0];
|
|
88
|
-
|
|
89
|
-
// get everything after the `:`
|
|
90
|
-
getHeadingNumber = getFirstLine.split( ":" )[1];
|
|
91
|
-
|
|
92
|
-
// there is no data to continue
|
|
93
|
-
if(
|
|
94
|
-
!getHeadingNumber ||
|
|
95
|
-
getHeadingNumber == null ||
|
|
96
|
-
getHeadingNumber == ''
|
|
97
|
-
) {
|
|
98
|
-
|
|
99
|
-
// set the headerNumber to null
|
|
100
|
-
getHeadingNumber = null;
|
|
101
|
-
|
|
102
|
-
// transform the data
|
|
103
|
-
} else {
|
|
104
|
-
|
|
105
|
-
// make an array from the separator
|
|
106
|
-
getHeadingNumber = getHeadingNumber.split( arraySeparator );
|
|
107
|
-
|
|
108
|
-
// dont work with too many items in the array
|
|
109
|
-
if( getHeadingNumber.length > 6 ) {
|
|
110
|
-
|
|
111
|
-
// set the headerNumber to null
|
|
112
|
-
getHeadingNumber = null;
|
|
113
|
-
|
|
114
|
-
} else {
|
|
115
|
-
|
|
116
|
-
// pad in the extra array items
|
|
117
|
-
getHeadingNumber = getHeadingNumber.concat(
|
|
118
|
-
new Array(6) // add a new array upto 6 items
|
|
119
|
-
.fill(0) ) // fill it with zeros
|
|
120
|
-
.slice(0, 6) // cut off after 6 items
|
|
121
|
-
.map( x => +x ); // map the Strings to Int
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// remove the line
|
|
126
|
-
var cleanedContent = content.replace( getFirstLine, '' );
|
|
127
|
-
|
|
128
|
-
// return the cleaned content
|
|
129
|
-
return cleanedContent;
|
|
130
|
-
|
|
131
|
-
} else {
|
|
132
|
-
|
|
133
|
-
// set the headerNumber to null
|
|
134
|
-
getHeadingNumber = null;
|
|
135
|
-
}
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
//
|
|
140
|
-
// MARK: - add the heading numbers
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
hook.doneEach( function() {
|
|
144
|
-
|
|
145
|
-
// set the scope of the automisation
|
|
146
|
-
const contentScope = document.querySelector( arrayScope );
|
|
147
|
-
|
|
148
|
-
// if the scope is empty
|
|
149
|
-
if( !contentScope && arrayDebug ) {
|
|
150
|
-
console.log( '--- autoHeaders error: start ---' + '\n' +
|
|
151
|
-
'The "scope" entry is not valid :(' + '\n' +
|
|
152
|
-
'--- autoHeaders error: end ---'
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
// exit out
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// MARK: - are we running autoHeaders
|
|
160
|
-
if( getHeadingNumber == null ) {
|
|
161
|
-
|
|
162
|
-
// if we're debugging
|
|
163
|
-
if( arrayDebug ) {
|
|
164
|
-
console.log( '--- autoHeaders error: start ---' + '\n\n' +
|
|
165
|
-
'The "start" number is empty or null' + '\n' +
|
|
166
|
-
'Logged value: "' + getHeadingNumber + '" \n\n' +
|
|
167
|
-
'--- autoHeaders error: end ---'
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// no errors
|
|
172
|
-
} else {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
// check if all the items in the array are numeric
|
|
176
|
-
if( !getHeadingNumber.every( isNaN ) ) {
|
|
177
|
-
|
|
178
|
-
// set the variable up
|
|
179
|
-
var validHeadingNumber = '';
|
|
180
|
-
|
|
181
|
-
// get the headings into array
|
|
182
|
-
const contentHeaders = contentScope.querySelectorAll(
|
|
183
|
-
'h1, h2, h3, h4, h5, h6'
|
|
184
|
-
),
|
|
185
|
-
|
|
186
|
-
// check if the array items are positive numbers
|
|
187
|
-
positiveNumber = (element) => ( element >= 0 ),
|
|
188
|
-
|
|
189
|
-
// rount the numbers down
|
|
190
|
-
roundDown = (element) => Math.floor( element );
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// are the numbers all positive
|
|
194
|
-
if( getHeadingNumber.every( positiveNumber ) ) {
|
|
195
|
-
|
|
196
|
-
// set the starting values
|
|
197
|
-
// -- minus 1 since we add immediately in the loop
|
|
198
|
-
const startingNumbers = [ 0, // null
|
|
199
|
-
getHeadingNumber[0] - 1, // h1
|
|
200
|
-
getHeadingNumber[1] - 1, // h2
|
|
201
|
-
getHeadingNumber[2] - 1, // h3
|
|
202
|
-
getHeadingNumber[3] - 1, // h4
|
|
203
|
-
getHeadingNumber[4] - 1, // h5
|
|
204
|
-
getHeadingNumber[5] - 1, // h6
|
|
205
|
-
];
|
|
206
|
-
|
|
207
|
-
// track the first run
|
|
208
|
-
let firstRun = [ true, // null
|
|
209
|
-
true, // h1 run yet
|
|
210
|
-
true, // h2 run yet
|
|
211
|
-
true, // h3 run yet
|
|
212
|
-
true, // h4 run yet
|
|
213
|
-
true, // h5 run yet
|
|
214
|
-
true // h6 run yet
|
|
215
|
-
];
|
|
216
|
-
|
|
217
|
-
// loop through all the elements inside scope
|
|
218
|
-
for( var item in contentHeaders ) {
|
|
219
|
-
|
|
220
|
-
// this element from item number
|
|
221
|
-
var element = contentHeaders[item],
|
|
222
|
-
numberText = '';
|
|
223
|
-
|
|
224
|
-
// limit the heading tag number in search
|
|
225
|
-
const headingRegex = new RegExp( '^H([1-' +
|
|
226
|
-
arrayLevel +
|
|
227
|
-
'])$'
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
// does the element match a heading regex
|
|
231
|
-
if( !element ||
|
|
232
|
-
!element.tagName ||
|
|
233
|
-
!element.tagName.match( headingRegex )
|
|
234
|
-
) {
|
|
235
|
-
|
|
236
|
-
// return to beginning of loop
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// return the heading level number
|
|
241
|
-
var elementLevel = RegExp.$1;
|
|
242
|
-
|
|
243
|
-
// add `1` to the array numbers
|
|
244
|
-
startingNumbers[elementLevel]++;
|
|
245
|
-
|
|
246
|
-
// reset all level below except for the first run
|
|
247
|
-
if( !firstRun[ elementLevel ] ) {
|
|
248
|
-
resetBelowLevels( elementLevel );
|
|
249
|
-
firstRun[ elementLevel ] = false;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// loop through the headings
|
|
253
|
-
for( var levelNumber = 1;
|
|
254
|
-
levelNumber <= 6;
|
|
255
|
-
levelNumber++
|
|
256
|
-
) {
|
|
257
|
-
|
|
258
|
-
// if the number is lt the element number
|
|
259
|
-
if( levelNumber <= elementLevel ) {
|
|
260
|
-
numberText += startingNumbers[levelNumber] + arraySeparator
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// add the number outside the heading
|
|
266
|
-
// -- keep the anchor links :)
|
|
267
|
-
element.innerHTML = numberText + ' ' + element.innerHTML.replace(/^[0-9\.\s]+/,'' );
|
|
268
|
-
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
} else {
|
|
272
|
-
|
|
273
|
-
// if we're debugging
|
|
274
|
-
if( arrayDebug ) {
|
|
275
|
-
console.log( '--- autoHeaders error: start ---' + '\n\n' +
|
|
276
|
-
'The numbers are not positive' + '\n' +
|
|
277
|
-
'Value: "' + getHeadingNumber + '" \n\n' +
|
|
278
|
-
'--- autoHeaders error: end ---'
|
|
279
|
-
);
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// exit out
|
|
283
|
-
return;
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
// one of the items in the array is not numeric
|
|
288
|
-
} else {
|
|
289
|
-
|
|
290
|
-
// if we're debugging
|
|
291
|
-
if( arrayDebug ) {
|
|
292
|
-
console.log( '--- autoHeaders error: start ---' + '\n\n' +
|
|
293
|
-
'The "start" number is not numeric' + '\n' +
|
|
294
|
-
'Value: "' + getHeadingNumber + '" \n\n' +
|
|
295
|
-
'--- autoHeaders error: end ---'
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// exit out
|
|
300
|
-
return;
|
|
301
|
-
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
// find heading plugin options
|
|
309
|
-
window.$docsify.autoHeaders = Object.assign(
|
|
310
|
-
headingOptions,
|
|
311
|
-
window.$docsify.autoHeaders
|
|
312
|
-
);
|
|
313
|
-
window.$docsify.plugins = [].concat(autoHeaders, window.$docsify.plugins);
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
docsify-autoHeaders.js v4.0.0 | (c) Mark Battistella */
|
|
3
|
-
function getHeading(c){if(!c.separator&&!c.levels&&!c.scope)return"No config set";switch(c.separator){case "decimal":var g=".";break;case "dash":g="-";break;case "bracket":g=")";break;default:return}return[g,c.levels?c.levels:6,c.scope?c.scope:"main",1==c.debug?1:0]}var headingOptions={separator:"",levels:"",scope:""};
|
|
4
|
-
function autoHeaders(c,g){function r(b){for(b=+b+1;6>=b;b++)startingNumbers[b]=0}var a=null,h=getHeading(headingOptions),n=h[0],t=h[1],u=h[2],k=h[3];c.beforeEach(function(b){if("@autoHeader:"===b.slice(0,12)){var e=b.split("\n")[0];(a=e.split(":")[1])&&null!=a&&""!=a?(a=a.split(n),a=6<a.length?null:a.concat(Array(6).fill(0)).slice(0,6).map(function(l){return+l})):a=null;return b.replace(e,"")}a=null});c.doneEach(function(){var b=document.querySelector(u);if(!b&&k)console.log('--- autoHeaders error: start ---\nThe "scope" entry is not valid :(\n--- autoHeaders error: end ---');
|
|
5
|
-
else if(null==a)k&&console.log('--- autoHeaders error: start ---\n\nThe "start" number is empty or null\nLogged value: "'+a+'" \n\n--- autoHeaders error: end ---');else if(a.every(isNaN))k&&console.log('--- autoHeaders error: start ---\n\nThe "start" number is not numeric\nValue: "'+a+'" \n\n--- autoHeaders error: end ---');else if(b=b.querySelectorAll("h1, h2, h3, h4, h5, h6"),a.every(function(v){return 0<=v})){var e=[0,a[0]-1,a[1]-1,a[2]-1,a[3]-1,a[4]-1,a[5]-1],l=[!0,!0,!0,!0,!0,!0,!0],p;for(p in b){var f=
|
|
6
|
-
b[p],q="",d=new RegExp("^H([1-"+t+"])$");if(f&&f.tagName&&f.tagName.match(d)){d=RegExp.$1;e[d]++;l[d]||(r(d),l[d]=!1);for(var m=1;6>=m;m++)m<=d&&(q+=e[m]+n);f.innerHTML=q+" "+f.innerHTML.replace(/^[0-9\.\s]+/,"")}}}else k&&console.log('--- autoHeaders error: start ---\n\nThe numbers are not positive\nValue: "'+a+'" \n\n--- autoHeaders error: end ---')})}window.$docsify.autoHeaders=Object.assign(headingOptions,window.$docsify.autoHeaders);window.$docsify.plugins=[].concat(autoHeaders,window.$docsify.plugins);
|