spec-up-t 1.3.0 → 1.4.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/.github/copilot-instructions.md +13 -0
- package/assets/compiled/body.js +18 -12
- package/assets/compiled/head.css +8 -6
- package/assets/css/collapse-definitions.css +0 -1
- package/assets/css/counter.css +10 -22
- package/assets/css/create-pdf.css +4 -2
- package/assets/css/create-term-filter.css +4 -4
- package/assets/css/definition-buttons-container.css +60 -0
- package/assets/css/{pdf-download.css → download-pdf-docx.css} +9 -5
- package/assets/css/insert-trefs.css +7 -0
- package/assets/css/sidebar-toc.css +2 -1
- package/assets/css/terms-and-definitions.css +73 -22
- package/assets/js/add-href-to-snapshot-link.js +16 -9
- package/assets/js/addAnchorsToTerms.js +2 -2
- package/assets/js/charts.js +10 -0
- package/assets/js/collapse-definitions.js +13 -2
- package/assets/js/collapse-meta-info.js +11 -9
- package/assets/js/definition-button-container-utils.js +82 -0
- package/assets/js/download-pdf-docx.js +68 -0
- package/assets/js/edit-term-buttons.js +77 -20
- package/assets/js/github-issues.js +35 -0
- package/assets/js/github-repo-info.js +144 -0
- package/assets/js/highlight-heading-plus-sibling-nodes.test.js +18 -0
- package/assets/js/insert-trefs.js +62 -13
- package/assets/js/mermaid-diagrams.js +11 -0
- package/assets/js/terminology-section-utility-container/README.md +107 -0
- package/assets/js/terminology-section-utility-container/create-alphabet-index.js +17 -0
- package/assets/js/{create-term-filter.js → terminology-section-utility-container/create-term-filter.js} +11 -44
- package/assets/js/terminology-section-utility-container/hide-show-utility-container.js +21 -0
- package/assets/js/terminology-section-utility-container/search.js +203 -0
- package/assets/js/terminology-section-utility-container.js +203 -0
- package/assets/js/tooltips.js +283 -0
- package/config/asset-map.json +26 -18
- package/index.js +57 -390
- package/package.json +5 -2
- package/src/add-remove-xref-source.js +20 -21
- package/src/collect-external-references.js +8 -337
- package/src/collect-external-references.test.js +440 -33
- package/src/configure.js +8 -109
- package/src/create-docx.js +7 -6
- package/src/create-pdf.js +15 -14
- package/src/freeze-spec-data.js +46 -0
- package/src/git-info.test.js +76 -0
- package/src/health-check/destination-gitignore-checker.js +5 -3
- package/src/health-check/external-specs-checker.js +5 -4
- package/src/health-check/specs-configuration-checker.js +2 -1
- package/src/health-check/term-references-checker.js +5 -3
- package/src/health-check/terms-intro-checker.js +2 -1
- package/src/health-check/tref-term-checker.js +8 -7
- package/src/health-check.js +8 -7
- package/src/init.js +3 -2
- package/src/install-from-boilerplate/add-gitignore-entries.js +3 -2
- package/src/install-from-boilerplate/add-scripts-keys.js +5 -4
- package/src/install-from-boilerplate/boilerplate/.github/workflows/menu.yml +74 -97
- package/src/install-from-boilerplate/boilerplate/README.md +1 -1
- package/src/install-from-boilerplate/boilerplate/spec/example-markup-in-markdown.md +1 -1
- package/src/install-from-boilerplate/boilerplate/spec/spec-head.md +2 -2
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/composability.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/compost.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/fertilizer.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/mulch.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/pruning.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/seedling.md +3 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/soil.md +11 -0
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/watering.md +3 -0
- package/src/install-from-boilerplate/boilerplate/specs.json +24 -10
- package/src/install-from-boilerplate/config-scripts-keys.js +3 -3
- package/src/install-from-boilerplate/config-system-files.js +0 -1
- package/src/install-from-boilerplate/copy-boilerplate.js +2 -1
- package/src/install-from-boilerplate/copy-system-files.js +4 -3
- package/src/install-from-boilerplate/custom-update.js +12 -1
- package/src/install-from-boilerplate/help.txt +1 -1
- package/src/install-from-boilerplate/menu.sh +6 -6
- package/src/json-key-validator.js +17 -11
- package/src/markdown-it/README.md +207 -0
- package/src/markdown-it/definition-lists.js +397 -0
- package/src/markdown-it/index.js +83 -0
- package/src/markdown-it/link-enhancement.js +98 -0
- package/src/markdown-it/plugins.js +118 -0
- package/src/markdown-it/table-enhancement.js +97 -0
- package/src/markdown-it/template-tag-syntax.js +152 -0
- package/src/parsers/index.js +16 -0
- package/src/parsers/spec-parser.js +152 -0
- package/src/parsers/spec-parser.test.js +109 -0
- package/src/parsers/template-tag-parser.js +277 -0
- package/src/parsers/template-tag-parser.test.js +107 -0
- package/src/pipeline/configuration/configure-starterpack.js +200 -0
- package/src/{create-external-specs-list.js → pipeline/configuration/create-external-specs-list.js} +13 -12
- package/src/{create-term-index.js → pipeline/configuration/create-term-index.js} +19 -18
- package/src/{create-versions-index.js → pipeline/configuration/create-versions-index.js} +4 -3
- package/src/{insert-term-index.js → pipeline/configuration/insert-term-index.js} +2 -2
- package/src/pipeline/configuration/prepare-spec-configuration.js +70 -0
- package/src/pipeline/parsing/apply-markdown-it-extensions.js +35 -0
- package/src/pipeline/parsing/create-markdown-parser.js +94 -0
- package/src/pipeline/parsing/create-markdown-parser.test.js +49 -0
- package/src/{html-dom-processor.js → pipeline/postprocessing/definition-list-postprocessor.js} +69 -10
- package/src/{escape-handler.js → pipeline/preprocessing/escape-processor.js} +3 -1
- package/src/{fix-markdown-files.js → pipeline/preprocessing/normalize-terminology-markdown.js} +41 -31
- package/src/pipeline/references/collect-external-references.js +307 -0
- package/src/pipeline/references/external-references-service.js +231 -0
- package/src/pipeline/references/fetch-terms-from-index.js +198 -0
- package/src/pipeline/references/match-term.js +34 -0
- package/src/{collectExternalReferences/matchTerm.test.js → pipeline/references/match-term.test.js} +8 -2
- package/src/pipeline/references/process-xtrefs-data.js +94 -0
- package/src/pipeline/references/xtref-utils.js +166 -0
- package/src/pipeline/rendering/render-spec-document.js +146 -0
- package/src/pipeline/rendering/render-utils.js +154 -0
- package/src/utils/LOGGER.md +81 -0
- package/src/utils/{doesUrlExist.js → does-url-exist.js} +4 -3
- package/src/utils/fetch.js +5 -4
- package/src/utils/file-opener.js +3 -2
- package/src/utils/git-info.js +77 -0
- package/src/utils/logger.js +74 -0
- package/src/utils/regex-patterns.js +471 -0
- package/src/utils/regex-patterns.test.js +281 -0
- package/templates/template.html +56 -21
- package/assets/js/create-alphabet-index.js +0 -60
- package/assets/js/hide-show-utility-container.js +0 -16
- package/assets/js/index.js +0 -87
- package/assets/js/pdf-download.js +0 -46
- package/assets/js/search.js +0 -365
- package/src/collectExternalReferences/fetchTermsFromIndex.js +0 -284
- package/src/collectExternalReferences/matchTerm.js +0 -32
- package/src/collectExternalReferences/processXTrefsData.js +0 -108
- package/src/freeze.js +0 -90
- package/src/install-from-boilerplate/boilerplate/.github/workflows/fetch-and-push-xrefs.yml.old +0 -42
- package/src/install-from-boilerplate/boilerplate/.github/workflows/render-specs.yml +0 -47
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-1.md +0 -13
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-2.md +0 -3
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-3.md +0 -3
- package/src/install-from-boilerplate/boilerplate/spec/terms-definitions/term-4.md +0 -3
- package/src/markdown-it-extensions.js +0 -395
- package/src/references.js +0 -114
- /package/assets/css/{bootstrap.min.css → embedded-libraries/bootstrap.min.css} +0 -0
- /package/assets/css/{prism.css → embedded-libraries/prism.css} +0 -0
- /package/assets/css/{prism.dark.css → embedded-libraries/prism.dark.css} +0 -0
- /package/assets/css/{prism.default.css → embedded-libraries/prism.default.css} +0 -0
- /package/assets/js/{bootstrap.bundle.min.js → embedded-libraries/bootstrap.bundle.min.js} +0 -0
- /package/assets/js/{chart.js → embedded-libraries/chart.js} +0 -0
- /package/assets/js/{diff.min.js → embedded-libraries/diff.min.js} +0 -0
- /package/assets/js/{font-awesome.js → embedded-libraries/font-awesome.js} +0 -0
- /package/assets/js/{mermaid.js → embedded-libraries/mermaid.js} +0 -0
- /package/assets/js/{notyf.js → embedded-libraries/notyf.js} +0 -0
- /package/assets/js/{popper.js → embedded-libraries/popper.js} +0 -0
- /package/assets/js/{prism.dark.js → embedded-libraries/prism.dark.js} +0 -0
- /package/assets/js/{prism.default.js → embedded-libraries/prism.default.js} +0 -0
- /package/assets/js/{prism.js → embedded-libraries/prism.js} +0 -0
- /package/assets/js/{tippy.js → embedded-libraries/tippy.js} +0 -0
- /package/src/{escape-mechanism.js → pipeline/preprocessing/escape-placeholder-utils.js} +0 -0
- /package/src/utils/{isLineWithDefinition.js → is-line-with-definition.js} +0 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Repository Information Utility
|
|
3
|
+
*
|
|
4
|
+
* This client-side utility provides easy access to the GitHub repository
|
|
5
|
+
* information embedded in the HTML meta tag by Spec-Up-T.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* const repoInfo = getGithubRepoInfo();
|
|
9
|
+
* if (repoInfo) {
|
|
10
|
+
* console.log(`Repository: ${repoInfo.username}/${repoInfo.repo} on ${repoInfo.branch}`);
|
|
11
|
+
* }
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Extracts GitHub repository information from the meta tag
|
|
16
|
+
* @returns {Object|null} Repository information object or null if not found
|
|
17
|
+
* @returns {string} returns.username - GitHub username/account
|
|
18
|
+
* @returns {string} returns.repo - Repository name
|
|
19
|
+
* @returns {string} returns.branch - Git branch name
|
|
20
|
+
*/
|
|
21
|
+
function getGithubRepoInfo() {
|
|
22
|
+
try {
|
|
23
|
+
const metaTag = document.querySelector('meta[property="spec-up-t:github-repo-info"]');
|
|
24
|
+
if (!metaTag) {
|
|
25
|
+
console.warn('GitHub repository meta tag not found');
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const content = metaTag.getAttribute('content');
|
|
30
|
+
if (!content) {
|
|
31
|
+
console.warn('GitHub repository meta tag has no content');
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const [username, repo, branch] = content.split(',');
|
|
36
|
+
if (!username || !repo || !branch) {
|
|
37
|
+
console.warn('Invalid GitHub repository meta tag format');
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
username: username.trim(),
|
|
43
|
+
repo: repo.trim(),
|
|
44
|
+
branch: branch.trim()
|
|
45
|
+
};
|
|
46
|
+
} catch (error) {
|
|
47
|
+
console.error('Error extracting GitHub repository information:', error);
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Creates a GitHub URL from the repository information
|
|
54
|
+
* @param {string} path - Optional path within the repository (e.g., 'issues', 'blob/main/README.md')
|
|
55
|
+
* @returns {string|null} GitHub URL or null if repository info not available
|
|
56
|
+
*/
|
|
57
|
+
function getGithubUrl(path = '') {
|
|
58
|
+
const repoInfo = getGithubRepoInfo();
|
|
59
|
+
if (!repoInfo) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const baseUrl = `https://github.com/${repoInfo.username}/${repoInfo.repo}`;
|
|
64
|
+
if (path) {
|
|
65
|
+
return `${baseUrl}/${path}`;
|
|
66
|
+
}
|
|
67
|
+
return baseUrl;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Gets the GitHub URL for the current branch
|
|
72
|
+
* @returns {string|null} GitHub branch URL or null if repository info not available
|
|
73
|
+
*/
|
|
74
|
+
function getCurrentBranchUrl() {
|
|
75
|
+
const repoInfo = getGithubRepoInfo();
|
|
76
|
+
if (!repoInfo) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return `https://github.com/${repoInfo.username}/${repoInfo.repo}/tree/${repoInfo.branch}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Populates the repository information in the settings menu
|
|
85
|
+
* This function is called when the page loads to display repo info
|
|
86
|
+
*/
|
|
87
|
+
function populateRepoInfoInSettings() {
|
|
88
|
+
const repoInfo = getGithubRepoInfo();
|
|
89
|
+
|
|
90
|
+
// Get DOM elements for repository info display
|
|
91
|
+
const accountElement = document.getElementById('repo-account');
|
|
92
|
+
const nameElement = document.getElementById('repo-name');
|
|
93
|
+
const branchElement = document.getElementById('repo-branch');
|
|
94
|
+
const urlElement = document.getElementById('repo-url');
|
|
95
|
+
|
|
96
|
+
if (!accountElement || !nameElement || !branchElement || !urlElement) {
|
|
97
|
+
console.warn('Repository info elements not found in settings menu');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (repoInfo) {
|
|
102
|
+
// Populate the information
|
|
103
|
+
accountElement.textContent = repoInfo.username;
|
|
104
|
+
nameElement.textContent = repoInfo.repo;
|
|
105
|
+
branchElement.textContent = repoInfo.branch;
|
|
106
|
+
|
|
107
|
+
// Set up the GitHub link
|
|
108
|
+
const repoUrl = getGithubUrl();
|
|
109
|
+
if (repoUrl) {
|
|
110
|
+
urlElement.href = repoUrl;
|
|
111
|
+
urlElement.style.display = 'inline-block';
|
|
112
|
+
} else {
|
|
113
|
+
urlElement.style.display = 'none';
|
|
114
|
+
}
|
|
115
|
+
} else {
|
|
116
|
+
// Handle case where no repository info is available
|
|
117
|
+
accountElement.textContent = 'Not available';
|
|
118
|
+
nameElement.textContent = 'Not available';
|
|
119
|
+
branchElement.textContent = 'Not available';
|
|
120
|
+
urlElement.style.display = 'none';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Initialize repository info when DOM is ready
|
|
125
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
126
|
+
populateRepoInfoInSettings();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Make functions available globally if not using modules
|
|
130
|
+
if (typeof window !== 'undefined') {
|
|
131
|
+
window.getGithubRepoInfo = getGithubRepoInfo;
|
|
132
|
+
window.getGithubUrl = getGithubUrl;
|
|
133
|
+
window.getCurrentBranchUrl = getCurrentBranchUrl;
|
|
134
|
+
window.populateRepoInfoInSettings = populateRepoInfoInSettings;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Export for module usage
|
|
138
|
+
if (typeof module !== 'undefined' && module.exports) {
|
|
139
|
+
module.exports = {
|
|
140
|
+
getGithubRepoInfo,
|
|
141
|
+
getGithubUrl,
|
|
142
|
+
getCurrentBranchUrl
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -14,6 +14,7 @@ const {
|
|
|
14
14
|
initializeAnchorHighlighting
|
|
15
15
|
} = require('./highlight-heading-plus-sibling-nodes');
|
|
16
16
|
|
|
17
|
+
// Tests for highlighting heading sections and their related content
|
|
17
18
|
describe('highlight-heading-plus-sibling-nodes', () => {
|
|
18
19
|
let container;
|
|
19
20
|
|
|
@@ -36,11 +37,14 @@ describe('highlight-heading-plus-sibling-nodes', () => {
|
|
|
36
37
|
document.body.innerHTML = '';
|
|
37
38
|
});
|
|
38
39
|
|
|
40
|
+
// Tests for determining heading levels (h2-h6)
|
|
39
41
|
describe('getHeadingLevel', () => {
|
|
42
|
+
// Test: Can the system correctly identify heading levels?
|
|
40
43
|
it('returns correct level for h2-h6', () => {
|
|
41
44
|
expect(getHeadingLevel(document.getElementById('h2-1'))).toBe(2);
|
|
42
45
|
expect(getHeadingLevel(document.getElementById('h3-1'))).toBe(3);
|
|
43
46
|
});
|
|
47
|
+
// Test: Does the system properly reject invalid headings?
|
|
44
48
|
it('returns null for h1 or non-heading', () => {
|
|
45
49
|
const h1 = document.createElement('h1');
|
|
46
50
|
expect(getHeadingLevel(h1)).toBeNull();
|
|
@@ -48,12 +52,15 @@ describe('highlight-heading-plus-sibling-nodes', () => {
|
|
|
48
52
|
});
|
|
49
53
|
});
|
|
50
54
|
|
|
55
|
+
// Tests for collecting heading sections (heading + related content)
|
|
51
56
|
describe('collectHeadingSiblings', () => {
|
|
57
|
+
// Test: Can the system collect all content belonging to a heading section?
|
|
52
58
|
it('collects heading and following siblings until next heading of same or higher level', () => {
|
|
53
59
|
const h2 = document.getElementById('h2-1');
|
|
54
60
|
const siblings = collectHeadingSiblings(h2, 2);
|
|
55
61
|
expect(siblings.map(n => n.id)).toEqual(['h2-1', 'p1', 'ul1', 'h3-1', 'p2']);
|
|
56
62
|
});
|
|
63
|
+
// Test: Does the system stop collecting at the right boundary?
|
|
57
64
|
it('stops at next heading of same or higher level', () => {
|
|
58
65
|
const h3 = document.getElementById('h3-1');
|
|
59
66
|
const siblings = collectHeadingSiblings(h3, 3);
|
|
@@ -61,7 +68,9 @@ describe('highlight-heading-plus-sibling-nodes', () => {
|
|
|
61
68
|
});
|
|
62
69
|
});
|
|
63
70
|
|
|
71
|
+
// Tests for wrapping content with highlight styling
|
|
64
72
|
describe('wrapNodesWithHighlight', () => {
|
|
73
|
+
// Test: Can the system wrap selected content with highlight styling?
|
|
65
74
|
it('wraps nodes in a div.highlight2', () => {
|
|
66
75
|
const nodes = [document.getElementById('h2-1'), document.getElementById('p1')];
|
|
67
76
|
const wrapper = wrapNodesWithHighlight(nodes);
|
|
@@ -70,12 +79,15 @@ describe('highlight-heading-plus-sibling-nodes', () => {
|
|
|
70
79
|
expect(wrapper.contains(nodes[0])).toBe(true);
|
|
71
80
|
expect(wrapper.contains(nodes[1])).toBe(true);
|
|
72
81
|
});
|
|
82
|
+
// Test: Does the system handle empty input gracefully?
|
|
73
83
|
it('returns null if nodes is empty', () => {
|
|
74
84
|
expect(wrapNodesWithHighlight([])).toBeNull();
|
|
75
85
|
});
|
|
76
86
|
});
|
|
77
87
|
|
|
88
|
+
// Tests for cleaning up existing highlights
|
|
78
89
|
describe('removeExistingHighlights', () => {
|
|
90
|
+
// Test: Can the system remove highlights and restore original content structure?
|
|
79
91
|
it('removes all .highlight2 wrappers and restores children', () => {
|
|
80
92
|
const nodes = [document.getElementById('h2-1'), document.getElementById('p1')];
|
|
81
93
|
wrapNodesWithHighlight(nodes);
|
|
@@ -89,24 +101,30 @@ describe('highlight-heading-plus-sibling-nodes', () => {
|
|
|
89
101
|
});
|
|
90
102
|
});
|
|
91
103
|
|
|
104
|
+
// Tests for the main highlighting functionality
|
|
92
105
|
describe('highlightHeadingSection', () => {
|
|
106
|
+
// Test: Can the system highlight a section when given a valid anchor?
|
|
93
107
|
it('highlights section for valid anchor', () => {
|
|
94
108
|
expect(highlightHeadingSection('#h2-1')).toBe(true);
|
|
95
109
|
const highlight = document.querySelector('.highlight2');
|
|
96
110
|
expect(highlight).not.toBeNull();
|
|
97
111
|
expect(highlight.contains(document.getElementById('h2-1'))).toBe(true);
|
|
98
112
|
});
|
|
113
|
+
// Test: Does the system properly reject invalid anchors?
|
|
99
114
|
it('returns false for invalid anchor', () => {
|
|
100
115
|
expect(highlightHeadingSection('#does-not-exist')).toBe(false);
|
|
101
116
|
expect(highlightHeadingSection('not-a-hash')).toBe(false);
|
|
102
117
|
expect(highlightHeadingSection('#')).toBe(false);
|
|
103
118
|
});
|
|
119
|
+
// Test: Does the system only work with heading elements?
|
|
104
120
|
it('returns false for non-heading element', () => {
|
|
105
121
|
expect(highlightHeadingSection('#p1')).toBe(false);
|
|
106
122
|
});
|
|
107
123
|
});
|
|
108
124
|
|
|
125
|
+
// Tests for setting up automatic highlighting based on URL anchors
|
|
109
126
|
describe('initializeAnchorHighlighting', () => {
|
|
127
|
+
// Test: Does the system automatically highlight sections based on URL hash changes?
|
|
110
128
|
it('sets up event listeners and highlights on hash', () => {
|
|
111
129
|
window.location.hash = '#h2-1';
|
|
112
130
|
// Remove highlights if any
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
/**
|
|
8
8
|
* Inserts transcluded external references (trefs) into the document.
|
|
9
9
|
* This function processes the allXTrefs data and inserts definition content into the document
|
|
10
|
-
* for terms marked with the '
|
|
10
|
+
* for terms marked with the 'term-external' class.
|
|
11
11
|
* @param {Object} allXTrefs - The object containing all external references data
|
|
12
12
|
* @param {Array} allXTrefs.xtrefs - Array of external reference objects, each containing term definitions
|
|
13
13
|
*/
|
|
@@ -28,10 +28,21 @@ function insertTrefs(allXTrefs) {
|
|
|
28
28
|
*/
|
|
29
29
|
const allTerms = [];
|
|
30
30
|
|
|
31
|
-
document.querySelectorAll('dl.terms-and-definitions-list dt span.
|
|
31
|
+
document.querySelectorAll('dl.terms-and-definitions-list dt span.term-external').forEach((termElement) => {
|
|
32
|
+
|
|
32
33
|
// Get the full text content including any nested spans (for aliases) of a term (dt)
|
|
33
34
|
// In case of `[[tref:toip1, agency, ag]]`, this will return `agency`
|
|
34
|
-
const textContent = termElement.textContent.trim();
|
|
35
|
+
// const textContent = termElement.textContent.trim();
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
We used to do this: Get the full text content including any nested spans (for aliases) of a term (dt):
|
|
39
|
+
`const textContent = termElement.textContent.trim();`
|
|
40
|
+
(In case of `[[tref:toip1, agency, ag]]`, this will return `agency`)
|
|
41
|
+
|
|
42
|
+
We cannot use the textContent directly anymore because it may not match the original term, since that can be the alias. That is why we use data-original-term.
|
|
43
|
+
In case of `[[tref:toip1, agency, ag]]`, this will always return `agency`, regardless if the alias is used or not, since the original term is stored in data-original-term
|
|
44
|
+
*/
|
|
45
|
+
const originalTerm = termElement.dataset.originalTerm;
|
|
35
46
|
|
|
36
47
|
// Find the dt element once outside the loop
|
|
37
48
|
const dt = termElement.closest('dt');
|
|
@@ -39,7 +50,7 @@ function insertTrefs(allXTrefs) {
|
|
|
39
50
|
// Skip if the term has already been processed
|
|
40
51
|
if (dt) {
|
|
41
52
|
const nextElement = dt.nextElementSibling;
|
|
42
|
-
if (nextElement?.classList.contains('
|
|
53
|
+
if (nextElement?.classList.contains('term-external') &&
|
|
43
54
|
nextElement.classList.contains('meta-info-content-wrapper')) {
|
|
44
55
|
return; // Already processed
|
|
45
56
|
}
|
|
@@ -48,7 +59,7 @@ function insertTrefs(allXTrefs) {
|
|
|
48
59
|
// Only add terms that haven't been processed yet
|
|
49
60
|
allTerms.push({
|
|
50
61
|
element: termElement,
|
|
51
|
-
textContent:
|
|
62
|
+
textContent: originalTerm,
|
|
52
63
|
dt: dt,
|
|
53
64
|
parent: dt?.parentNode
|
|
54
65
|
});
|
|
@@ -60,21 +71,21 @@ function insertTrefs(allXTrefs) {
|
|
|
60
71
|
* @type {Array<{dt: Element, parent: Element, fragment: DocumentFragment}>}
|
|
61
72
|
*/
|
|
62
73
|
const domChanges = allTerms.map(termData => {
|
|
63
|
-
const { textContent, dt, parent } = termData;
|
|
74
|
+
const { textContent: originalTerm, dt, parent } = termData;
|
|
64
75
|
|
|
65
76
|
if (!dt || !parent) {
|
|
66
77
|
return null; // Skip invalid entries
|
|
67
78
|
}
|
|
68
79
|
|
|
69
80
|
// Find the first matching xref to avoid duplicates
|
|
70
|
-
const xref = xtrefsData.xtrefs.find(x => x.term ===
|
|
71
|
-
|
|
81
|
+
const xref = xtrefsData.xtrefs.find(x => x.term === originalTerm);
|
|
82
|
+
|
|
72
83
|
// Create a DocumentFragment to hold all new elements for this term
|
|
73
84
|
const fragment = document.createDocumentFragment();
|
|
74
85
|
|
|
75
86
|
// Create meta info element
|
|
76
87
|
const metaInfoEl = document.createElement('dd');
|
|
77
|
-
metaInfoEl.classList.add('
|
|
88
|
+
metaInfoEl.classList.add('term-external', 'meta-info-content-wrapper', 'collapsed');
|
|
78
89
|
|
|
79
90
|
if (xref) {
|
|
80
91
|
// Generate meta info content
|
|
@@ -82,10 +93,13 @@ function insertTrefs(allXTrefs) {
|
|
|
82
93
|
const owner = xref.owner || 'Unknown';
|
|
83
94
|
const repo = xref.repo && xref.repoUrl ? `[${xref.repo}](${xref.repoUrl})` : 'Unknown';
|
|
84
95
|
const commitHash = xref.commitHash || 'Unknown';
|
|
96
|
+
const linkToOriginalTerm = xref.ghPageUrl ? new URL(`#term:${xref.term}`, xref.ghPageUrl).href : 'Unknown';
|
|
85
97
|
|
|
86
98
|
const metaInfo = `
|
|
87
99
|
| Property | Value |
|
|
88
100
|
| -------- | ----- |
|
|
101
|
+
| Original Term | ${originalTerm} |
|
|
102
|
+
| Link | ${linkToOriginalTerm} |
|
|
89
103
|
| Owner | ${avatar} ${owner} |
|
|
90
104
|
| Repo | ${repo} |
|
|
91
105
|
| Commit hash | ${commitHash} |
|
|
@@ -102,10 +116,45 @@ function insertTrefs(allXTrefs) {
|
|
|
102
116
|
.replace(/\]\]/g, '');
|
|
103
117
|
|
|
104
118
|
// Clean up the markdown content in the term definition
|
|
105
|
-
// Part B: Remove
|
|
119
|
+
// Part B: Remove some <a> elements from the content. We can turn the fact that the inserted URLs are relative (which previously seemed to be a disadvantage) into an advantage, since the fact that the URL then points to a possible local variant is actually an advantage. To this end, if the local variant does indeed exist, we leave the link intact.
|
|
106
120
|
const tempDivForLinks = document.createElement('div');
|
|
107
121
|
tempDivForLinks.innerHTML = md.render(content);
|
|
108
|
-
tempDivForLinks.querySelectorAll('a').forEach(a =>
|
|
122
|
+
tempDivForLinks.querySelectorAll('a').forEach(a => {
|
|
123
|
+
// Helper function to safely check if an element exists by ID
|
|
124
|
+
// This handles IDs with special characters (like colons) that are invalid in CSS selectors
|
|
125
|
+
const elementExistsById = (id) => {
|
|
126
|
+
try {
|
|
127
|
+
return document.getElementById(id) !== null;
|
|
128
|
+
} catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const url = new URL(a.href);
|
|
135
|
+
|
|
136
|
+
// Keep links to different domains
|
|
137
|
+
if (url.hostname !== window.location.hostname) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Keep links with valid local hash anchors
|
|
142
|
+
if (url.hash && elementExistsById(url.hash.slice(1))) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Remove links to same domain without valid hash
|
|
147
|
+
a.replaceWith(...a.childNodes);
|
|
148
|
+
} catch {
|
|
149
|
+
// Handle relative URLs or invalid URL formats
|
|
150
|
+
if (a.href.startsWith('#') && elementExistsById(a.href.slice(1))) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Remove invalid or non-local links
|
|
155
|
+
a.replaceWith(...a.childNodes);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
109
158
|
content = tempDivForLinks.innerHTML;
|
|
110
159
|
|
|
111
160
|
// Parse the rendered HTML to check for dd elements. xref.content is a string that contains HTML, in the form of <dd>...</dd>'s
|
|
@@ -120,7 +169,7 @@ function insertTrefs(allXTrefs) {
|
|
|
120
169
|
// Clone the node to avoid removing it from tempDiv during insertion
|
|
121
170
|
const clonedDD = dd.cloneNode(true);
|
|
122
171
|
// Add necessary classes
|
|
123
|
-
clonedDD.classList.add('
|
|
172
|
+
clonedDD.classList.add('term-external', 'term-external-embedded');
|
|
124
173
|
// Add to fragment
|
|
125
174
|
fragment.appendChild(clonedDD);
|
|
126
175
|
});
|
|
@@ -130,7 +179,7 @@ function insertTrefs(allXTrefs) {
|
|
|
130
179
|
`"content": "This term was not found in the external repository"`
|
|
131
180
|
*/
|
|
132
181
|
const contentEl = document.createElement('dd');
|
|
133
|
-
contentEl.classList.add('
|
|
182
|
+
contentEl.classList.add('term-external', 'term-external-embedded');
|
|
134
183
|
contentEl.innerHTML = tempDiv.innerHTML;
|
|
135
184
|
fragment.appendChild(contentEl);
|
|
136
185
|
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Terminology Section Utility Container
|
|
2
|
+
|
|
3
|
+
This directory contains the modular components for the terminology section utility container functionality.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
The architecture follows a clear separation of concerns:
|
|
8
|
+
|
|
9
|
+
- **DOM Construction**: All layout and structure in `../terminology-section-utility-container.js`
|
|
10
|
+
- **Functionality**: Individual behavior modules in this directory
|
|
11
|
+
|
|
12
|
+
## Structure
|
|
13
|
+
|
|
14
|
+
```text
|
|
15
|
+
terminology-section-utility-container/
|
|
16
|
+
├── README.md # This documentation
|
|
17
|
+
├── hide-show-utility-container.js # Removes container when no terms
|
|
18
|
+
├── create-alphabet-index.js # Alphabet navigation functionality
|
|
19
|
+
├── create-term-filter.js # Local/Remote filter functionality
|
|
20
|
+
└── search.js # Search functionality
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Main Module (`../terminology-section-utility-container.js`)
|
|
24
|
+
|
|
25
|
+
The main coordination module contains:
|
|
26
|
+
|
|
27
|
+
### DOM Construction Section
|
|
28
|
+
Complete layout definition in one place:
|
|
29
|
+
|
|
30
|
+
```text
|
|
31
|
+
/* ===== ROW 1: ALPHABET INDEX ===== */
|
|
32
|
+
- Bootstrap row/col structure
|
|
33
|
+
- Alphabet navigation buttons
|
|
34
|
+
- Sorted character links
|
|
35
|
+
|
|
36
|
+
/* ===== ROW 2: UTILITIES ===== */
|
|
37
|
+
- Term count display
|
|
38
|
+
- Local/Remote filter checkboxes
|
|
39
|
+
- Search input with navigation buttons
|
|
40
|
+
- Matches counter
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Functionality Coordination
|
|
44
|
+
Calls the individual modules:
|
|
45
|
+
|
|
46
|
+
1. `attachAlphabetIndexFunctionality()`
|
|
47
|
+
2. `attachTermFilterFunctionality(checkboxesContainer)`
|
|
48
|
+
3. `attachSearchFunctionality(searchInput, prevBtn, nextBtn, counter)`
|
|
49
|
+
|
|
50
|
+
## Sub-Modules (Functionality Only)
|
|
51
|
+
|
|
52
|
+
Each module receives DOM elements as parameters and attaches behavior:
|
|
53
|
+
|
|
54
|
+
### `create-alphabet-index.js`
|
|
55
|
+
- Currently minimal (standard anchor links)
|
|
56
|
+
- Ready for future enhancements (smooth scroll, analytics)
|
|
57
|
+
|
|
58
|
+
### `create-term-filter.js`
|
|
59
|
+
- Checkbox change event handling
|
|
60
|
+
- "At least one checked" enforcement logic
|
|
61
|
+
- HTML class toggling for hide/show terms
|
|
62
|
+
|
|
63
|
+
### `search.js`
|
|
64
|
+
- Input event handling with debouncing
|
|
65
|
+
- Text highlighting and regex matching
|
|
66
|
+
- Keyboard navigation (Arrow keys)
|
|
67
|
+
- Match counting and navigation
|
|
68
|
+
|
|
69
|
+
## Dependencies
|
|
70
|
+
|
|
71
|
+
All modules depend on:
|
|
72
|
+
|
|
73
|
+
- Bootstrap 5.3+ (for styling classes)
|
|
74
|
+
- `specConfig` global object (for search styling configuration)
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
The modules are automatically loaded and initialized via the asset compilation process. The main module coordinates everything when the DOM is ready.
|
|
79
|
+
|
|
80
|
+
## Layout Result
|
|
81
|
+
|
|
82
|
+
**Row 1 (Alphabet):**
|
|
83
|
+
|
|
84
|
+
```text
|
|
85
|
+
[A] [B] [C] [D] [E] [F] [G] [H] [I] [J] [K] [L] [M] [N] [O] [P] [Q] [R] [S] [T] [U] [V] [W] [X] [Y] [Z]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Row 2 (Controls):**
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
25 terms ☑ Local ☑ Remote [🔍 Search] [0 matches] [▲] [▼]
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Benefits of This Architecture
|
|
95
|
+
|
|
96
|
+
✅ **Layout Visibility**: Complete DOM structure visible in one file
|
|
97
|
+
✅ **Modular Logic**: Functionality separated by concern
|
|
98
|
+
✅ **Maintainability**: Easy to modify layout or individual behaviors
|
|
99
|
+
✅ **Testability**: Functions receive dependencies as parameters
|
|
100
|
+
✅ **Reusability**: Logic modules could be used elsewhere
|
|
101
|
+
|
|
102
|
+
## Maintenance Notes
|
|
103
|
+
|
|
104
|
+
- Order in `asset-map.json` matters: functionality modules before main module
|
|
105
|
+
- All modules use traditional function declarations for Gulp compatibility
|
|
106
|
+
- DOM elements are passed as parameters to avoid tight coupling
|
|
107
|
+
- Bootstrap classes are used extensively to minimize custom CSS
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Alphabet index functionality
|
|
3
|
+
* @author Kor Dwarshuis
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
* @since 2024-08-31
|
|
6
|
+
* @description Handles alphabet navigation functionality (DOM is constructed in main module)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Attaches functionality to the alphabet index
|
|
11
|
+
* @returns {void}
|
|
12
|
+
*/
|
|
13
|
+
function attachAlphabetIndexFunctionality() {
|
|
14
|
+
// Currently, alphabet links are just standard anchor links
|
|
15
|
+
// Functionality can be added here in the future if needed
|
|
16
|
+
// (e.g., smooth scrolling, analytics tracking, etc.)
|
|
17
|
+
}
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @file
|
|
2
|
+
* @file Term filter functionality
|
|
3
3
|
* @author Kor Dwarshuis
|
|
4
|
-
* @version 0.0
|
|
5
|
-
* @since 2024-
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
* @since 2024-08-31
|
|
6
|
+
* @description Handles Local/Remote term filtering logic (DOM is constructed in main module)
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Attaches functionality to the term filter checkboxes
|
|
11
|
+
* @param {HTMLElement} checkboxesContainer - The container with the checkboxes
|
|
12
|
+
* @returns {void}
|
|
13
|
+
*/
|
|
14
|
+
function attachTermFilterFunctionality(checkboxesContainer) {
|
|
8
15
|
// Check if the terms and definitions list exists
|
|
9
|
-
// If it doesn't exist, exit the function
|
|
10
|
-
// This prevents errors when the script is run on pages without the terms and definitions list
|
|
11
|
-
// and ensures that the script only runs when necessary
|
|
12
16
|
const termsListElement = document.querySelector(".terms-and-definitions-list");
|
|
13
17
|
const dtElements = termsListElement ? termsListElement.querySelectorAll("dt") : [];
|
|
14
18
|
|
|
@@ -16,36 +20,6 @@ function createTermFilter() {
|
|
|
16
20
|
return;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
const terminologySectionUtilityContainer = document.getElementById("terminology-section-utility-container");
|
|
20
|
-
|
|
21
|
-
// Create checkboxes container
|
|
22
|
-
const checkboxesContainer = document.createElement('div');
|
|
23
|
-
checkboxesContainer.className = 'd-flex mt-0';
|
|
24
|
-
|
|
25
|
-
// Create and configure checkbox for local terms
|
|
26
|
-
const localTermsCheckboxDiv = document.createElement('div');
|
|
27
|
-
localTermsCheckboxDiv.className = 'form-check me-3';
|
|
28
|
-
localTermsCheckboxDiv.innerHTML = `
|
|
29
|
-
<input class="form-check-input" type="checkbox" id="showLocalTermsCheckbox" checked>
|
|
30
|
-
<label class="form-check-label" for="showLocalTermsCheckbox">
|
|
31
|
-
Show local terms
|
|
32
|
-
</label>
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
// Create and configure checkbox for external terms
|
|
36
|
-
const externalTermsCheckboxDiv = document.createElement('div');
|
|
37
|
-
externalTermsCheckboxDiv.className = 'form-check ms-3';
|
|
38
|
-
externalTermsCheckboxDiv.innerHTML = `
|
|
39
|
-
<input class="form-check-input" type="checkbox" id="showExternalTermsCheckbox" checked>
|
|
40
|
-
<label class="form-check-label" for="showExternalTermsCheckbox">
|
|
41
|
-
Show external terms
|
|
42
|
-
</label>
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
// Append checkboxes to container
|
|
46
|
-
checkboxesContainer.appendChild(localTermsCheckboxDiv);
|
|
47
|
-
checkboxesContainer.appendChild(externalTermsCheckboxDiv);
|
|
48
|
-
|
|
49
23
|
// Add event listeners to checkboxes (generic for any number of checkboxes)
|
|
50
24
|
function enforceAtLeastOneChecked(event) {
|
|
51
25
|
const checkboxes = checkboxesContainer.querySelectorAll('input[type="checkbox"]');
|
|
@@ -81,11 +55,4 @@ function createTermFilter() {
|
|
|
81
55
|
enforceAtLeastOneChecked(event);
|
|
82
56
|
}
|
|
83
57
|
});
|
|
84
|
-
|
|
85
|
-
// Add checkboxes to the terminology section utility container
|
|
86
|
-
terminologySectionUtilityContainer.appendChild(checkboxesContainer);
|
|
87
58
|
}
|
|
88
|
-
|
|
89
|
-
document.addEventListener("DOMContentLoaded", function () {
|
|
90
|
-
createTermFilter();
|
|
91
|
-
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Hides the utility container when no terms exist
|
|
3
|
+
* @author Kor Dwarshuis
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
* @since 2024-08-31
|
|
6
|
+
* @description Removes the utility container if no terms and definitions are present
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hides the utility container if no terms exist
|
|
11
|
+
* @returns {void}
|
|
12
|
+
*/
|
|
13
|
+
function hideShowUtilityContainer() {
|
|
14
|
+
// Check if the terms and definitions list exists
|
|
15
|
+
const termsListElement = document.querySelector(".terms-and-definitions-list");
|
|
16
|
+
const dtElements = termsListElement ? termsListElement.querySelectorAll("dt") : [];
|
|
17
|
+
|
|
18
|
+
if (dtElements.length === 0) {
|
|
19
|
+
document.getElementById("terminology-section-utility-container")?.remove();
|
|
20
|
+
}
|
|
21
|
+
}
|