@matdata/yasgui 4.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +175 -0
- package/build/ts/src/PersistentConfig.d.ts +49 -0
- package/build/ts/src/Tab.d.ts +105 -0
- package/build/ts/src/TabContextMenu.d.ts +29 -0
- package/build/ts/src/TabElements.d.ts +45 -0
- package/build/ts/src/TabSettingsModal.d.ts +28 -0
- package/build/ts/src/defaults.d.ts +3 -0
- package/build/ts/src/endpointSelect.d.ts +44 -0
- package/build/ts/src/index.d.ts +104 -0
- package/build/ts/src/linkUtils.d.ts +43 -0
- package/build/yasgui.html +24 -0
- package/build/yasgui.min.css +2 -0
- package/build/yasgui.min.css.map +1 -0
- package/build/yasgui.min.js +3 -0
- package/build/yasgui.min.js.LICENSE.txt +59 -0
- package/build/yasgui.min.js.map +1 -0
- package/package.json +48 -0
- package/src/PersistentConfig.ts +159 -0
- package/src/Tab.ts +775 -0
- package/src/TabContextMenu.scss +42 -0
- package/src/TabContextMenu.ts +143 -0
- package/src/TabElements.scss +134 -0
- package/src/TabElements.ts +336 -0
- package/src/TabSettingsModal.scss +226 -0
- package/src/TabSettingsModal.ts +424 -0
- package/src/defaults.ts +64 -0
- package/src/endpointSelect.scss +122 -0
- package/src/endpointSelect.ts +339 -0
- package/src/index.scss +97 -0
- package/src/index.ts +373 -0
- package/src/linkUtils.ts +234 -0
- package/src/tab.scss +61 -0
- package/static/yasgui.bootstrap.css +36 -0
- package/static/yasgui.polyfill.min.js +4 -0
- package/typings-custom/@tarekraafat/autocomplete.js/index.d.ts +48 -0
- package/typings-custom/main.d.ts +1 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
.tabPrefixButton {
|
|
2
|
+
cursor: pointer;
|
|
3
|
+
background: transparent;
|
|
4
|
+
color: inherit;
|
|
5
|
+
border: none;
|
|
6
|
+
padding: 6px 12px;
|
|
7
|
+
font-size: 12px;
|
|
8
|
+
font-weight: 600;
|
|
9
|
+
margin-left: 10px;
|
|
10
|
+
|
|
11
|
+
&:hover {
|
|
12
|
+
opacity: 0.7;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
&:active {
|
|
16
|
+
opacity: 0.5;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.tabSettingsModalOverlay {
|
|
21
|
+
display: none;
|
|
22
|
+
position: fixed;
|
|
23
|
+
top: 0;
|
|
24
|
+
left: 0;
|
|
25
|
+
right: 0;
|
|
26
|
+
bottom: 0;
|
|
27
|
+
background: rgba(0, 0, 0, 0.5);
|
|
28
|
+
z-index: 10000;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
|
|
32
|
+
&.open {
|
|
33
|
+
display: flex;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.tabSettingsModal {
|
|
38
|
+
background: white;
|
|
39
|
+
border-radius: 8px;
|
|
40
|
+
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
|
41
|
+
max-width: 800px;
|
|
42
|
+
width: 90%;
|
|
43
|
+
max-height: 90vh;
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
overflow: hidden;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.modalHeader {
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
align-items: center;
|
|
53
|
+
padding: 20px;
|
|
54
|
+
border-bottom: 1px solid #e0e0e0;
|
|
55
|
+
|
|
56
|
+
h2 {
|
|
57
|
+
margin: 0;
|
|
58
|
+
font-size: 20px;
|
|
59
|
+
font-weight: 600;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.closeButton {
|
|
63
|
+
background: none;
|
|
64
|
+
border: none;
|
|
65
|
+
font-size: 32px;
|
|
66
|
+
line-height: 1;
|
|
67
|
+
cursor: pointer;
|
|
68
|
+
color: #666;
|
|
69
|
+
padding: 0;
|
|
70
|
+
width: 32px;
|
|
71
|
+
height: 32px;
|
|
72
|
+
|
|
73
|
+
&:hover {
|
|
74
|
+
color: #333;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.modalBody {
|
|
80
|
+
flex: 1;
|
|
81
|
+
overflow-y: auto;
|
|
82
|
+
padding: 20px;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.modalTabs {
|
|
86
|
+
display: flex;
|
|
87
|
+
gap: 10px;
|
|
88
|
+
margin-bottom: 20px;
|
|
89
|
+
border-bottom: 2px solid #e0e0e0;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.modalTabButton {
|
|
93
|
+
background: none;
|
|
94
|
+
border: none;
|
|
95
|
+
padding: 10px 20px;
|
|
96
|
+
font-size: 14px;
|
|
97
|
+
font-weight: 600;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
color: #666;
|
|
100
|
+
border-bottom: 3px solid transparent;
|
|
101
|
+
margin-bottom: -2px;
|
|
102
|
+
transition: all 0.2s;
|
|
103
|
+
|
|
104
|
+
&:hover {
|
|
105
|
+
color: #337ab7;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
&.active {
|
|
109
|
+
color: #337ab7;
|
|
110
|
+
border-bottom-color: #337ab7;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.modalTabContent {
|
|
115
|
+
display: none;
|
|
116
|
+
|
|
117
|
+
&.active {
|
|
118
|
+
display: block;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.settingsSection {
|
|
123
|
+
margin-bottom: 20px;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.settingsLabel {
|
|
127
|
+
font-weight: 600;
|
|
128
|
+
margin-bottom: 8px;
|
|
129
|
+
font-size: 14px;
|
|
130
|
+
display: block;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.settingsHelp {
|
|
134
|
+
font-size: 12px;
|
|
135
|
+
color: #666;
|
|
136
|
+
margin-bottom: 10px;
|
|
137
|
+
font-style: italic;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.settingsSelect {
|
|
141
|
+
width: 100%;
|
|
142
|
+
padding: 8px;
|
|
143
|
+
border: 1px solid #ccc;
|
|
144
|
+
border-radius: 4px;
|
|
145
|
+
font-size: 14px;
|
|
146
|
+
|
|
147
|
+
&:focus {
|
|
148
|
+
outline: none;
|
|
149
|
+
border-color: #337ab7;
|
|
150
|
+
box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.1);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.prefixTextarea {
|
|
155
|
+
width: 100%;
|
|
156
|
+
padding: 10px;
|
|
157
|
+
border: 1px solid #ccc;
|
|
158
|
+
border-radius: 4px;
|
|
159
|
+
font-family: monospace;
|
|
160
|
+
font-size: 13px;
|
|
161
|
+
resize: vertical;
|
|
162
|
+
box-sizing: border-box;
|
|
163
|
+
min-height: 200px;
|
|
164
|
+
|
|
165
|
+
&:focus {
|
|
166
|
+
outline: none;
|
|
167
|
+
border-color: #337ab7;
|
|
168
|
+
box-shadow: 0 0 0 2px rgba(51, 122, 183, 0.1);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.checkboxContainer {
|
|
173
|
+
margin-top: 15px;
|
|
174
|
+
display: flex;
|
|
175
|
+
align-items: center;
|
|
176
|
+
gap: 8px;
|
|
177
|
+
|
|
178
|
+
input[type="checkbox"] {
|
|
179
|
+
width: 18px;
|
|
180
|
+
height: 18px;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
label {
|
|
185
|
+
cursor: pointer;
|
|
186
|
+
font-size: 14px;
|
|
187
|
+
user-select: none;
|
|
188
|
+
margin: 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.modalFooter {
|
|
193
|
+
padding: 15px 20px;
|
|
194
|
+
border-top: 1px solid #e0e0e0;
|
|
195
|
+
display: flex;
|
|
196
|
+
justify-content: flex-end;
|
|
197
|
+
gap: 10px;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
.primaryButton,
|
|
201
|
+
.secondaryButton {
|
|
202
|
+
padding: 8px 20px;
|
|
203
|
+
border: none;
|
|
204
|
+
border-radius: 4px;
|
|
205
|
+
font-size: 14px;
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
transition: background-color 0.2s;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.primaryButton {
|
|
211
|
+
background: #337ab7;
|
|
212
|
+
color: white;
|
|
213
|
+
|
|
214
|
+
&:hover {
|
|
215
|
+
background: #286090;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.secondaryButton {
|
|
220
|
+
background: #e0e0e0;
|
|
221
|
+
color: #333;
|
|
222
|
+
|
|
223
|
+
&:hover {
|
|
224
|
+
background: #d0d0d0;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { addClass, removeClass, drawSvgStringAsElement } from "@matdata/yasgui-utils";
|
|
2
|
+
import "./TabSettingsModal.scss";
|
|
3
|
+
import Tab from "./Tab";
|
|
4
|
+
|
|
5
|
+
const AcceptOptionsMap: { key: string; value: string }[] = [
|
|
6
|
+
{ key: "JSON", value: "application/sparql-results+json" },
|
|
7
|
+
{ key: "XML", value: "application/sparql-results+xml" },
|
|
8
|
+
{ key: "CSV", value: "text/csv" },
|
|
9
|
+
{ key: "TSV", value: "text/tab-separated-values" },
|
|
10
|
+
];
|
|
11
|
+
const AcceptHeaderGraphMap: { key: string; value: string }[] = [
|
|
12
|
+
{ key: "Turtle", value: "text/turtle" },
|
|
13
|
+
{ key: "JSON", value: "application/rdf+json" },
|
|
14
|
+
{ key: "RDF/XML", value: "application/rdf+xml" },
|
|
15
|
+
{ key: "TriG", value: "application/trig" },
|
|
16
|
+
{ key: "N-Triples", value: "application/n-triples" },
|
|
17
|
+
{ key: "N-Quads", value: "application/n-quads" },
|
|
18
|
+
{ key: "CSV", value: "text/csv" },
|
|
19
|
+
{ key: "TSV", value: "text/tab-separated-values" },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
export default class TabSettingsModal {
|
|
23
|
+
private tab: Tab;
|
|
24
|
+
private modalOverlay!: HTMLElement;
|
|
25
|
+
private modalContent!: HTMLElement;
|
|
26
|
+
private settingsButton!: HTMLButtonElement;
|
|
27
|
+
private prefixButton!: HTMLButtonElement;
|
|
28
|
+
private prefixTextarea!: HTMLTextAreaElement;
|
|
29
|
+
private autoCaptureCheckbox!: HTMLInputElement;
|
|
30
|
+
|
|
31
|
+
constructor(tab: Tab, controlBarEl: HTMLElement) {
|
|
32
|
+
this.tab = tab;
|
|
33
|
+
this.init(controlBarEl);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private init(controlBarEl: HTMLElement) {
|
|
37
|
+
// Settings button
|
|
38
|
+
this.settingsButton = document.createElement("button");
|
|
39
|
+
this.settingsButton.setAttribute("aria-label", "Settings");
|
|
40
|
+
this.settingsButton.title = "Settings";
|
|
41
|
+
this.settingsButton.appendChild(
|
|
42
|
+
drawSvgStringAsElement(
|
|
43
|
+
`<svg width="100.06" height="100.05" data-name="Layer 1" version="1.1" viewBox="0 0 100.06 100.05" xmlns="http://www.w3.org/2000/svg">
|
|
44
|
+
<title>Settings</title>
|
|
45
|
+
<path d="m95.868 58.018-3-3.24a42.5 42.5 0 0 0 0-9.43l3-3.22c1.79-1.91 5-4.44 4-6.85l-4.11-10c-1-2.41-5.08-1.91-7.69-2l-4.43-0.16a43.24 43.24 0 0 0-6.64-6.66l-0.14-4.43c-0.08-2.6 0.43-6.69-2-7.69l-10-4.15c-2.4-1-4.95 2.25-6.85 4l-3.23 3a42.49 42.49 0 0 0-9.44 0l-3.21-3c-1.9-1.78-4.44-5-6.85-4l-10 4.11c-2.41 1-1.9 5.09-2 7.69l-0.16 4.42a43.24 43.24 0 0 0-6.67 6.65l-4.42 0.14c-2.6 0.08-6.69-0.43-7.69 2l-4.15 10c-1 2.4 2.25 4.94 4 6.84l3 3.23a42.49 42.49 0 0 0 0 9.44l-3 3.22c-1.78 1.9-5 4.43-4 6.84l4.11 10c1 2.41 5.09 1.91 7.7 2l4.41 0.15a43.24 43.24 0 0 0 6.66 6.68l0.13 4.41c0.08 2.6-0.43 6.7 2 7.7l10 4.15c2.4 1 4.94-2.25 6.84-4l3.24-3a42.5 42.5 0 0 0 9.42 0l3.22 3c1.91 1.79 4.43 5 6.84 4l10-4.11c2.41-1 1.91-5.08 2-7.7l0.15-4.42a43.24 43.24 0 0 0 6.68-6.65l4.42-0.14c2.6-0.08 6.7 0.43 7.7-2l4.15-10c1.04-2.36-2.22-4.9-3.99-6.82zm-45.74 15.7c-12.66 0-22.91-10.61-22.91-23.7s10.25-23.7 22.91-23.7 22.91 10.61 22.91 23.7-10.25 23.7-22.91 23.7z"/>
|
|
46
|
+
</svg>`,
|
|
47
|
+
),
|
|
48
|
+
);
|
|
49
|
+
addClass(this.settingsButton, "tabContextButton");
|
|
50
|
+
controlBarEl.appendChild(this.settingsButton);
|
|
51
|
+
this.settingsButton.onclick = () => this.open();
|
|
52
|
+
|
|
53
|
+
// Prefix button
|
|
54
|
+
this.prefixButton = document.createElement("button");
|
|
55
|
+
this.prefixButton.setAttribute("aria-label", "Insert Prefixes");
|
|
56
|
+
this.prefixButton.title = "Insert saved prefixes into query";
|
|
57
|
+
this.prefixButton.textContent = "PREFIX";
|
|
58
|
+
addClass(this.prefixButton, "tabPrefixButton");
|
|
59
|
+
controlBarEl.appendChild(this.prefixButton);
|
|
60
|
+
this.prefixButton.onclick = () => this.insertPrefixesIntoQuery();
|
|
61
|
+
|
|
62
|
+
this.createModal();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private createModal() {
|
|
66
|
+
// Modal overlay
|
|
67
|
+
this.modalOverlay = document.createElement("div");
|
|
68
|
+
addClass(this.modalOverlay, "tabSettingsModalOverlay");
|
|
69
|
+
this.modalOverlay.onclick = () => this.close();
|
|
70
|
+
|
|
71
|
+
// Modal content
|
|
72
|
+
this.modalContent = document.createElement("div");
|
|
73
|
+
addClass(this.modalContent, "tabSettingsModal");
|
|
74
|
+
this.modalContent.onclick = (e) => e.stopPropagation();
|
|
75
|
+
|
|
76
|
+
// Header
|
|
77
|
+
const header = document.createElement("div");
|
|
78
|
+
addClass(header, "modalHeader");
|
|
79
|
+
const title = document.createElement("h2");
|
|
80
|
+
title.textContent = "Tab Settings";
|
|
81
|
+
header.appendChild(title);
|
|
82
|
+
|
|
83
|
+
const closeBtn = document.createElement("button");
|
|
84
|
+
closeBtn.textContent = "×";
|
|
85
|
+
addClass(closeBtn, "closeButton");
|
|
86
|
+
closeBtn.onclick = () => this.close();
|
|
87
|
+
header.appendChild(closeBtn);
|
|
88
|
+
|
|
89
|
+
this.modalContent.appendChild(header);
|
|
90
|
+
|
|
91
|
+
// Body with tabs
|
|
92
|
+
const body = document.createElement("div");
|
|
93
|
+
addClass(body, "modalBody");
|
|
94
|
+
|
|
95
|
+
const tabsContainer = document.createElement("div");
|
|
96
|
+
addClass(tabsContainer, "modalTabs");
|
|
97
|
+
|
|
98
|
+
const requestTab = document.createElement("button");
|
|
99
|
+
requestTab.textContent = "Request";
|
|
100
|
+
addClass(requestTab, "modalTabButton", "active");
|
|
101
|
+
requestTab.onclick = () => this.switchTab("request");
|
|
102
|
+
|
|
103
|
+
const prefixTab = document.createElement("button");
|
|
104
|
+
prefixTab.textContent = "Prefixes";
|
|
105
|
+
addClass(prefixTab, "modalTabButton");
|
|
106
|
+
prefixTab.onclick = () => this.switchTab("prefix");
|
|
107
|
+
|
|
108
|
+
tabsContainer.appendChild(requestTab);
|
|
109
|
+
tabsContainer.appendChild(prefixTab);
|
|
110
|
+
body.appendChild(tabsContainer);
|
|
111
|
+
|
|
112
|
+
// Tab content containers
|
|
113
|
+
const requestContent = document.createElement("div");
|
|
114
|
+
addClass(requestContent, "modalTabContent", "active");
|
|
115
|
+
requestContent.id = "request-content";
|
|
116
|
+
this.drawRequestSettings(requestContent);
|
|
117
|
+
|
|
118
|
+
const prefixContent = document.createElement("div");
|
|
119
|
+
addClass(prefixContent, "modalTabContent");
|
|
120
|
+
prefixContent.id = "prefix-content";
|
|
121
|
+
this.drawPrefixSettings(prefixContent);
|
|
122
|
+
|
|
123
|
+
body.appendChild(requestContent);
|
|
124
|
+
body.appendChild(prefixContent);
|
|
125
|
+
|
|
126
|
+
this.modalContent.appendChild(body);
|
|
127
|
+
|
|
128
|
+
// Footer
|
|
129
|
+
const footer = document.createElement("div");
|
|
130
|
+
addClass(footer, "modalFooter");
|
|
131
|
+
|
|
132
|
+
const saveBtn = document.createElement("button");
|
|
133
|
+
saveBtn.textContent = "Save";
|
|
134
|
+
addClass(saveBtn, "primaryButton");
|
|
135
|
+
saveBtn.onclick = () => this.save();
|
|
136
|
+
|
|
137
|
+
const cancelBtn = document.createElement("button");
|
|
138
|
+
cancelBtn.textContent = "Cancel";
|
|
139
|
+
addClass(cancelBtn, "secondaryButton");
|
|
140
|
+
cancelBtn.onclick = () => this.close();
|
|
141
|
+
|
|
142
|
+
footer.appendChild(cancelBtn);
|
|
143
|
+
footer.appendChild(saveBtn);
|
|
144
|
+
|
|
145
|
+
this.modalContent.appendChild(footer);
|
|
146
|
+
|
|
147
|
+
this.modalOverlay.appendChild(this.modalContent);
|
|
148
|
+
document.body.appendChild(this.modalOverlay);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private switchTab(tabName: string) {
|
|
152
|
+
const buttons = this.modalContent.querySelectorAll(".modalTabButton");
|
|
153
|
+
const contents = this.modalContent.querySelectorAll(".modalTabContent");
|
|
154
|
+
|
|
155
|
+
buttons.forEach((btn, index) => {
|
|
156
|
+
if ((tabName === "request" && index === 0) || (tabName === "prefix" && index === 1)) {
|
|
157
|
+
addClass(btn as HTMLElement, "active");
|
|
158
|
+
} else {
|
|
159
|
+
removeClass(btn as HTMLElement, "active");
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
contents.forEach((content) => {
|
|
164
|
+
if (content.id === `${tabName}-content`) {
|
|
165
|
+
addClass(content as HTMLElement, "active");
|
|
166
|
+
} else {
|
|
167
|
+
removeClass(content as HTMLElement, "active");
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private drawPrefixSettings(container: HTMLElement) {
|
|
173
|
+
const section = document.createElement("div");
|
|
174
|
+
addClass(section, "settingsSection");
|
|
175
|
+
|
|
176
|
+
const label = document.createElement("label");
|
|
177
|
+
label.textContent = "Saved Prefixes";
|
|
178
|
+
addClass(label, "settingsLabel");
|
|
179
|
+
|
|
180
|
+
const help = document.createElement("div");
|
|
181
|
+
help.textContent = "Enter PREFIX declarations (one per line). These will be available for insertion into queries.";
|
|
182
|
+
addClass(help, "settingsHelp");
|
|
183
|
+
|
|
184
|
+
this.prefixTextarea = document.createElement("textarea");
|
|
185
|
+
addClass(this.prefixTextarea, "prefixTextarea");
|
|
186
|
+
this.prefixTextarea.placeholder =
|
|
187
|
+
"PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\nPREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>";
|
|
188
|
+
this.prefixTextarea.rows = 10;
|
|
189
|
+
|
|
190
|
+
const checkboxContainer = document.createElement("div");
|
|
191
|
+
addClass(checkboxContainer, "checkboxContainer");
|
|
192
|
+
|
|
193
|
+
this.autoCaptureCheckbox = document.createElement("input");
|
|
194
|
+
this.autoCaptureCheckbox.type = "checkbox";
|
|
195
|
+
this.autoCaptureCheckbox.id = "autoCapturePrefixes";
|
|
196
|
+
|
|
197
|
+
const checkboxLabel = document.createElement("label");
|
|
198
|
+
checkboxLabel.htmlFor = "autoCapturePrefixes";
|
|
199
|
+
checkboxLabel.textContent = "Automatically capture new prefixes from query editor";
|
|
200
|
+
|
|
201
|
+
checkboxContainer.appendChild(this.autoCaptureCheckbox);
|
|
202
|
+
checkboxContainer.appendChild(checkboxLabel);
|
|
203
|
+
|
|
204
|
+
section.appendChild(label);
|
|
205
|
+
section.appendChild(help);
|
|
206
|
+
section.appendChild(this.prefixTextarea);
|
|
207
|
+
section.appendChild(checkboxContainer);
|
|
208
|
+
|
|
209
|
+
container.appendChild(section);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
private drawRequestSettings(container: HTMLElement) {
|
|
213
|
+
// This is a simplified version - you can expand based on TabPanel.ts
|
|
214
|
+
const reqConfig = this.tab.getRequestConfig();
|
|
215
|
+
|
|
216
|
+
// Request Method section
|
|
217
|
+
const methodSection = this.createSection("Request Method");
|
|
218
|
+
const methodSelect = this.createSelect(
|
|
219
|
+
["GET", "POST"],
|
|
220
|
+
typeof reqConfig.method === "function" ? "POST" : reqConfig.method,
|
|
221
|
+
);
|
|
222
|
+
methodSelect.setAttribute("data-config", "method");
|
|
223
|
+
methodSection.appendChild(methodSelect);
|
|
224
|
+
container.appendChild(methodSection);
|
|
225
|
+
|
|
226
|
+
// Accept Header sections
|
|
227
|
+
const acceptSection = this.createSection("Accept Header (SELECT/ASK)");
|
|
228
|
+
const acceptSelect = this.createOptionsSelect(AcceptOptionsMap, <string>reqConfig.acceptHeaderSelect);
|
|
229
|
+
acceptSelect.setAttribute("data-config", "acceptHeaderSelect");
|
|
230
|
+
acceptSection.appendChild(acceptSelect);
|
|
231
|
+
container.appendChild(acceptSection);
|
|
232
|
+
|
|
233
|
+
const acceptGraphSection = this.createSection("Accept Header (CONSTRUCT/DESCRIBE)");
|
|
234
|
+
const acceptGraphSelect = this.createOptionsSelect(AcceptHeaderGraphMap, <string>reqConfig.acceptHeaderGraph);
|
|
235
|
+
acceptGraphSelect.setAttribute("data-config", "acceptHeaderGraph");
|
|
236
|
+
acceptGraphSection.appendChild(acceptGraphSelect);
|
|
237
|
+
container.appendChild(acceptGraphSection);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
private createSection(title: string): HTMLElement {
|
|
241
|
+
const section = document.createElement("div");
|
|
242
|
+
addClass(section, "settingsSection");
|
|
243
|
+
const label = document.createElement("div");
|
|
244
|
+
addClass(label, "settingsLabel");
|
|
245
|
+
label.textContent = title;
|
|
246
|
+
section.appendChild(label);
|
|
247
|
+
return section;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
private createSelect(options: string[], selected?: string): HTMLSelectElement {
|
|
251
|
+
const select = document.createElement("select");
|
|
252
|
+
addClass(select, "settingsSelect");
|
|
253
|
+
options.forEach((opt) => {
|
|
254
|
+
const option = document.createElement("option");
|
|
255
|
+
option.value = opt;
|
|
256
|
+
option.textContent = opt;
|
|
257
|
+
if (opt === selected) option.selected = true;
|
|
258
|
+
select.appendChild(option);
|
|
259
|
+
});
|
|
260
|
+
return select;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
private createOptionsSelect(options: { key: string; value: string }[], selected?: string): HTMLSelectElement {
|
|
264
|
+
const select = document.createElement("select");
|
|
265
|
+
addClass(select, "settingsSelect");
|
|
266
|
+
options.forEach((opt) => {
|
|
267
|
+
const option = document.createElement("option");
|
|
268
|
+
option.value = opt.value;
|
|
269
|
+
option.textContent = opt.key;
|
|
270
|
+
if (opt.value === selected) option.selected = true;
|
|
271
|
+
select.appendChild(option);
|
|
272
|
+
});
|
|
273
|
+
return select;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
public open() {
|
|
277
|
+
this.loadSettings();
|
|
278
|
+
addClass(this.modalOverlay, "open");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public close() {
|
|
282
|
+
removeClass(this.modalOverlay, "open");
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private loadSettings() {
|
|
286
|
+
// Load prefix settings
|
|
287
|
+
let prefixes = this.tab.yasgui.persistentConfig.getPrefixes();
|
|
288
|
+
const autoCapture = this.tab.yasgui.persistentConfig.getAutoCaptureEnabled();
|
|
289
|
+
|
|
290
|
+
// Set default prefixes if none exist
|
|
291
|
+
if (!prefixes || prefixes.trim().length === 0) {
|
|
292
|
+
prefixes = `PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\nPREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>`;
|
|
293
|
+
this.tab.yasgui.persistentConfig.setPrefixes(prefixes);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
this.prefixTextarea.value = prefixes;
|
|
297
|
+
this.autoCaptureCheckbox.checked = autoCapture;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private save() {
|
|
301
|
+
// Save prefix settings
|
|
302
|
+
const prefixText = this.prefixTextarea.value;
|
|
303
|
+
const autoCapture = this.autoCaptureCheckbox.checked;
|
|
304
|
+
|
|
305
|
+
// Parse and deduplicate prefixes
|
|
306
|
+
const deduplicated = this.deduplicatePrefixes(prefixText);
|
|
307
|
+
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
308
|
+
this.tab.yasgui.persistentConfig.setAutoCaptureEnabled(autoCapture);
|
|
309
|
+
|
|
310
|
+
// Save request settings
|
|
311
|
+
const requestContent = this.modalContent.querySelector("#request-content");
|
|
312
|
+
if (requestContent) {
|
|
313
|
+
const selects = requestContent.querySelectorAll("select[data-config]");
|
|
314
|
+
const updates: any = {};
|
|
315
|
+
selects.forEach((select) => {
|
|
316
|
+
const config = (select as HTMLSelectElement).getAttribute("data-config");
|
|
317
|
+
if (config) {
|
|
318
|
+
updates[config] = (select as HTMLSelectElement).value;
|
|
319
|
+
}
|
|
320
|
+
});
|
|
321
|
+
this.tab.setRequestConfig(updates);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
this.close();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private deduplicatePrefixes(prefixText: string): string {
|
|
328
|
+
const lines = prefixText.split("\n");
|
|
329
|
+
const seen = new Map<string, string>();
|
|
330
|
+
const result: string[] = [];
|
|
331
|
+
|
|
332
|
+
for (const line of lines) {
|
|
333
|
+
const trimmed = line.trim();
|
|
334
|
+
if (!trimmed) continue;
|
|
335
|
+
|
|
336
|
+
// Extract prefix label from line
|
|
337
|
+
const match = trimmed.match(/^\s*PREFIX\s+(\w+):\s*<(.+)>\s*$/i);
|
|
338
|
+
if (match) {
|
|
339
|
+
const label = match[1].toLowerCase();
|
|
340
|
+
if (!seen.has(label)) {
|
|
341
|
+
seen.set(label, trimmed);
|
|
342
|
+
result.push(trimmed);
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
// Keep non-prefix lines as-is
|
|
346
|
+
result.push(trimmed);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return result.join("\n");
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
private insertPrefixesIntoQuery() {
|
|
354
|
+
const yasqe = this.tab.getYasqe();
|
|
355
|
+
if (!yasqe) return;
|
|
356
|
+
|
|
357
|
+
const savedPrefixes = this.tab.yasgui.persistentConfig.getPrefixes();
|
|
358
|
+
if (!savedPrefixes.trim()) return;
|
|
359
|
+
|
|
360
|
+
// Get current query and find where PREFIX declarations end
|
|
361
|
+
const currentQuery = yasqe.getValue();
|
|
362
|
+
const lines = currentQuery.split("\n");
|
|
363
|
+
|
|
364
|
+
let firstNonPrefixLine = 0;
|
|
365
|
+
for (let i = 0; i < lines.length; i++) {
|
|
366
|
+
const trimmed = lines[i].trim();
|
|
367
|
+
// Skip empty lines and PREFIX lines
|
|
368
|
+
if (trimmed.length > 0 && !trimmed.toUpperCase().startsWith("PREFIX")) {
|
|
369
|
+
firstNonPrefixLine = i;
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// If we didn't find a non-PREFIX line, all lines are PREFIX or empty
|
|
375
|
+
if (firstNonPrefixLine === 0 && lines.length > 0) {
|
|
376
|
+
// Check if there's any content at all
|
|
377
|
+
const hasContent = lines.some((line) => line.trim().length > 0);
|
|
378
|
+
if (
|
|
379
|
+
!hasContent ||
|
|
380
|
+
lines.every((line) => line.trim().length === 0 || line.trim().toUpperCase().startsWith("PREFIX"))
|
|
381
|
+
) {
|
|
382
|
+
firstNonPrefixLine = lines.length;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Keep the rest of the query (non-PREFIX lines)
|
|
387
|
+
const restOfQuery = lines.slice(firstNonPrefixLine).join("\n").trim();
|
|
388
|
+
|
|
389
|
+
// Build new query with saved prefixes + rest of query
|
|
390
|
+
const newQuery = savedPrefixes + (restOfQuery ? "\n\n" + restOfQuery : "");
|
|
391
|
+
yasqe.setValue(newQuery);
|
|
392
|
+
yasqe.focus();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
public capturePrefixesFromQuery() {
|
|
396
|
+
const autoCapture = this.tab.yasgui.persistentConfig.getAutoCaptureEnabled();
|
|
397
|
+
if (!autoCapture) return;
|
|
398
|
+
|
|
399
|
+
const yasqe = this.tab.getYasqe();
|
|
400
|
+
if (!yasqe) return;
|
|
401
|
+
|
|
402
|
+
const queryPrefixes = yasqe.getPrefixesFromQuery();
|
|
403
|
+
if (!queryPrefixes || Object.keys(queryPrefixes).length === 0) return;
|
|
404
|
+
|
|
405
|
+
// Convert query prefixes to text format
|
|
406
|
+
const newPrefixLines: string[] = [];
|
|
407
|
+
for (const [label, uri] of Object.entries(queryPrefixes)) {
|
|
408
|
+
newPrefixLines.push(`PREFIX ${label}: <${uri}>`);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Merge with existing prefixes
|
|
412
|
+
const existingPrefixes = this.tab.yasgui.persistentConfig.getPrefixes();
|
|
413
|
+
const combined = existingPrefixes + "\n" + newPrefixLines.join("\n");
|
|
414
|
+
const deduplicated = this.deduplicatePrefixes(combined);
|
|
415
|
+
|
|
416
|
+
this.tab.yasgui.persistentConfig.setPrefixes(deduplicated);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
public destroy() {
|
|
420
|
+
if (this.modalOverlay && this.modalOverlay.parentNode) {
|
|
421
|
+
this.modalOverlay.parentNode.removeChild(this.modalOverlay);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
package/src/defaults.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Config } from "./";
|
|
2
|
+
import Yasr from "@matdata/yasr";
|
|
3
|
+
import { default as Yasqe } from "@matdata/yasqe";
|
|
4
|
+
import { CatalogueItem } from "./endpointSelect";
|
|
5
|
+
|
|
6
|
+
export default function initialize(): Config<CatalogueItem> {
|
|
7
|
+
return {
|
|
8
|
+
autofocus: true,
|
|
9
|
+
endpointInfo: undefined,
|
|
10
|
+
persistenceId: function (yasgui) {
|
|
11
|
+
//Traverse parents untl we've got an id
|
|
12
|
+
// Get matching parent elements
|
|
13
|
+
var id = "";
|
|
14
|
+
var elem: any = yasgui.rootEl;
|
|
15
|
+
if ((<any>elem).id) id = (<any>elem).id;
|
|
16
|
+
for (; elem && elem !== <any>document; elem = elem.parentNode) {
|
|
17
|
+
if (elem) {
|
|
18
|
+
if ((<any>elem).id) id = (<any>elem).id;
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return "yagui_" + id;
|
|
23
|
+
},
|
|
24
|
+
tabName: "Query",
|
|
25
|
+
corsProxy: undefined,
|
|
26
|
+
persistencyExpire: 60 * 60 * 24 * 30,
|
|
27
|
+
persistenceLabelResponse: "response",
|
|
28
|
+
persistenceLabelConfig: "config",
|
|
29
|
+
yasqe: Yasqe.defaults,
|
|
30
|
+
yasr: Yasr.defaults,
|
|
31
|
+
endpointCatalogueOptions: {
|
|
32
|
+
getData: () => {
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
endpoint: "https://dbpedia.org/sparql",
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
endpoint: "https://query.wikidata.org/bigdata/namespace/wdq/sparql",
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
},
|
|
42
|
+
keys: [],
|
|
43
|
+
renderItem: (data, source) => {
|
|
44
|
+
const contentDiv = document.createElement("div");
|
|
45
|
+
|
|
46
|
+
contentDiv.style.display = "flex";
|
|
47
|
+
contentDiv.style.flexDirection = "column";
|
|
48
|
+
const endpointSpan = document.createElement("span");
|
|
49
|
+
endpointSpan.innerHTML =
|
|
50
|
+
data.matches.endpoint?.reduce(
|
|
51
|
+
(current, object) => (object.highlight ? current + object.text.bold() : current + object.text),
|
|
52
|
+
"",
|
|
53
|
+
) || "";
|
|
54
|
+
contentDiv.appendChild(endpointSpan);
|
|
55
|
+
source.appendChild(contentDiv);
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
copyEndpointOnNewTab: true,
|
|
59
|
+
populateFromUrl: true,
|
|
60
|
+
autoAddOnInit: true,
|
|
61
|
+
requestConfig: Yasqe.defaults.requestConfig,
|
|
62
|
+
contextMenuContainer: undefined,
|
|
63
|
+
};
|
|
64
|
+
}
|