@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.
@@ -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,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-autoHeaders
3
+ # docsify-auto-headers
4
4
 
5
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
6
 
@@ -9,8 +9,6 @@
9
9
  [![](https://img.shields.io/badge/%20-@markbattistella-blue?logo=paypal&style=for-the-badge)](https://www.paypal.me/markbattistella/6AUD)
10
10
  [![](https://img.shields.io/badge/%20-buymeacoffee-black?logo=buy-me-a-coffee&style=for-the-badge)](https://www.buymeacoffee.com/markbattistella)
11
11
 
12
- [![](https://img.shields.io/badge/demo-@markbattistella/docsify--autoHeaders-1E5749?style=for-the-badge)](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
- ```js
48
- <script>
49
- window.$docsify = {
50
- 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
- ```
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 - `decimal`, `dash`, or `bracket` |
75
- | levels | heading levels to target `1-6` |
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
- 3.5.6.6.2.1 New heading
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
- "name": "@markbattistella/docsify-autoheaders",
3
- "version": "4.0.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
- }
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,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,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);