arcane-os 0.1.0-dev.5
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/CHANGELOG.md +154 -0
- package/COMMERCIAL-LICENSE.md +13 -0
- package/LICENSE +661 -0
- package/NOTICE +70 -0
- package/README.md +448 -0
- package/bin/arcane-test.mjs +741 -0
- package/bin/arcane.mjs +5 -0
- package/docs/architecture.md +234 -0
- package/docs/compatibility.md +36 -0
- package/docs/event-manager.md +166 -0
- package/docs/platform-targets.md +108 -0
- package/docs/publishing.md +203 -0
- package/docs/reference/README.md +117 -0
- package/docs/reference/arcane-ollama.md +288 -0
- package/docs/reference/availability-and-normalization.md +123 -0
- package/docs/reference/behavioral-testing.md +86 -0
- package/docs/reference/cli.md +569 -0
- package/docs/reference/core/README.md +62 -0
- package/docs/reference/core/arcane-ai-contracts.md +873 -0
- package/docs/reference/core/arcane-api.md +601 -0
- package/docs/reference/core/arcane-entities.md +65 -0
- package/docs/reference/core/arcane-events.md +134 -0
- package/docs/reference/core/ollama-module.md +181 -0
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
- package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
- package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
- package/docs/reference/event-manager.md +957 -0
- package/docs/reference/inventory/package-api.json +2632 -0
- package/docs/reference/inventory/runtime-components.json +934 -0
- package/docs/reference/inventory/runtime-entities.json +26 -0
- package/docs/reference/inventory/runtime-modules.json +1249 -0
- package/docs/reference/protocols.md +242 -0
- package/docs/reference/runtime-components.md +1098 -0
- package/docs/reference/runtime-entities.md +303 -0
- package/docs/reference/runtime-modules.md +2010 -0
- package/docs/reference/sdk-api.md +4901 -0
- package/docs/roadmap.md +79 -0
- package/docs/work-amplification.md +124 -0
- package/node_modules/event-pubsub/CHANGELOG.md +55 -0
- package/node_modules/event-pubsub/MIGRATION.md +70 -0
- package/node_modules/event-pubsub/README.md +363 -0
- package/node_modules/event-pubsub/SECURITY.md +37 -0
- package/node_modules/event-pubsub/index.js +141 -0
- package/node_modules/event-pubsub/licence +21 -0
- package/node_modules/event-pubsub/package.json +59 -0
- package/node_modules/strong-type/README.md +408 -0
- package/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/strong-type/index.js +1151 -0
- package/node_modules/strong-type/licence +21 -0
- package/node_modules/strong-type/node.js +125 -0
- package/node_modules/strong-type/package.json +61 -0
- package/package.json +95 -0
- package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
- package/runtime/arcane/components/app-bar.html +468 -0
- package/runtime/arcane/components/assistant-panel.html +715 -0
- package/runtime/arcane/components/calculator.html +8 -0
- package/runtime/arcane/components/chart.html +655 -0
- package/runtime/arcane/components/chat.html +1225 -0
- package/runtime/arcane/components/conversation-view.html +13 -0
- package/runtime/arcane/components/dashboard-config.html +341 -0
- package/runtime/arcane/components/data-maintenance.html +112 -0
- package/runtime/arcane/components/data-view.html +92 -0
- package/runtime/arcane/components/directory-picker.html +197 -0
- package/runtime/arcane/components/document-inspector.html +252 -0
- package/runtime/arcane/components/file-drop.html +264 -0
- package/runtime/arcane/components/file-inspector.html +293 -0
- package/runtime/arcane/components/file-manager.html +1715 -0
- package/runtime/arcane/components/header.html +142 -0
- package/runtime/arcane/components/integration-settings.html +14 -0
- package/runtime/arcane/components/local-ai-status.html +360 -0
- package/runtime/arcane/components/markdown-document.html +1048 -0
- package/runtime/arcane/components/markdown-editor.html +360 -0
- package/runtime/arcane/components/media-embed.html +8 -0
- package/runtime/arcane/components/modal.html +402 -0
- package/runtime/arcane/components/output-panel.html +259 -0
- package/runtime/arcane/components/preferences-form.html +135 -0
- package/runtime/arcane/components/record-timeline.html +105 -0
- package/runtime/arcane/components/relationship-board.html +116 -0
- package/runtime/arcane/components/screen-capture.html +8 -0
- package/runtime/arcane/components/source-code-viewer.html +441 -0
- package/runtime/arcane/components/source-explanation.html +124 -0
- package/runtime/arcane/components/speech.html +365 -0
- package/runtime/arcane/components/summary-strip.html +177 -0
- package/runtime/arcane/components/table.html +77 -0
- package/runtime/arcane/components/task-progress.html +282 -0
- package/runtime/arcane/components/terminal-workspace.html +65 -0
- package/runtime/arcane/components/theme-editor.html +41 -0
- package/runtime/arcane/components/theme-switcher.html +46 -0
- package/runtime/arcane/components/unified-inbox.html +20 -0
- package/runtime/arcane/components/voice-transcription.html +476 -0
- package/runtime/arcane/components/weather-widget.html +8 -0
- package/runtime/arcane/components/web-navigator.html +239 -0
- package/runtime/arcane/css/communications.css +1 -0
- package/runtime/arcane/css/dashboard-config.css +45 -0
- package/runtime/arcane/css/document-site.css +981 -0
- package/runtime/arcane/css/layout.css +438 -0
- package/runtime/arcane/css/primitives.css +321 -0
- package/runtime/arcane/css/theme.css +112 -0
- package/runtime/arcane/css/utility-workspace.css +1 -0
- package/runtime/arcane/entities/ApiModelRecord.js +20 -0
- package/runtime/arcane/entities/Calculation.js +13 -0
- package/runtime/arcane/entities/Chat.js +581 -0
- package/runtime/arcane/entities/CommunicationMessage.js +29 -0
- package/runtime/arcane/entities/CommunicationThread.js +21 -0
- package/runtime/arcane/entities/Document.js +10 -0
- package/runtime/arcane/entities/File.js +143 -0
- package/runtime/arcane/entities/Image.js +135 -0
- package/runtime/arcane/entities/IntentEnvelope.js +834 -0
- package/runtime/arcane/entities/Preference.js +83 -0
- package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
- package/runtime/arcane/entities/TerminalSession.js +46 -0
- package/runtime/arcane/entities/Theme.js +107 -0
- package/runtime/arcane/entities/User.js +1046 -0
- package/runtime/arcane/entities/Weather.js +23 -0
- package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
- package/runtime/arcane/img/arrow-left.png +0 -0
- package/runtime/arcane/img/arrow-right.png +0 -0
- package/runtime/arcane/img/doc.svg +5 -0
- package/runtime/arcane/img/folder.svg +4 -0
- package/runtime/arcane/img/image.svg +5 -0
- package/runtime/arcane/img/refresh.png +0 -0
- package/runtime/arcane/img/send.svg +9 -0
- package/runtime/arcane/img/trash.svg +5 -0
- package/runtime/arcane/img/upload.svg +5 -0
- package/runtime/arcane/modules/AI.js +2048 -0
- package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
- package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
- package/runtime/arcane/modules/AIResponseLength.js +42 -0
- package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
- package/runtime/arcane/modules/AnsiText.js +53 -0
- package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
- package/runtime/arcane/modules/AppDataScope.js +245 -0
- package/runtime/arcane/modules/AppearancePreferences.js +28 -0
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
- package/runtime/arcane/modules/AsyncBoundary.js +161 -0
- package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
- package/runtime/arcane/modules/CalculatorEngine.js +20 -0
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
- package/runtime/arcane/modules/ChartLibrary.js +35 -0
- package/runtime/arcane/modules/ChatRecords.js +13 -0
- package/runtime/arcane/modules/CommunicationAppController.js +43 -0
- package/runtime/arcane/modules/CommunicationHub.js +16 -0
- package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
- package/runtime/arcane/modules/ComponentContracts.js +586 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
- package/runtime/arcane/modules/ConversationActionItems.js +488 -0
- package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
- package/runtime/arcane/modules/ConversationTimebox.js +527 -0
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
- package/runtime/arcane/modules/DBLS.js +171 -0
- package/runtime/arcane/modules/DBOPFS.js +1154 -0
- package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
- package/runtime/arcane/modules/DataMaintenance.js +91 -0
- package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
- package/runtime/arcane/modules/DirectoryPicker.js +109 -0
- package/runtime/arcane/modules/DocumentNavigation.js +223 -0
- package/runtime/arcane/modules/Errors.js +1025 -0
- package/runtime/arcane/modules/GifEncoder.js +29 -0
- package/runtime/arcane/modules/HTMLImport.js +114 -0
- package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
- package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
- package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
- package/runtime/arcane/modules/MD.js +111 -0
- package/runtime/arcane/modules/Mail.js +352 -0
- package/runtime/arcane/modules/MailTransport.mjs +180 -0
- package/runtime/arcane/modules/Marked.min.js +71 -0
- package/runtime/arcane/modules/MemoryRecords.js +44 -0
- package/runtime/arcane/modules/MessageAdvisory.js +38 -0
- package/runtime/arcane/modules/ModelDefinition.js +189 -0
- package/runtime/arcane/modules/Ollama.js +74 -0
- package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
- package/runtime/arcane/modules/OllamaSettings.js +24 -0
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
- package/runtime/arcane/modules/PreferenceStore.js +109 -0
- package/runtime/arcane/modules/QRCode.min.js +1 -0
- package/runtime/arcane/modules/Questionnaire.js +61 -0
- package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
- package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
- package/runtime/arcane/modules/RecordReviewStore.js +94 -0
- package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
- package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
- package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
- package/runtime/arcane/modules/ScreenCapture.js +20 -0
- package/runtime/arcane/modules/SpeechPlayback.js +581 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
- package/runtime/arcane/modules/SystemAppearance.js +20 -0
- package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
- package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
- package/runtime/arcane/modules/TerminalClient.js +52 -0
- package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
- package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
- package/runtime/arcane/modules/ThemeManager.js +131 -0
- package/runtime/arcane/modules/TimeGuard.js +149 -0
- package/runtime/arcane/modules/ToolCallRouter.js +83 -0
- package/runtime/arcane/modules/WaitForComponent.js +102 -0
- package/runtime/arcane/modules/YouTubeMedia.js +16 -0
- package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
- package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
- package/runtime/arcane/modules/uPlot.min.css +1 -0
- package/runtime/arcane/security/arcane-network-policy.json +6 -0
- package/runtime/strong-type/index.js +352 -0
- package/runtime/strong-type/licence +21 -0
- package/runtime/strong-type/package.json +45 -0
- package/schemas/arcane-app-bundle.schema.json +125 -0
- package/schemas/arcane-app.schema.json +329 -0
- package/schemas/arcane-lock.schema.json +86 -0
- package/schemas/arcane-package.schema.json +224 -0
- package/schemas/cli-event.schema.json +122 -0
- package/schemas/event-stack.schema.json +152 -0
- package/schemas/native-build-plan.schema.json +176 -0
- package/schemas/target-adapter.schema.json +117 -0
- package/src/app-descriptor.mjs +500 -0
- package/src/cli/main.mjs +561 -0
- package/src/constants.mjs +31 -0
- package/src/dev-server.mjs +718 -0
- package/src/doctor.mjs +315 -0
- package/src/dom-event-instrumentation.mjs +594 -0
- package/src/errors.mjs +75 -0
- package/src/event-manager.mjs +1342 -0
- package/src/event-queue.mjs +138 -0
- package/src/events.mjs +219 -0
- package/src/index.mjs +177 -0
- package/src/integrated-provider-loader.mjs +432 -0
- package/src/native-plan.mjs +698 -0
- package/src/native-provider-loader.mjs +1126 -0
- package/src/packager/core.mjs +2691 -0
- package/src/process.mjs +353 -0
- package/src/release-bundle.mjs +2523 -0
- package/src/repository.mjs +90 -0
- package/src/runtime.mjs +452 -0
- package/src/scaffold.mjs +380 -0
- package/src/targets/index.mjs +436 -0
- package/src/templates/assets/app-icon.png +0 -0
- package/src/templates/workspace-template.mjs +388 -0
- package/src/testing-loader.mjs +9 -0
- package/src/testing.mjs +427 -0
- package/src/toolchain.mjs +1335 -0
- package/src/update-check.mjs +307 -0
- package/src/workspace.mjs +449 -0
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
<link rel="stylesheet" href="./arcane/css/layout.css?v=3">
|
|
2
|
+
<link rel="stylesheet" href="./arcane/css/theme.css?v=1">
|
|
3
|
+
<link rel="stylesheet" href="./arcane/css/primitives.css?v=1">
|
|
4
|
+
|
|
5
|
+
<style>
|
|
6
|
+
:host{
|
|
7
|
+
color:var(--text-color);
|
|
8
|
+
display:block;
|
|
9
|
+
min-width:0;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
[hidden]{
|
|
13
|
+
display:none !important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.markdown-document{
|
|
17
|
+
min-width:0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.document-state{
|
|
21
|
+
background:var(--secondary-color);
|
|
22
|
+
border:1px solid var(--border-color);
|
|
23
|
+
border-radius:.5rem;
|
|
24
|
+
color:var(--muted-text-color);
|
|
25
|
+
margin:0;
|
|
26
|
+
padding:1rem;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.document-state[data-state="error"]{
|
|
30
|
+
border-inline-start:.3rem solid var(--danger-color);
|
|
31
|
+
color:var(--text-color);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.table-of-contents{
|
|
35
|
+
background:var(--secondary-color);
|
|
36
|
+
border:1px solid var(--border-color);
|
|
37
|
+
border-radius:.5rem;
|
|
38
|
+
margin-block:0 1.5rem;
|
|
39
|
+
padding:1rem 1.25rem;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.table-of-contents h2{
|
|
43
|
+
font-size:1rem;
|
|
44
|
+
margin:0 0 .6rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.table-of-contents ol{
|
|
48
|
+
margin:0;
|
|
49
|
+
padding-inline-start:1.5rem;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.table-of-contents li+li{
|
|
53
|
+
margin-block-start:.35rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.table-of-contents li[data-level="3"]{
|
|
57
|
+
margin-inline-start:1rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.table-of-contents li[data-level="4"],
|
|
61
|
+
.table-of-contents li[data-level="5"],
|
|
62
|
+
.table-of-contents li[data-level="6"]{
|
|
63
|
+
margin-inline-start:2rem;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.document-content{
|
|
67
|
+
line-height:1.65;
|
|
68
|
+
min-width:0;
|
|
69
|
+
overflow-wrap:break-word;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.document-content:focus{
|
|
73
|
+
outline:none;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.document-content :where(h1,h2,h3,h4,h5,h6){
|
|
77
|
+
line-height:1.25;
|
|
78
|
+
scroll-margin-block-start:1rem;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.document-content :where(h1,h2,h3,h4,h5,h6):focus{
|
|
82
|
+
border-radius:.25rem;
|
|
83
|
+
outline:3px solid var(--focus-color);
|
|
84
|
+
outline-offset:.2rem;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.document-content a,
|
|
88
|
+
.table-of-contents a{
|
|
89
|
+
color:var(--focus-color);
|
|
90
|
+
text-decoration-thickness:.08em;
|
|
91
|
+
text-underline-offset:.15em;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.document-content a:focus-visible,
|
|
95
|
+
.table-of-contents a:focus-visible{
|
|
96
|
+
border-radius:.2rem;
|
|
97
|
+
outline:3px solid var(--focus-color);
|
|
98
|
+
outline-offset:.2rem;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.document-content blockquote{
|
|
102
|
+
border-inline-start:.25rem solid var(--border-color);
|
|
103
|
+
color:var(--muted-text-color);
|
|
104
|
+
margin-inline:0;
|
|
105
|
+
padding-inline-start:1rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.document-content :where(code,kbd){
|
|
109
|
+
background:var(--secondary-color);
|
|
110
|
+
border-radius:.25rem;
|
|
111
|
+
font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
112
|
+
padding:.08em .3em;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.document-content pre{
|
|
116
|
+
background:var(--secondary-color);
|
|
117
|
+
border:1px solid var(--border-color);
|
|
118
|
+
border-radius:.5rem;
|
|
119
|
+
overflow:auto;
|
|
120
|
+
padding:1rem;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.document-content pre code{
|
|
124
|
+
background:transparent;
|
|
125
|
+
padding:0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
.document-content img{
|
|
129
|
+
block-size:auto;
|
|
130
|
+
border-radius:.35rem;
|
|
131
|
+
max-inline-size:100%;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.document-content table{
|
|
135
|
+
border-collapse:collapse;
|
|
136
|
+
display:block;
|
|
137
|
+
inline-size:100%;
|
|
138
|
+
max-inline-size:100%;
|
|
139
|
+
overflow-x:auto;
|
|
140
|
+
overscroll-behavior-inline:contain;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.document-content table :where(code,kbd){
|
|
144
|
+
overflow-wrap:normal;
|
|
145
|
+
white-space:normal;
|
|
146
|
+
word-break:normal;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.document-content table:focus-visible{
|
|
150
|
+
outline:3px solid var(--focus-color);
|
|
151
|
+
outline-offset:.2rem;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.document-content :where(th,td){
|
|
155
|
+
border:1px solid var(--border-color);
|
|
156
|
+
padding:.5rem;
|
|
157
|
+
text-align:start;
|
|
158
|
+
vertical-align:top;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.document-content th{
|
|
162
|
+
background:var(--secondary-color);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
@media (forced-colors:active){
|
|
166
|
+
.document-state,
|
|
167
|
+
.table-of-contents,
|
|
168
|
+
.document-content pre,
|
|
169
|
+
.document-content :where(th,td){
|
|
170
|
+
border-color:CanvasText;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.document-content :where(h1,h2,h3,h4,h5,h6):focus,
|
|
174
|
+
.document-content a:focus-visible,
|
|
175
|
+
.document-content table:focus-visible,
|
|
176
|
+
.table-of-contents a:focus-visible{
|
|
177
|
+
outline-color:Highlight;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
</style>
|
|
181
|
+
|
|
182
|
+
<section id="document" class="markdown-document" part="container" aria-busy="false">
|
|
183
|
+
<p
|
|
184
|
+
id="status"
|
|
185
|
+
class="document-state"
|
|
186
|
+
part="status"
|
|
187
|
+
role="status"
|
|
188
|
+
aria-live="polite"
|
|
189
|
+
aria-atomic="true"
|
|
190
|
+
tabindex="-1"
|
|
191
|
+
></p>
|
|
192
|
+
|
|
193
|
+
<nav
|
|
194
|
+
id="tableOfContents"
|
|
195
|
+
class="table-of-contents"
|
|
196
|
+
part="table-of-contents"
|
|
197
|
+
aria-labelledby="tableOfContentsTitle"
|
|
198
|
+
hidden
|
|
199
|
+
>
|
|
200
|
+
<h2 id="tableOfContentsTitle"></h2>
|
|
201
|
+
<ol id="tableOfContentsList"></ol>
|
|
202
|
+
</nav>
|
|
203
|
+
|
|
204
|
+
<article
|
|
205
|
+
id="content"
|
|
206
|
+
class="document-content"
|
|
207
|
+
part="content"
|
|
208
|
+
tabindex="-1"
|
|
209
|
+
hidden
|
|
210
|
+
></article>
|
|
211
|
+
</section>
|
|
212
|
+
|
|
213
|
+
<script type="module">
|
|
214
|
+
const host=this;
|
|
215
|
+
host.ready=false;
|
|
216
|
+
|
|
217
|
+
const componentURL=new URL(
|
|
218
|
+
host.getAttribute('href')||'./arcane/components/markdown-document.html',
|
|
219
|
+
document.baseURI
|
|
220
|
+
);
|
|
221
|
+
const moduleURL=name=>new URL(`../modules/${name}`,componentURL).href;
|
|
222
|
+
const {default:MD}=await import(moduleURL('MD.js?v=2'));
|
|
223
|
+
|
|
224
|
+
const documentElement=host.shadowRoot.querySelector('#document');
|
|
225
|
+
const statusElement=host.shadowRoot.querySelector('#status');
|
|
226
|
+
const tableOfContentsElement=host.shadowRoot.querySelector('#tableOfContents');
|
|
227
|
+
const tableOfContentsTitle=host.shadowRoot.querySelector('#tableOfContentsTitle');
|
|
228
|
+
const tableOfContentsList=host.shadowRoot.querySelector('#tableOfContentsList');
|
|
229
|
+
const contentElement=host.shadowRoot.querySelector('#content');
|
|
230
|
+
|
|
231
|
+
// BEGIN MARKDOWN_DOCUMENT_POLICY
|
|
232
|
+
const MARKDOWN_DOCUMENT_ALLOWED_ELEMENTS=Object.freeze([
|
|
233
|
+
'A','ABBR','BLOCKQUOTE','BR','CODE','DEL','EM',
|
|
234
|
+
'H1','H2','H3','H4','H5','H6','HR','IMG','KBD','LI',
|
|
235
|
+
'OL','P','PRE','S','STRONG','SUB','SUP','TABLE','TBODY',
|
|
236
|
+
'TD','TFOOT','TH','THEAD','TR','UL'
|
|
237
|
+
]);
|
|
238
|
+
const MARKDOWN_DOCUMENT_ALLOWED_ATTRIBUTES=Object.freeze(
|
|
239
|
+
{
|
|
240
|
+
A:Object.freeze(['href','title']),
|
|
241
|
+
ABBR:Object.freeze(['title']),
|
|
242
|
+
CODE:Object.freeze(['class']),
|
|
243
|
+
IMG:Object.freeze(['alt','src','title']),
|
|
244
|
+
LI:Object.freeze(['value']),
|
|
245
|
+
OL:Object.freeze(['start']),
|
|
246
|
+
TD:Object.freeze(['colspan','rowspan']),
|
|
247
|
+
TH:Object.freeze(['colspan','rowspan','scope'])
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
const MARKDOWN_DOCUMENT_LINK_PROTOCOLS=new Set(['http:','https:','mailto:']);
|
|
251
|
+
const MARKDOWN_DOCUMENT_ASSET_PROTOCOLS=new Set(['http:','https:']);
|
|
252
|
+
|
|
253
|
+
function markdownDocumentElementAllowed(name=''){
|
|
254
|
+
return MARKDOWN_DOCUMENT_ALLOWED_ELEMENTS.includes(
|
|
255
|
+
String(name||'').toUpperCase()
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function markdownDocumentAllowedAttributes(name=''){
|
|
260
|
+
return MARKDOWN_DOCUMENT_ALLOWED_ATTRIBUTES[
|
|
261
|
+
String(name||'').toUpperCase()
|
|
262
|
+
]||Object.freeze([]);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function markdownDocumentCleanURLInput(value=''){
|
|
266
|
+
const input=String(value??'').trim();
|
|
267
|
+
if(!input||/[\u0000-\u001F\u007F]/.test(input)){
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
270
|
+
return input;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function markdownDocumentDecodeFragment(value=''){
|
|
274
|
+
try{
|
|
275
|
+
return decodeURIComponent(String(value||''));
|
|
276
|
+
}catch{
|
|
277
|
+
return String(value||'');
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function markdownDocumentSourceURL(value='',baseURL='',pageOrigin=''){
|
|
282
|
+
const fallback=baseURL||document.baseURI;
|
|
283
|
+
const expectedOrigin=pageOrigin
|
|
284
|
+
||globalThis.location?.origin
|
|
285
|
+
||new URL(fallback).origin;
|
|
286
|
+
const input=markdownDocumentCleanURLInput(value||fallback);
|
|
287
|
+
if(!input){
|
|
288
|
+
return '';
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
try{
|
|
292
|
+
const url=new URL(input,fallback);
|
|
293
|
+
if(
|
|
294
|
+
!MARKDOWN_DOCUMENT_ASSET_PROTOCOLS.has(url.protocol)
|
|
295
|
+
||url.origin!==expectedOrigin
|
|
296
|
+
||url.username
|
|
297
|
+
||url.password
|
|
298
|
+
){
|
|
299
|
+
return '';
|
|
300
|
+
}
|
|
301
|
+
return url.href;
|
|
302
|
+
}catch{
|
|
303
|
+
return '';
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function markdownDocumentLinkPolicy(value='',sourceURL='',pageOrigin=''){
|
|
308
|
+
const input=markdownDocumentCleanURLInput(value);
|
|
309
|
+
if(!input){
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const expectedOrigin=pageOrigin
|
|
314
|
+
||globalThis.location?.origin
|
|
315
|
+
||new URL(sourceURL).origin;
|
|
316
|
+
|
|
317
|
+
if(input.startsWith('#')){
|
|
318
|
+
const fragment=markdownDocumentDecodeFragment(input.slice(1))
|
|
319
|
+
.replace(/[\u0000-\u001F\u007F]/g,'')
|
|
320
|
+
.slice(0,256);
|
|
321
|
+
if(!fragment){
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
return Object.freeze(
|
|
325
|
+
{
|
|
326
|
+
external:false,
|
|
327
|
+
fragment,
|
|
328
|
+
href:`#${encodeURIComponent(fragment)}`,
|
|
329
|
+
kind:'fragment',
|
|
330
|
+
targetURL:new URL(`#${encodeURIComponent(fragment)}`,sourceURL).href
|
|
331
|
+
}
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
try{
|
|
336
|
+
const url=new URL(input,sourceURL);
|
|
337
|
+
if(
|
|
338
|
+
!MARKDOWN_DOCUMENT_LINK_PROTOCOLS.has(url.protocol)
|
|
339
|
+
||url.username
|
|
340
|
+
||url.password
|
|
341
|
+
){
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const sameOrigin=url.origin===expectedOrigin;
|
|
346
|
+
const markdown=sameOrigin&&/\.(?:md|markdown)$/i.test(url.pathname);
|
|
347
|
+
return Object.freeze(
|
|
348
|
+
{
|
|
349
|
+
external:!sameOrigin,
|
|
350
|
+
fragment:markdownDocumentDecodeFragment(url.hash.slice(1)).slice(0,256),
|
|
351
|
+
href:url.href,
|
|
352
|
+
kind:markdown?'markdown':'link',
|
|
353
|
+
targetURL:url.href
|
|
354
|
+
}
|
|
355
|
+
);
|
|
356
|
+
}catch{
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function markdownDocumentAssetURL(value='',sourceURL='',pageOrigin=''){
|
|
362
|
+
const input=markdownDocumentCleanURLInput(value);
|
|
363
|
+
if(!input){
|
|
364
|
+
return '';
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const expectedOrigin=pageOrigin
|
|
368
|
+
||globalThis.location?.origin
|
|
369
|
+
||new URL(sourceURL).origin;
|
|
370
|
+
try{
|
|
371
|
+
const url=new URL(input,sourceURL);
|
|
372
|
+
if(
|
|
373
|
+
!MARKDOWN_DOCUMENT_ASSET_PROTOCOLS.has(url.protocol)
|
|
374
|
+
||url.origin!==expectedOrigin
|
|
375
|
+
||url.username
|
|
376
|
+
||url.password
|
|
377
|
+
){
|
|
378
|
+
return '';
|
|
379
|
+
}
|
|
380
|
+
return url.href;
|
|
381
|
+
}catch{
|
|
382
|
+
return '';
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function markdownDocumentHeadingBase(value=''){
|
|
387
|
+
const identifier=String(value||'')
|
|
388
|
+
.normalize('NFKD')
|
|
389
|
+
.replace(/[\u0300-\u036f]/g,'')
|
|
390
|
+
.toLowerCase()
|
|
391
|
+
.replace(/&/g,' and ')
|
|
392
|
+
.replace(/[^a-z0-9]+/g,'-')
|
|
393
|
+
.replace(/^-+|-+$/g,'')
|
|
394
|
+
.slice(0,80);
|
|
395
|
+
return identifier||'section';
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function markdownDocumentNextHeadingIdentifier(value='',usedIdentifiers=new Set()){
|
|
399
|
+
const base=markdownDocumentHeadingBase(value);
|
|
400
|
+
let candidate=base;
|
|
401
|
+
let suffix=2;
|
|
402
|
+
while(usedIdentifiers.has(candidate)){
|
|
403
|
+
candidate=`${base}-${suffix++}`;
|
|
404
|
+
}
|
|
405
|
+
usedIdentifiers.add(candidate);
|
|
406
|
+
return candidate;
|
|
407
|
+
}
|
|
408
|
+
// END MARKDOWN_DOCUMENT_POLICY
|
|
409
|
+
|
|
410
|
+
const pageOrigin=globalThis.location?.origin||new URL(document.baseURI).origin;
|
|
411
|
+
const defaultSourceURL=markdownDocumentSourceURL(
|
|
412
|
+
document.baseURI,
|
|
413
|
+
document.baseURI,
|
|
414
|
+
pageOrigin
|
|
415
|
+
);
|
|
416
|
+
const defaultOptions=Object.freeze(
|
|
417
|
+
{
|
|
418
|
+
labels:Object.freeze(
|
|
419
|
+
{
|
|
420
|
+
content:'Document content',
|
|
421
|
+
empty:'No document content is available.',
|
|
422
|
+
error:'Unable to display this document.',
|
|
423
|
+
loading:'Loading document…',
|
|
424
|
+
tableOfContents:'On this page'
|
|
425
|
+
}
|
|
426
|
+
),
|
|
427
|
+
onNavigate:null,
|
|
428
|
+
showTableOfContents:true,
|
|
429
|
+
sourceURL:defaultSourceURL
|
|
430
|
+
}
|
|
431
|
+
);
|
|
432
|
+
|
|
433
|
+
let currentMarkdown='';
|
|
434
|
+
let currentSourceURL=defaultSourceURL;
|
|
435
|
+
let loadSequence=0;
|
|
436
|
+
let options=normalizeOptions(optionsFromDataset(),defaultOptions);
|
|
437
|
+
let state='empty';
|
|
438
|
+
let tableOfContents=[];
|
|
439
|
+
|
|
440
|
+
host.clear=clear;
|
|
441
|
+
host.configure=configure;
|
|
442
|
+
host.fail=fail;
|
|
443
|
+
host.focus=focusDocument;
|
|
444
|
+
host.focusFragment=focusFragment;
|
|
445
|
+
host.load=load;
|
|
446
|
+
host.render=render;
|
|
447
|
+
|
|
448
|
+
Object.defineProperties(
|
|
449
|
+
host,
|
|
450
|
+
{
|
|
451
|
+
options:{get:getOptions,set:configure},
|
|
452
|
+
sourceURL:{get:()=>currentSourceURL,set:setSourceURL},
|
|
453
|
+
state:{get:()=>state},
|
|
454
|
+
tableOfContents:{get:()=>tableOfContents.map(entry=>({...entry}))},
|
|
455
|
+
value:{get:()=>currentMarkdown,set:value=>render(value)}
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
documentElement.addEventListener('click',handleNavigation);
|
|
460
|
+
|
|
461
|
+
function optionsFromDataset(){
|
|
462
|
+
const configured={};
|
|
463
|
+
if(host.dataset.sourceUrl!==undefined){
|
|
464
|
+
configured.sourceURL=host.dataset.sourceUrl;
|
|
465
|
+
}
|
|
466
|
+
if(host.dataset.showTableOfContents!==undefined){
|
|
467
|
+
configured.showTableOfContents=host.dataset.showTableOfContents!=='false';
|
|
468
|
+
}
|
|
469
|
+
return configured;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function normalizeOptions(input={},previous=defaultOptions){
|
|
473
|
+
if(!input||typeof input!=='object'||Array.isArray(input)){
|
|
474
|
+
throw new TypeError('Markdown document options must be an object.');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const labelInput=input.labels===undefined?{}:input.labels;
|
|
478
|
+
if(!labelInput||typeof labelInput!=='object'||Array.isArray(labelInput)){
|
|
479
|
+
throw new TypeError('Markdown document labels must be an object.');
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const labels={...previous.labels};
|
|
483
|
+
for(const name of ['content','empty','error','loading','tableOfContents']){
|
|
484
|
+
if(labelInput[name]!==undefined){
|
|
485
|
+
if(typeof labelInput[name]!=='string'||!labelInput[name].trim()){
|
|
486
|
+
throw new TypeError(`Markdown document labels.${name} must be a nonempty string.`);
|
|
487
|
+
}
|
|
488
|
+
labels[name]=labelInput[name].trim();
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const sourceInput=input.sourceURL??input.sourceUrl??previous.sourceURL;
|
|
493
|
+
const sourceURL=markdownDocumentSourceURL(
|
|
494
|
+
sourceInput,
|
|
495
|
+
previous.sourceURL||document.baseURI,
|
|
496
|
+
pageOrigin
|
|
497
|
+
);
|
|
498
|
+
if(!sourceURL){
|
|
499
|
+
throw new TypeError('Markdown document sourceURL must resolve to a same-origin HTTP(S) URL.');
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const onNavigate=input.onNavigate===undefined
|
|
503
|
+
?previous.onNavigate
|
|
504
|
+
:input.onNavigate;
|
|
505
|
+
if(onNavigate!==null&&typeof onNavigate!=='function'){
|
|
506
|
+
throw new TypeError('Markdown document onNavigate must be a function or null.');
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const showTableOfContents=input.showTableOfContents===undefined
|
|
510
|
+
?previous.showTableOfContents
|
|
511
|
+
:input.showTableOfContents;
|
|
512
|
+
if(typeof showTableOfContents!=='boolean'){
|
|
513
|
+
throw new TypeError('Markdown document showTableOfContents must be boolean.');
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return Object.freeze(
|
|
517
|
+
{
|
|
518
|
+
labels:Object.freeze(labels),
|
|
519
|
+
onNavigate,
|
|
520
|
+
showTableOfContents,
|
|
521
|
+
sourceURL
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function getOptions(){
|
|
527
|
+
return {
|
|
528
|
+
labels:{...options.labels},
|
|
529
|
+
onNavigate:options.onNavigate,
|
|
530
|
+
showTableOfContents:options.showTableOfContents,
|
|
531
|
+
sourceURL:options.sourceURL
|
|
532
|
+
};
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function configure(input={}){
|
|
536
|
+
const priorSourceURL=options.sourceURL;
|
|
537
|
+
options=normalizeOptions(input,options);
|
|
538
|
+
renderLabels();
|
|
539
|
+
|
|
540
|
+
if(!currentMarkdown){
|
|
541
|
+
currentSourceURL=options.sourceURL;
|
|
542
|
+
updateStateDOM();
|
|
543
|
+
}else if(priorSourceURL!==options.sourceURL){
|
|
544
|
+
render(currentMarkdown,{sourceURL:options.sourceURL});
|
|
545
|
+
}else{
|
|
546
|
+
buildTableOfContents(tableOfContents);
|
|
547
|
+
updateStateDOM();
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return getOptions();
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function renderLabels(){
|
|
554
|
+
contentElement.setAttribute('aria-label',options.labels.content);
|
|
555
|
+
tableOfContentsTitle.textContent=options.labels.tableOfContents;
|
|
556
|
+
updateStateDOM();
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function sourceURLFrom(input={},fallback=options.sourceURL){
|
|
560
|
+
if(!input||typeof input!=='object'||Array.isArray(input)){
|
|
561
|
+
throw new TypeError('Markdown document render options must be an object.');
|
|
562
|
+
}
|
|
563
|
+
const candidate=input.sourceURL??input.sourceUrl??fallback;
|
|
564
|
+
const result=markdownDocumentSourceURL(
|
|
565
|
+
candidate,
|
|
566
|
+
fallback||document.baseURI,
|
|
567
|
+
pageOrigin
|
|
568
|
+
);
|
|
569
|
+
if(!result){
|
|
570
|
+
throw new TypeError('Markdown document sourceURL must resolve to a same-origin HTTP(S) URL.');
|
|
571
|
+
}
|
|
572
|
+
return result;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function setSourceURL(value=''){
|
|
576
|
+
options=normalizeOptions({sourceURL:value},options);
|
|
577
|
+
if(currentMarkdown){
|
|
578
|
+
render(currentMarkdown,{sourceURL:options.sourceURL});
|
|
579
|
+
}else{
|
|
580
|
+
currentSourceURL=options.sourceURL;
|
|
581
|
+
updateStateDOM();
|
|
582
|
+
}
|
|
583
|
+
return currentSourceURL;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
async function load(source='',loadOptions={}){
|
|
587
|
+
const sequence=++loadSequence;
|
|
588
|
+
let sourceURL;
|
|
589
|
+
try{
|
|
590
|
+
sourceURL=sourceURLFrom(loadOptions,currentSourceURL||options.sourceURL);
|
|
591
|
+
}catch(error){
|
|
592
|
+
if(sequence===loadSequence){
|
|
593
|
+
showError(error);
|
|
594
|
+
}
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
currentSourceURL=sourceURL;
|
|
599
|
+
transition('loading');
|
|
600
|
+
|
|
601
|
+
try{
|
|
602
|
+
const pending=typeof source==='function'
|
|
603
|
+
?source({sourceURL})
|
|
604
|
+
:source;
|
|
605
|
+
const result=await pending;
|
|
606
|
+
if(sequence!==loadSequence){
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if(
|
|
611
|
+
result
|
|
612
|
+
&&typeof result==='object'
|
|
613
|
+
&&!Array.isArray(result)
|
|
614
|
+
){
|
|
615
|
+
sourceURL=sourceURLFrom(
|
|
616
|
+
result,
|
|
617
|
+
sourceURL
|
|
618
|
+
);
|
|
619
|
+
return renderMarkdown(result.markdown??result.value??'',sourceURL);
|
|
620
|
+
}
|
|
621
|
+
return renderMarkdown(result??'',sourceURL);
|
|
622
|
+
}catch(error){
|
|
623
|
+
if(sequence===loadSequence){
|
|
624
|
+
showError(error);
|
|
625
|
+
}
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function render(markdown='',renderOptions={}){
|
|
631
|
+
++loadSequence;
|
|
632
|
+
try{
|
|
633
|
+
const sourceURL=sourceURLFrom(
|
|
634
|
+
renderOptions,
|
|
635
|
+
currentSourceURL||options.sourceURL
|
|
636
|
+
);
|
|
637
|
+
return renderMarkdown(markdown,sourceURL);
|
|
638
|
+
}catch(error){
|
|
639
|
+
showError(error);
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function renderMarkdown(markdown='',sourceURL=options.sourceURL){
|
|
645
|
+
if(typeof markdown!=='string'){
|
|
646
|
+
throw new TypeError('Markdown document content must be a string.');
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
currentMarkdown=markdown;
|
|
650
|
+
currentSourceURL=sourceURL;
|
|
651
|
+
contentElement.replaceChildren();
|
|
652
|
+
tableOfContents=[];
|
|
653
|
+
tableOfContentsList.replaceChildren();
|
|
654
|
+
|
|
655
|
+
if(!markdown.trim()){
|
|
656
|
+
transition('empty');
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// MD.safeRendered is the shared first pass. The detached template used
|
|
661
|
+
// below is inert while the stricter positive policy removes anything
|
|
662
|
+
// that the shared sanitizer intentionally leaves available.
|
|
663
|
+
const safeRendered=new MD(markdown).safeRendered;
|
|
664
|
+
const fragment=strictMarkdownFragment(safeRendered,sourceURL);
|
|
665
|
+
const meaningful=Boolean(
|
|
666
|
+
fragment.textContent?.trim()
|
|
667
|
+
||fragment.querySelector('img,hr,table')
|
|
668
|
+
);
|
|
669
|
+
if(!meaningful){
|
|
670
|
+
transition('empty');
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
tableOfContents=decorateHeadings(fragment);
|
|
675
|
+
decorateTables(fragment);
|
|
676
|
+
contentElement.replaceChildren(fragment);
|
|
677
|
+
buildTableOfContents(tableOfContents);
|
|
678
|
+
transition(
|
|
679
|
+
'ready',
|
|
680
|
+
{
|
|
681
|
+
characters:markdown.length,
|
|
682
|
+
headings:tableOfContents.map(entry=>({...entry}))
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function strictMarkdownFragment(html='',sourceURL=options.sourceURL){
|
|
689
|
+
const template=document.createElement('template');
|
|
690
|
+
template.innerHTML=html;
|
|
691
|
+
|
|
692
|
+
const elements=Array.from(template.content.querySelectorAll('*'));
|
|
693
|
+
for(const element of elements){
|
|
694
|
+
const name=element.tagName.toUpperCase();
|
|
695
|
+
if(!markdownDocumentElementAllowed(name)){
|
|
696
|
+
// Removing the complete subtree rejects custom elements such as
|
|
697
|
+
// html-import rather than preserving executable descendants.
|
|
698
|
+
element.remove();
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
const allowedAttributes=new Set(markdownDocumentAllowedAttributes(name));
|
|
703
|
+
for(const attribute of Array.from(element.attributes)){
|
|
704
|
+
if(!allowedAttributes.has(attribute.name.toLowerCase())){
|
|
705
|
+
element.removeAttribute(attribute.name);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
sanitizeAllowedAttributeValues(element,name);
|
|
710
|
+
if(name==='A'){
|
|
711
|
+
sanitizeLink(element,sourceURL);
|
|
712
|
+
}else if(name==='IMG'){
|
|
713
|
+
sanitizeImage(element,sourceURL);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
const comments=[];
|
|
718
|
+
const commentWalker=document.createTreeWalker(
|
|
719
|
+
template.content,
|
|
720
|
+
globalThis.NodeFilter?.SHOW_COMMENT||128
|
|
721
|
+
);
|
|
722
|
+
while(commentWalker.nextNode()){
|
|
723
|
+
comments.push(commentWalker.currentNode);
|
|
724
|
+
}
|
|
725
|
+
comments.forEach(comment=>comment.remove());
|
|
726
|
+
return template.content;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function sanitizeAllowedAttributeValues(element,name){
|
|
730
|
+
for(const attribute of Array.from(element.attributes)){
|
|
731
|
+
if(attribute.value.length>2048){
|
|
732
|
+
element.setAttribute(attribute.name,attribute.value.slice(0,2048));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if(name==='CODE'&&element.hasAttribute('class')){
|
|
737
|
+
const className=element.getAttribute('class').trim();
|
|
738
|
+
if(!/^language-[a-z0-9][a-z0-9_+-]{0,63}$/i.test(className)){
|
|
739
|
+
element.removeAttribute('class');
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
for(const attribute of ['colspan','rowspan']){
|
|
744
|
+
if(element.hasAttribute(attribute)){
|
|
745
|
+
sanitizeIntegerAttribute(element,attribute,1,100);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
if(name==='OL'&&element.hasAttribute('start')){
|
|
749
|
+
sanitizeIntegerAttribute(element,'start',-100000,100000);
|
|
750
|
+
}
|
|
751
|
+
if(name==='LI'&&element.hasAttribute('value')){
|
|
752
|
+
sanitizeIntegerAttribute(element,'value',-100000,100000);
|
|
753
|
+
}
|
|
754
|
+
if(name==='TH'&&element.hasAttribute('scope')){
|
|
755
|
+
const scope=element.getAttribute('scope').toLowerCase();
|
|
756
|
+
if(!['col','colgroup','row','rowgroup'].includes(scope)){
|
|
757
|
+
element.removeAttribute('scope');
|
|
758
|
+
}else{
|
|
759
|
+
element.setAttribute('scope',scope);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function sanitizeIntegerAttribute(element,name,min,max){
|
|
765
|
+
const value=Number(element.getAttribute(name));
|
|
766
|
+
if(!Number.isSafeInteger(value)||value<min||value>max){
|
|
767
|
+
element.removeAttribute(name);
|
|
768
|
+
return;
|
|
769
|
+
}
|
|
770
|
+
element.setAttribute(name,String(value));
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
function sanitizeLink(link,sourceURL){
|
|
774
|
+
const originalHref=link.getAttribute('href')||'';
|
|
775
|
+
const policy=markdownDocumentLinkPolicy(
|
|
776
|
+
originalHref,
|
|
777
|
+
sourceURL,
|
|
778
|
+
pageOrigin
|
|
779
|
+
);
|
|
780
|
+
if(!policy){
|
|
781
|
+
link.removeAttribute('href');
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
link.setAttribute('href',policy.href);
|
|
786
|
+
link.dataset.markdownKind=policy.kind;
|
|
787
|
+
link.dataset.markdownOriginalHref=originalHref.slice(0,2048);
|
|
788
|
+
link.dataset.markdownTargetUrl=policy.targetURL;
|
|
789
|
+
link.dataset.markdownFragment=policy.fragment;
|
|
790
|
+
|
|
791
|
+
if(
|
|
792
|
+
policy.external
|
|
793
|
+
&&/^https?:$/i.test(new URL(policy.targetURL).protocol)
|
|
794
|
+
){
|
|
795
|
+
link.target='_blank';
|
|
796
|
+
link.rel='noopener noreferrer';
|
|
797
|
+
link.referrerPolicy='no-referrer';
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function sanitizeImage(image,sourceURL){
|
|
802
|
+
const resolved=markdownDocumentAssetURL(
|
|
803
|
+
image.getAttribute('src')||'',
|
|
804
|
+
sourceURL,
|
|
805
|
+
pageOrigin
|
|
806
|
+
);
|
|
807
|
+
if(resolved){
|
|
808
|
+
image.setAttribute('src',resolved);
|
|
809
|
+
}else{
|
|
810
|
+
image.removeAttribute('src');
|
|
811
|
+
}
|
|
812
|
+
image.alt=image.getAttribute('alt')||'';
|
|
813
|
+
image.loading='lazy';
|
|
814
|
+
image.decoding='async';
|
|
815
|
+
image.referrerPolicy='no-referrer';
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function decorateHeadings(fragment){
|
|
819
|
+
const usedHeadingIds=new Set();
|
|
820
|
+
return Array.from(fragment.querySelectorAll('h1,h2,h3,h4,h5,h6'))
|
|
821
|
+
.map(
|
|
822
|
+
heading=>{
|
|
823
|
+
const text=heading.textContent.trim()||'Untitled section';
|
|
824
|
+
const id=markdownDocumentNextHeadingIdentifier(
|
|
825
|
+
text,
|
|
826
|
+
usedHeadingIds
|
|
827
|
+
);
|
|
828
|
+
const level=Number(heading.tagName.slice(1));
|
|
829
|
+
heading.id=id;
|
|
830
|
+
heading.tabIndex=-1;
|
|
831
|
+
return Object.freeze({id,level,text});
|
|
832
|
+
}
|
|
833
|
+
);
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function decorateTables(fragment){
|
|
837
|
+
Array.from(fragment.querySelectorAll('table')).forEach(
|
|
838
|
+
(table,index)=>{
|
|
839
|
+
const headers=Array.from(table.querySelectorAll('thead th'));
|
|
840
|
+
headers.forEach(header=>{
|
|
841
|
+
header.scope='col';
|
|
842
|
+
});
|
|
843
|
+
const headingText=headers
|
|
844
|
+
.map(header=>header.textContent.trim())
|
|
845
|
+
.filter(Boolean)
|
|
846
|
+
.join(', ');
|
|
847
|
+
const prefix=`Data table ${index+1}`;
|
|
848
|
+
const label=headingText?`${prefix}: ${headingText}`:prefix;
|
|
849
|
+
|
|
850
|
+
table.tabIndex=0;
|
|
851
|
+
table.setAttribute('aria-label',label.slice(0,256));
|
|
852
|
+
}
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function buildTableOfContents(entries=[]){
|
|
857
|
+
const fragment=document.createDocumentFragment();
|
|
858
|
+
for(const entry of entries){
|
|
859
|
+
const item=document.createElement('li');
|
|
860
|
+
const link=document.createElement('a');
|
|
861
|
+
const href=`#${encodeURIComponent(entry.id)}`;
|
|
862
|
+
const targetURL=new URL(href,currentSourceURL).href;
|
|
863
|
+
|
|
864
|
+
item.dataset.level=String(entry.level);
|
|
865
|
+
link.href=href;
|
|
866
|
+
link.textContent=entry.text;
|
|
867
|
+
link.dataset.markdownKind='fragment';
|
|
868
|
+
link.dataset.markdownOriginalHref=href;
|
|
869
|
+
link.dataset.markdownTargetUrl=targetURL;
|
|
870
|
+
link.dataset.markdownFragment=entry.id;
|
|
871
|
+
item.append(link);
|
|
872
|
+
fragment.append(item);
|
|
873
|
+
}
|
|
874
|
+
tableOfContentsList.replaceChildren(fragment);
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
function clear(){
|
|
878
|
+
++loadSequence;
|
|
879
|
+
currentMarkdown='';
|
|
880
|
+
currentSourceURL=options.sourceURL;
|
|
881
|
+
contentElement.replaceChildren();
|
|
882
|
+
tableOfContents=[];
|
|
883
|
+
tableOfContentsList.replaceChildren();
|
|
884
|
+
transition('empty');
|
|
885
|
+
return true;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
function fail(error){
|
|
889
|
+
++loadSequence;
|
|
890
|
+
return showError(error);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
function showError(error){
|
|
894
|
+
currentMarkdown='';
|
|
895
|
+
contentElement.replaceChildren();
|
|
896
|
+
tableOfContents=[];
|
|
897
|
+
tableOfContentsList.replaceChildren();
|
|
898
|
+
const message=error instanceof Error
|
|
899
|
+
?error.message
|
|
900
|
+
:String(error||options.labels.error);
|
|
901
|
+
transition('error',{message:message.slice(0,2048)});
|
|
902
|
+
return false;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
function transition(nextState,detail={}){
|
|
906
|
+
state=nextState;
|
|
907
|
+
updateStateDOM();
|
|
908
|
+
|
|
909
|
+
const eventDetail=Object.freeze(
|
|
910
|
+
{
|
|
911
|
+
...detail,
|
|
912
|
+
sourceURL:currentSourceURL,
|
|
913
|
+
state
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
host.dispatchEvent(
|
|
917
|
+
new CustomEvent(
|
|
918
|
+
'markdown-document-state',
|
|
919
|
+
{bubbles:true,composed:true,detail:eventDetail}
|
|
920
|
+
)
|
|
921
|
+
);
|
|
922
|
+
|
|
923
|
+
const stateEvent={
|
|
924
|
+
empty:'markdown-document-empty',
|
|
925
|
+
error:'markdown-document-error',
|
|
926
|
+
loading:'markdown-document-loading',
|
|
927
|
+
ready:'markdown-document-rendered'
|
|
928
|
+
}[state];
|
|
929
|
+
if(stateEvent){
|
|
930
|
+
host.dispatchEvent(
|
|
931
|
+
new CustomEvent(
|
|
932
|
+
stateEvent,
|
|
933
|
+
{bubbles:true,composed:true,detail:eventDetail}
|
|
934
|
+
)
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
return eventDetail;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function updateStateDOM(){
|
|
941
|
+
const readyState=state==='ready';
|
|
942
|
+
const loadingState=state==='loading';
|
|
943
|
+
documentElement.setAttribute('aria-busy',String(loadingState));
|
|
944
|
+
host.setAttribute('aria-busy',String(loadingState));
|
|
945
|
+
host.dataset.state=state;
|
|
946
|
+
statusElement.dataset.state=state;
|
|
947
|
+
statusElement.hidden=readyState;
|
|
948
|
+
contentElement.hidden=!readyState;
|
|
949
|
+
tableOfContentsElement.hidden=(
|
|
950
|
+
!readyState
|
|
951
|
+
||!options.showTableOfContents
|
|
952
|
+
||tableOfContents.length===0
|
|
953
|
+
);
|
|
954
|
+
|
|
955
|
+
statusElement.textContent={
|
|
956
|
+
empty:options.labels.empty,
|
|
957
|
+
error:options.labels.error,
|
|
958
|
+
loading:options.labels.loading,
|
|
959
|
+
ready:''
|
|
960
|
+
}[state]||'';
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
function focusDocument(){
|
|
964
|
+
if(state==='ready'){
|
|
965
|
+
const heading=contentElement.querySelector('h1,h2,h3,h4,h5,h6');
|
|
966
|
+
(heading||contentElement).focus({preventScroll:true});
|
|
967
|
+
return true;
|
|
968
|
+
}
|
|
969
|
+
statusElement.focus({preventScroll:true});
|
|
970
|
+
return false;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
function handleNavigation(event){
|
|
974
|
+
if(event.defaultPrevented||event.button!==0){
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const link=event.composedPath().find(
|
|
978
|
+
node=>node?.tagName==='A'&&documentElement.contains(node)
|
|
979
|
+
);
|
|
980
|
+
if(!link||!link.hasAttribute('href')){
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const detail=Object.freeze(
|
|
985
|
+
{
|
|
986
|
+
fragment:link.dataset.markdownFragment||'',
|
|
987
|
+
href:link.dataset.markdownOriginalHref||link.getAttribute('href'),
|
|
988
|
+
kind:link.dataset.markdownKind||'link',
|
|
989
|
+
sourceURL:currentSourceURL,
|
|
990
|
+
targetURL:link.dataset.markdownTargetUrl||link.href
|
|
991
|
+
}
|
|
992
|
+
);
|
|
993
|
+
const navigationEvent=new CustomEvent(
|
|
994
|
+
'markdown-document-navigate',
|
|
995
|
+
{
|
|
996
|
+
bubbles:true,
|
|
997
|
+
cancelable:true,
|
|
998
|
+
composed:true,
|
|
999
|
+
detail
|
|
1000
|
+
}
|
|
1001
|
+
);
|
|
1002
|
+
const continueNavigation=host.dispatchEvent(navigationEvent);
|
|
1003
|
+
|
|
1004
|
+
if(detail.kind==='fragment'||detail.kind==='markdown'){
|
|
1005
|
+
event.preventDefault();
|
|
1006
|
+
}
|
|
1007
|
+
if(!continueNavigation){
|
|
1008
|
+
event.preventDefault();
|
|
1009
|
+
return;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if(detail.kind==='fragment'){
|
|
1013
|
+
focusFragment(detail.fragment);
|
|
1014
|
+
}else if(detail.kind==='markdown'&&options.onNavigate){
|
|
1015
|
+
Promise.resolve(options.onNavigate(detail)).catch(
|
|
1016
|
+
error=>console.error('Markdown document navigation callback failed.',error)
|
|
1017
|
+
);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
function focusFragment(fragment=''){
|
|
1022
|
+
const decoded=markdownDocumentDecodeFragment(fragment);
|
|
1023
|
+
const generatedIdentifier=markdownDocumentHeadingBase(decoded);
|
|
1024
|
+
const target=Array.from(contentElement.querySelectorAll('[id]')).find(
|
|
1025
|
+
element=>element.id===decoded||element.id===generatedIdentifier
|
|
1026
|
+
);
|
|
1027
|
+
if(!target){
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
target.focus({preventScroll:true});
|
|
1031
|
+
target.scrollIntoView({block:'start'});
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
renderLabels();
|
|
1036
|
+
host.ready=true;
|
|
1037
|
+
transition('empty');
|
|
1038
|
+
host.dispatchEvent(
|
|
1039
|
+
new CustomEvent(
|
|
1040
|
+
'markdown-document-ready',
|
|
1041
|
+
{
|
|
1042
|
+
bubbles:true,
|
|
1043
|
+
composed:true,
|
|
1044
|
+
detail:{component:host,state}
|
|
1045
|
+
}
|
|
1046
|
+
)
|
|
1047
|
+
);
|
|
1048
|
+
</script>
|