@wokki20/jspt 2.0.3
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/LICENSE +21 -0
- package/README.md +586 -0
- package/dist/jspt.css +264 -0
- package/dist/jspt.d.ts +49 -0
- package/dist/jspt.js +626 -0
- package/dist/jspt.min.js +1 -0
- package/dist/jspt.module.js +611 -0
- package/package.json +38 -0
- package/src/jspt.css +264 -0
- package/src/jspt.d.ts +49 -0
- package/src/jspt.js +626 -0
- package/src/jspt.module.js +611 -0
package/dist/jspt.js
ADDED
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {Object} PopupOptions
|
|
3
|
+
* @property {'default'} [style='default']
|
|
4
|
+
* @property {'text'|'html'} [content_type='text']
|
|
5
|
+
* @property {string} [header]
|
|
6
|
+
* @property {string} [title]
|
|
7
|
+
* @property {string} [message]
|
|
8
|
+
* @property {string} [content]
|
|
9
|
+
* @property {boolean} [close_on_blur=true]
|
|
10
|
+
* @property {string} [custom_id]
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {Object} ClosePopupOptions
|
|
15
|
+
* @property {string} custom_id
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {Object} ToastOptions
|
|
20
|
+
* @property {'default'|'default-error'|string} [style='default']
|
|
21
|
+
* @property {string} message
|
|
22
|
+
* @property {string} [custom_id]
|
|
23
|
+
* @property {string} [icon_left]
|
|
24
|
+
* @property {'google_material_rounded'|'google_material_outlined'|'svg'|'image'|'text'|'emoji'} [icon_left_type='google_material_rounded']
|
|
25
|
+
* @property {Function} [icon_left_action]
|
|
26
|
+
* @property {string} [icon_right]
|
|
27
|
+
* @property {'google_material_rounded'|'google_material_outlined'|'svg'|'image'|'text'|'emoji'} [icon_right_type='google_material_rounded']
|
|
28
|
+
* @property {Function} [icon_right_action]
|
|
29
|
+
* @property {number} [duration=5000]
|
|
30
|
+
* @property {boolean} [close_on_click=false]
|
|
31
|
+
* @property {Function} [onclick]
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
let debugMode = false;
|
|
35
|
+
|
|
36
|
+
const script = document.createElement('script');
|
|
37
|
+
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js';
|
|
38
|
+
document.head.appendChild(script);
|
|
39
|
+
|
|
40
|
+
const link = document.createElement('link');
|
|
41
|
+
link.rel = 'stylesheet';
|
|
42
|
+
link.href = 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css';
|
|
43
|
+
document.head.appendChild(link);
|
|
44
|
+
|
|
45
|
+
const jspt = {
|
|
46
|
+
/**
|
|
47
|
+
* @param {ClosePopupOptions} options
|
|
48
|
+
* @returns {void}
|
|
49
|
+
*/
|
|
50
|
+
closePopup: function(options) {
|
|
51
|
+
const { custom_id } = options;
|
|
52
|
+
const popup = document.querySelector(`.popup-container#${custom_id}`)
|
|
53
|
+
if (popup) {
|
|
54
|
+
popup.remove();
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* @param {PopupOptions} options
|
|
59
|
+
* @returns {void}
|
|
60
|
+
*/
|
|
61
|
+
makePopup: function(options) {
|
|
62
|
+
const {
|
|
63
|
+
style = 'default',
|
|
64
|
+
content_type = 'text',
|
|
65
|
+
header,
|
|
66
|
+
title,
|
|
67
|
+
message,
|
|
68
|
+
content,
|
|
69
|
+
close_on_blur = true,
|
|
70
|
+
custom_id = Math.random().toString(36).substring(2)
|
|
71
|
+
} = options;
|
|
72
|
+
|
|
73
|
+
const sanitize = (input) => input
|
|
74
|
+
.replace(/&/g, '&')
|
|
75
|
+
.replace(/"/g, '"')
|
|
76
|
+
.replace(/'/g, ''')
|
|
77
|
+
.replace(/</g, '<')
|
|
78
|
+
.replace(/>/g, '>');
|
|
79
|
+
|
|
80
|
+
const createErrorPopupContent = (lineNumber, error_message) => {
|
|
81
|
+
return fetch(location.href)
|
|
82
|
+
.then(res => res.text())
|
|
83
|
+
.then(code => {
|
|
84
|
+
const lines = code.split('\n');
|
|
85
|
+
const start = Math.max(0, lineNumber - 3);
|
|
86
|
+
const end = Math.min(lines.length, lineNumber + 10);
|
|
87
|
+
const snippetLines = lines.slice(start, end);
|
|
88
|
+
|
|
89
|
+
while (snippetLines.length && snippetLines[snippetLines.length - 1].trim() === '') {
|
|
90
|
+
snippetLines.pop();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const snippet = snippetLines.map((line, i) => {
|
|
94
|
+
const actualLine = start + i + 1;
|
|
95
|
+
|
|
96
|
+
if (!/\S/.test(line)) return '';
|
|
97
|
+
|
|
98
|
+
const leadingSpaces = line.match(/^\s*/)[0]
|
|
99
|
+
.replace(/ /g, ' ')
|
|
100
|
+
.replace(/\t/g, ' ');
|
|
101
|
+
const highlighted = hljs.highlight(line.trimStart(), { language: 'javascript' }).value;
|
|
102
|
+
|
|
103
|
+
if (actualLine === lineNumber) {
|
|
104
|
+
return `<span class="popup-error-code-line popup-error-code-line-highlight"><span class="popup-error-code-line-number">${actualLine}</span>${leadingSpaces}${highlighted}</span>`;
|
|
105
|
+
}
|
|
106
|
+
return `<span class="popup-error-code-line"><span class="popup-error-code-line-number">${actualLine}</span>${leadingSpaces}${highlighted}</span>`;
|
|
107
|
+
}).join('');
|
|
108
|
+
|
|
109
|
+
return `
|
|
110
|
+
<p>${error_message}</p>
|
|
111
|
+
<pre class="popup-error-code">
|
|
112
|
+
<div class="popup-error-code-header"><p class="popup-error-code-header-file">${location.href.split('/').pop().split('?')[0]}</p></div>
|
|
113
|
+
<code class="hljs language-javascript">${snippet}</code>
|
|
114
|
+
</pre>
|
|
115
|
+
`;
|
|
116
|
+
})
|
|
117
|
+
.catch(err => {
|
|
118
|
+
console.error('Failed to fetch source:', err);
|
|
119
|
+
return `<p>Failed to fetch source code</p>`;
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const popup = document.createElement('div');
|
|
124
|
+
popup.classList.add('popup');
|
|
125
|
+
|
|
126
|
+
if (content_type === 'text') {
|
|
127
|
+
if (content) {
|
|
128
|
+
const err = new Error();
|
|
129
|
+
const stackLines = err.stack.split('\n');
|
|
130
|
+
const callerLine = stackLines[2] || '';
|
|
131
|
+
const lineMatch = callerLine.match(/:(\d+):\d+\)?$/);
|
|
132
|
+
const lineNumber = lineMatch ? parseInt(lineMatch[1]) : null;
|
|
133
|
+
|
|
134
|
+
const errorMsg = `Error on line ${lineNumber}: Cannot use content when content_type is text in jspt.makePopup(), Please make sure content is not provided`;
|
|
135
|
+
|
|
136
|
+
if (lineNumber) {
|
|
137
|
+
createErrorPopupContent(lineNumber, errorMsg).then(popupContent => {
|
|
138
|
+
jspt.makeToast({
|
|
139
|
+
style: "default-error",
|
|
140
|
+
message: errorMsg,
|
|
141
|
+
duration: -1,
|
|
142
|
+
close_on_click: true,
|
|
143
|
+
icon_left: "unknown_document",
|
|
144
|
+
icon_left_type: "google_material_rounded",
|
|
145
|
+
icon_left_action: () => {
|
|
146
|
+
jspt.makePopup({
|
|
147
|
+
content_type: "html",
|
|
148
|
+
header: "Error",
|
|
149
|
+
content: popupContent
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
} else {
|
|
155
|
+
jspt.makeToast({
|
|
156
|
+
style: "default-error",
|
|
157
|
+
message: errorMsg,
|
|
158
|
+
duration: -1,
|
|
159
|
+
close_on_click: true
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} else if (content_type === 'html') {
|
|
164
|
+
if (message) {
|
|
165
|
+
const stackLines = new Error().stack.split('\n');
|
|
166
|
+
const callerLine = stackLines[2] || '';
|
|
167
|
+
const lineMatch = callerLine.match(/:(\d+):\d+\)?$/);
|
|
168
|
+
const lineNumber = lineMatch ? parseInt(lineMatch[1]) : null;
|
|
169
|
+
|
|
170
|
+
const errorMsg = `Error on line ${lineNumber}: Cannot use message when content_type is html in jspt.makePopup(), Please make sure message is not provided`;
|
|
171
|
+
|
|
172
|
+
if (lineNumber) {
|
|
173
|
+
createErrorPopupContent(lineNumber, errorMsg).then(popupContent => {
|
|
174
|
+
jspt.makeToast({
|
|
175
|
+
style: "default-error",
|
|
176
|
+
message: errorMsg,
|
|
177
|
+
duration: -1,
|
|
178
|
+
close_on_click: true,
|
|
179
|
+
icon_left: "unknown_document",
|
|
180
|
+
icon_left_type: "google_material_rounded",
|
|
181
|
+
icon_left_action: () => {
|
|
182
|
+
jspt.makePopup({
|
|
183
|
+
content_type: "html",
|
|
184
|
+
header: "Error",
|
|
185
|
+
content: popupContent
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
} else {
|
|
191
|
+
jspt.makeToast({
|
|
192
|
+
style: "default-error",
|
|
193
|
+
message: errorMsg,
|
|
194
|
+
duration: -1,
|
|
195
|
+
close_on_click: true
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (title) {
|
|
200
|
+
const stackLines = new Error().stack.split('\n');
|
|
201
|
+
const callerLine = stackLines[2] || '';
|
|
202
|
+
const lineMatch = callerLine.match(/:(\d+):\d+\)?$/);
|
|
203
|
+
const lineNumber = lineMatch ? parseInt(lineMatch[1]) : null;
|
|
204
|
+
|
|
205
|
+
const errorMsg = `Error on line ${lineNumber}: Cannot use title when content_type is html in jspt.makePopup(), Please make sure title is not provided`;
|
|
206
|
+
|
|
207
|
+
if (lineNumber) {
|
|
208
|
+
createErrorPopupContent(lineNumber, errorMsg).then(popupContent => {
|
|
209
|
+
jspt.makeToast({
|
|
210
|
+
style: "default-error",
|
|
211
|
+
message: errorMsg,
|
|
212
|
+
duration: -1,
|
|
213
|
+
close_on_click: true,
|
|
214
|
+
icon_left: "unknown_document",
|
|
215
|
+
icon_left_type: "google_material_rounded",
|
|
216
|
+
icon_left_action: () => {
|
|
217
|
+
jspt.makePopup({
|
|
218
|
+
content_type: "html",
|
|
219
|
+
header: "Error",
|
|
220
|
+
content: popupContent
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
} else {
|
|
226
|
+
jspt.makeToast({
|
|
227
|
+
style: "default-error",
|
|
228
|
+
message: errorMsg,
|
|
229
|
+
duration: -1,
|
|
230
|
+
close_on_click: true
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const popupHeader = document.createElement('div');
|
|
237
|
+
popupHeader.classList.add('popup-header');
|
|
238
|
+
|
|
239
|
+
const popupHeaderTitle = document.createElement('p');
|
|
240
|
+
popupHeaderTitle.innerText = header || '';
|
|
241
|
+
popupHeader.appendChild(popupHeaderTitle);
|
|
242
|
+
|
|
243
|
+
const popupHeaderClose = document.createElement('span');
|
|
244
|
+
popupHeaderClose.classList.add('popup-header-close', 'material-symbols-rounded');
|
|
245
|
+
popupHeaderClose.innerText = 'close';
|
|
246
|
+
popupHeaderClose.addEventListener('click', () => {
|
|
247
|
+
popupContainer.remove();
|
|
248
|
+
});
|
|
249
|
+
popupHeader.appendChild(popupHeaderClose);
|
|
250
|
+
|
|
251
|
+
popup.appendChild(popupHeader);
|
|
252
|
+
|
|
253
|
+
const popupContent = document.createElement('div');
|
|
254
|
+
popupContent.classList.add('popup-content');
|
|
255
|
+
|
|
256
|
+
if (content_type === 'text') {
|
|
257
|
+
if (title) {
|
|
258
|
+
const popupTitle = document.createElement('h3');
|
|
259
|
+
popupTitle.innerText = title;
|
|
260
|
+
popupContent.appendChild(popupTitle);
|
|
261
|
+
}
|
|
262
|
+
if (message) {
|
|
263
|
+
const popupMessage = document.createElement('p');
|
|
264
|
+
popupMessage.innerText = message;
|
|
265
|
+
popupContent.appendChild(popupMessage);
|
|
266
|
+
}
|
|
267
|
+
} else if (content_type === 'html') {
|
|
268
|
+
popupContent.innerHTML = content || '';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
popup.appendChild(popupContent);
|
|
272
|
+
|
|
273
|
+
const popupContainer = document.createElement('div');
|
|
274
|
+
popupContainer.classList.add('popup-container');
|
|
275
|
+
popupContainer.appendChild(popup);
|
|
276
|
+
popupContainer.id = custom_id;
|
|
277
|
+
|
|
278
|
+
if (close_on_blur) {
|
|
279
|
+
popupContainer.addEventListener('click', (e) => {
|
|
280
|
+
if (e.target === popupContainer) {
|
|
281
|
+
popupContainer.remove();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
document.body.appendChild(popupContainer);
|
|
287
|
+
},
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* @param {ToastOptions} options
|
|
291
|
+
* @returns {void}
|
|
292
|
+
*/
|
|
293
|
+
makeToast: function(options) {
|
|
294
|
+
const {
|
|
295
|
+
style = 'default',
|
|
296
|
+
message,
|
|
297
|
+
custom_id,
|
|
298
|
+
icon_left,
|
|
299
|
+
icon_left_type = 'google_material_rounded',
|
|
300
|
+
icon_left_action,
|
|
301
|
+
icon_right,
|
|
302
|
+
icon_right_type = 'google_material_rounded',
|
|
303
|
+
icon_right_action,
|
|
304
|
+
duration = 5000,
|
|
305
|
+
close_on_click = false,
|
|
306
|
+
onclick
|
|
307
|
+
} = options;
|
|
308
|
+
|
|
309
|
+
let container = document.querySelector('.toast-container');
|
|
310
|
+
if (!container) {
|
|
311
|
+
container = document.createElement('div');
|
|
312
|
+
container.classList.add('toast-container');
|
|
313
|
+
document.body.appendChild(container);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
const existingToast = custom_id ? document.getElementById(custom_id) : null;
|
|
317
|
+
if (existingToast) {
|
|
318
|
+
existingToast.remove();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const toast = document.createElement('div');
|
|
322
|
+
toast.classList.add('toast');
|
|
323
|
+
if (custom_id) toast.id = custom_id;
|
|
324
|
+
|
|
325
|
+
if (style.startsWith('toast-')) {
|
|
326
|
+
toast.classList.add(style);
|
|
327
|
+
} else {
|
|
328
|
+
toast.classList.add(`toast-${style}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (icon_left) {
|
|
332
|
+
const iconLeftElement = document.createElement('span');
|
|
333
|
+
iconLeftElement.classList.add('toast-icon');
|
|
334
|
+
|
|
335
|
+
if (icon_left_action) {
|
|
336
|
+
iconLeftElement.classList.add('action');
|
|
337
|
+
iconLeftElement.style.setProperty('--cursor', 'pointer');
|
|
338
|
+
iconLeftElement.addEventListener('click', (e) => {
|
|
339
|
+
e.stopPropagation();
|
|
340
|
+
icon_left_action();
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
switch (icon_left_type) {
|
|
345
|
+
case 'google_material_rounded':
|
|
346
|
+
iconLeftElement.classList.add('material-symbols-rounded');
|
|
347
|
+
iconLeftElement.innerText = icon_left;
|
|
348
|
+
break;
|
|
349
|
+
case 'google_material_outlined':
|
|
350
|
+
iconLeftElement.classList.add('material-symbols-outlined');
|
|
351
|
+
iconLeftElement.innerText = icon_left;
|
|
352
|
+
break;
|
|
353
|
+
case 'svg':
|
|
354
|
+
iconLeftElement.innerHTML = icon_left;
|
|
355
|
+
break;
|
|
356
|
+
case 'image':
|
|
357
|
+
const img = document.createElement('img');
|
|
358
|
+
img.src = icon_left;
|
|
359
|
+
img.classList.add('toast-icon-image');
|
|
360
|
+
if (icon_left_action) img.classList.add('action');
|
|
361
|
+
iconLeftElement.appendChild(img);
|
|
362
|
+
break;
|
|
363
|
+
case 'text':
|
|
364
|
+
iconLeftElement.innerText = icon_left;
|
|
365
|
+
break;
|
|
366
|
+
case 'emoji':
|
|
367
|
+
iconLeftElement.innerText = icon_left;
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
toast.appendChild(iconLeftElement);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const toastText = document.createElement('span');
|
|
375
|
+
toastText.classList.add('toast-text');
|
|
376
|
+
toastText.innerHTML = message;
|
|
377
|
+
toast.appendChild(toastText);
|
|
378
|
+
|
|
379
|
+
if (icon_right) {
|
|
380
|
+
const iconRightElement = document.createElement('span');
|
|
381
|
+
iconRightElement.classList.add('toast-icon');
|
|
382
|
+
|
|
383
|
+
if (icon_right_action) {
|
|
384
|
+
iconRightElement.classList.add('action');
|
|
385
|
+
iconRightElement.style.setProperty('--cursor', 'pointer');
|
|
386
|
+
iconRightElement.addEventListener('click', (e) => {
|
|
387
|
+
e.stopPropagation();
|
|
388
|
+
icon_right_action();
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
switch (icon_right_type) {
|
|
393
|
+
case 'google_material_rounded':
|
|
394
|
+
iconRightElement.classList.add('material-symbols-rounded');
|
|
395
|
+
iconRightElement.innerText = icon_right;
|
|
396
|
+
break;
|
|
397
|
+
case 'google_material_outlined':
|
|
398
|
+
iconRightElement.classList.add('material-symbols-outlined');
|
|
399
|
+
iconRightElement.innerText = icon_right;
|
|
400
|
+
break;
|
|
401
|
+
case 'svg':
|
|
402
|
+
iconRightElement.innerHTML = icon_right;
|
|
403
|
+
break;
|
|
404
|
+
case 'image':
|
|
405
|
+
const img = document.createElement('img');
|
|
406
|
+
img.src = icon_right;
|
|
407
|
+
img.classList.add('toast-icon-image');
|
|
408
|
+
if (icon_right_action) img.classList.add('action');
|
|
409
|
+
iconRightElement.appendChild(img);
|
|
410
|
+
break;
|
|
411
|
+
case 'text':
|
|
412
|
+
iconRightElement.innerText = icon_right;
|
|
413
|
+
break;
|
|
414
|
+
case 'emoji':
|
|
415
|
+
iconRightElement.innerText = icon_right;
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
toast.appendChild(iconRightElement);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (duration > 0) {
|
|
423
|
+
const toastDurationBar = document.createElement('div');
|
|
424
|
+
toastDurationBar.classList.add('toast-duration-bar');
|
|
425
|
+
toast.appendChild(toastDurationBar);
|
|
426
|
+
|
|
427
|
+
setTimeout(() => {
|
|
428
|
+
toastDurationBar.style.width = '100%';
|
|
429
|
+
toastDurationBar.style.transition = `width ${duration}ms linear`;
|
|
430
|
+
}, 10);
|
|
431
|
+
|
|
432
|
+
setTimeout(() => {
|
|
433
|
+
removeToast(toast);
|
|
434
|
+
}, duration);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (close_on_click) {
|
|
438
|
+
toast.style.setProperty('--cursor', 'pointer');
|
|
439
|
+
toast.addEventListener('click', (e) => {
|
|
440
|
+
if (e.target.classList.contains('action')) return;
|
|
441
|
+
removeToast(toast);
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
container.appendChild(toast);
|
|
446
|
+
|
|
447
|
+
if (onclick) {
|
|
448
|
+
toast.style.setProperty('--cursor', 'pointer');
|
|
449
|
+
toast.addEventListener('click', (e) => {
|
|
450
|
+
if (e.target.classList.contains('action')) return;
|
|
451
|
+
onclick();
|
|
452
|
+
updateToasts();
|
|
453
|
+
container.dispatchEvent(new CustomEvent('mouseleave'));
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function updateToasts() {
|
|
458
|
+
const toasts = Array.from(container.querySelectorAll('.toast'));
|
|
459
|
+
if (!toasts.length) return;
|
|
460
|
+
|
|
461
|
+
const cs = getComputedStyle(container);
|
|
462
|
+
const gap = parseFloat(cs.gap || cs.rowGap || '0') || 0;
|
|
463
|
+
|
|
464
|
+
const originalHeights = toasts.map(t => t.getBoundingClientRect().height);
|
|
465
|
+
|
|
466
|
+
const lastIndex = toasts.length - 1;
|
|
467
|
+
|
|
468
|
+
toasts.forEach((t, i) => {
|
|
469
|
+
t.style.zIndex = String(100 + i);
|
|
470
|
+
t.classList.remove('stacked');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
if (toasts.length === 1) {
|
|
474
|
+
toasts[0].style.setProperty('--translate', '0px');
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
for (let i = 0; i < toasts.length; i++) {
|
|
479
|
+
const toast = toasts[i];
|
|
480
|
+
|
|
481
|
+
if (i === lastIndex) {
|
|
482
|
+
toast.style.setProperty('--translate', '0px');
|
|
483
|
+
} else if (i === lastIndex - 1) {
|
|
484
|
+
const translate = originalHeights[i] + gap - 5;
|
|
485
|
+
toast.style.setProperty('--translate', `${translate}px`);
|
|
486
|
+
toast.style.setProperty('--scale', '0.97');
|
|
487
|
+
toast.classList.add('stacked');
|
|
488
|
+
} else {
|
|
489
|
+
let delta = 0;
|
|
490
|
+
for (let k = i; k <= lastIndex - 1; k++) {
|
|
491
|
+
delta += originalHeights[k] + gap - 2;
|
|
492
|
+
}
|
|
493
|
+
toast.style.setProperty('--translate', `${delta}px`);
|
|
494
|
+
toast.style.setProperty('--scale', '0.97');
|
|
495
|
+
toast.classList.add('stacked');
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function removeToast(toast) {
|
|
501
|
+
const toasts = Array.from(container.querySelectorAll('.toast'));
|
|
502
|
+
const index = toasts.indexOf(toast);
|
|
503
|
+
|
|
504
|
+
const toastRects = toasts.map(t => t.getBoundingClientRect());
|
|
505
|
+
|
|
506
|
+
toasts.forEach((el, i) => {
|
|
507
|
+
if (i < index) {
|
|
508
|
+
el.style.transition = 'transform 0.25s ease';
|
|
509
|
+
el.style.transform = `translateY(${toastRects[index].height + 8}px)`;
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
toast.style.transition = 'transform 0.25s ease, opacity 0.25s ease';
|
|
514
|
+
toast.style.transform = `translateX(100%)`;
|
|
515
|
+
toast.style.opacity = '0';
|
|
516
|
+
|
|
517
|
+
toast.addEventListener('transitionend', () => {
|
|
518
|
+
toast.remove();
|
|
519
|
+
toasts.forEach(el => {
|
|
520
|
+
el.style.transition = '';
|
|
521
|
+
el.style.transform = '';
|
|
522
|
+
});
|
|
523
|
+
}, { once: true });
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
let toastTimeout;
|
|
527
|
+
|
|
528
|
+
function expandToastsOnContainerHover(container) {
|
|
529
|
+
const toasts = Array.from(container.querySelectorAll('.toast'));
|
|
530
|
+
|
|
531
|
+
const expandedHeights = toasts.map((toast) => {
|
|
532
|
+
const clone = toast.cloneNode(true);
|
|
533
|
+
clone.style.position = 'absolute';
|
|
534
|
+
clone.style.visibility = 'hidden';
|
|
535
|
+
clone.style.height = 'auto';
|
|
536
|
+
clone.style.whiteSpace = 'normal';
|
|
537
|
+
clone.style.overflow = 'visible';
|
|
538
|
+
clone.style.textOverflow = 'unset';
|
|
539
|
+
clone.style.wordBreak = 'break-word';
|
|
540
|
+
container.appendChild(clone);
|
|
541
|
+
const height = clone.offsetHeight;
|
|
542
|
+
container.removeChild(clone);
|
|
543
|
+
return height;
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
const expandAll = () => {
|
|
547
|
+
toasts.forEach((toast, i) => {
|
|
548
|
+
const textHeight = toast.querySelector('.toast-text').scrollHeight;
|
|
549
|
+
const height = textHeight > expandedHeights[i] ? expandedHeights[i] + 20 : expandedHeights[i] - 20;
|
|
550
|
+
toast.style.height = `${height}px`;
|
|
551
|
+
});
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
const resetAll = () => {
|
|
555
|
+
toasts.forEach((toast) => toast.style.height = '');
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
container.addEventListener('mouseenter', () => {
|
|
559
|
+
if (toastTimeout) {
|
|
560
|
+
clearTimeout(toastTimeout);
|
|
561
|
+
toastTimeout = null;
|
|
562
|
+
}
|
|
563
|
+
expandAll();
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
container.addEventListener('mouseleave', () => {
|
|
567
|
+
resetAll();
|
|
568
|
+
if (toastTimeout) {
|
|
569
|
+
clearTimeout(toastTimeout);
|
|
570
|
+
}
|
|
571
|
+
toastTimeout = setTimeout(() => {
|
|
572
|
+
toastTimeout = null;
|
|
573
|
+
}, 300);
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
expandToastsOnContainerHover(container);
|
|
578
|
+
updateToasts();
|
|
579
|
+
|
|
580
|
+
window.addEventListener('resize', updateToasts);
|
|
581
|
+
|
|
582
|
+
const mo = new MutationObserver(updateToasts);
|
|
583
|
+
mo.observe(container, { childList: true });
|
|
584
|
+
}
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
const currentScript = document.currentScript;
|
|
588
|
+
const params = currentScript ? new URL(currentScript.src).searchParams : new URLSearchParams();
|
|
589
|
+
|
|
590
|
+
window.addEventListener('DOMContentLoaded', () => {
|
|
591
|
+
if (params.get('debug') === 'true') {
|
|
592
|
+
debugMode = true;
|
|
593
|
+
|
|
594
|
+
if (currentScript && !currentScript.src.includes('https://cdn.wokki20.nl/content')) {
|
|
595
|
+
console.log('Debug mode disabled.');
|
|
596
|
+
console.log('This script is not hosted on cdn.wokki20.nl, debug mode is disabled. Please use https://cdn.wokki20.nl/content/jspt_latest/jspt.js instead.');
|
|
597
|
+
console.log('If you are hosting this script yourself, you can manually enable debug mode by changing debugMode to true in the script.');
|
|
598
|
+
debugMode = false;
|
|
599
|
+
|
|
600
|
+
if (typeof jspt !== 'undefined') {
|
|
601
|
+
jspt.makeToast({
|
|
602
|
+
style: "default-error",
|
|
603
|
+
message: "This script is not hosted on cdn.wokki20.nl, please check console for more information.",
|
|
604
|
+
duration: -1,
|
|
605
|
+
close_on_click: true
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
console.log('Debug mode enabled');
|
|
612
|
+
|
|
613
|
+
if (typeof jspt !== 'undefined') {
|
|
614
|
+
jspt.makeToast({
|
|
615
|
+
style: "default",
|
|
616
|
+
message: "Debug mode enabled, do not use in production",
|
|
617
|
+
duration: -1,
|
|
618
|
+
close_on_click: true
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
if (typeof window !== 'undefined') {
|
|
625
|
+
window.jspt = jspt;
|
|
626
|
+
}
|
package/dist/jspt.min.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
let debugMode=false;const script=document.createElement("script");script.src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js";document.head.appendChild(script);const link=document.createElement("link");link.rel="stylesheet";link.href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css";document.head.appendChild(link);const jspt={closePopup:function(options){const{custom_id:custom_id}=options;const popup=document.querySelector(`.popup-container#${custom_id}`);if(popup){popup.remove()}},makePopup:function(options){const{style:style="default",content_type:content_type="text",header:header,title:title,message:message,content:content,close_on_blur:close_on_blur=true,custom_id:custom_id=Math.random().toString(36).substring(2)}=options;const sanitize=input=>input.replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(/</g,"<").replace(/>/g,">");const createErrorPopupContent=(lineNumber,error_message)=>fetch(location.href).then(res=>res.text()).then(code=>{const lines=code.split("\n");const start=Math.max(0,lineNumber-3);const end=Math.min(lines.length,lineNumber+10);const snippetLines=lines.slice(start,end);while(snippetLines.length&&snippetLines[snippetLines.length-1].trim()===""){snippetLines.pop()}const snippet=snippetLines.map((line,i)=>{const actualLine=start+i+1;if(!/\S/.test(line))return"";const leadingSpaces=line.match(/^\s*/)[0].replace(/ /g," ").replace(/\t/g," ");const highlighted=hljs.highlight(line.trimStart(),{language:"javascript"}).value;if(actualLine===lineNumber){return`<span class="popup-error-code-line popup-error-code-line-highlight"><span class="popup-error-code-line-number">${actualLine}</span>${leadingSpaces}${highlighted}</span>`}return`<span class="popup-error-code-line"><span class="popup-error-code-line-number">${actualLine}</span>${leadingSpaces}${highlighted}</span>`}).join("");return`\n <p>${error_message}</p>\n <pre class="popup-error-code">\n <div class="popup-error-code-header"><p class="popup-error-code-header-file">${location.href.split("/").pop().split("?")[0]}</p></div>\n <code class="hljs language-javascript">${snippet}</code>\n </pre>\n `}).catch(err=>{console.error("Failed to fetch source:",err);return`<p>Failed to fetch source code</p>`});const popup=document.createElement("div");popup.classList.add("popup");if(content_type==="text"){if(content){const err=new Error;const stackLines=err.stack.split("\n");const callerLine=stackLines[2]||"";const lineMatch=callerLine.match(/:(\d+):\d+\)?$/);const lineNumber=lineMatch?parseInt(lineMatch[1]):null;const errorMsg=`Error on line ${lineNumber}: Cannot use content when content_type is text in jspt.makePopup(), Please make sure content is not provided`;if(lineNumber){createErrorPopupContent(lineNumber,errorMsg).then(popupContent=>{jspt.makeToast({style:"default-error",message:errorMsg,duration:-1,close_on_click:true,icon_left:"unknown_document",icon_left_type:"google_material_rounded",icon_left_action:()=>{jspt.makePopup({content_type:"html",header:"Error",content:popupContent})}})})}else{jspt.makeToast({style:"default-error",message:errorMsg,duration:-1,close_on_click:true})}}}else if(content_type==="html"){if(message){const stackLines=(new Error).stack.split("\n");const callerLine=stackLines[2]||"";const lineMatch=callerLine.match(/:(\d+):\d+\)?$/);const lineNumber=lineMatch?parseInt(lineMatch[1]):null;const errorMsg=`Error on line ${lineNumber}: Cannot use message when content_type is html in jspt.makePopup(), Please make sure message is not provided`;if(lineNumber){createErrorPopupContent(lineNumber,errorMsg).then(popupContent=>{jspt.makeToast({style:"default-error",message:errorMsg,duration:-1,close_on_click:true,icon_left:"unknown_document",icon_left_type:"google_material_rounded",icon_left_action:()=>{jspt.makePopup({content_type:"html",header:"Error",content:popupContent})}})})}else{jspt.makeToast({style:"default-error",message:errorMsg,duration:-1,close_on_click:true})}}if(title){const stackLines=(new Error).stack.split("\n");const callerLine=stackLines[2]||"";const lineMatch=callerLine.match(/:(\d+):\d+\)?$/);const lineNumber=lineMatch?parseInt(lineMatch[1]):null;const errorMsg=`Error on line ${lineNumber}: Cannot use title when content_type is html in jspt.makePopup(), Please make sure title is not provided`;if(lineNumber){createErrorPopupContent(lineNumber,errorMsg).then(popupContent=>{jspt.makeToast({style:"default-error",message:errorMsg,duration:-1,close_on_click:true,icon_left:"unknown_document",icon_left_type:"google_material_rounded",icon_left_action:()=>{jspt.makePopup({content_type:"html",header:"Error",content:popupContent})}})})}else{jspt.makeToast({style:"default-error",message:errorMsg,duration:-1,close_on_click:true})}}}const popupHeader=document.createElement("div");popupHeader.classList.add("popup-header");const popupHeaderTitle=document.createElement("p");popupHeaderTitle.innerText=header||"";popupHeader.appendChild(popupHeaderTitle);const popupHeaderClose=document.createElement("span");popupHeaderClose.classList.add("popup-header-close","material-symbols-rounded");popupHeaderClose.innerText="close";popupHeaderClose.addEventListener("click",()=>{popupContainer.remove()});popupHeader.appendChild(popupHeaderClose);popup.appendChild(popupHeader);const popupContent=document.createElement("div");popupContent.classList.add("popup-content");if(content_type==="text"){if(title){const popupTitle=document.createElement("h3");popupTitle.innerText=title;popupContent.appendChild(popupTitle)}if(message){const popupMessage=document.createElement("p");popupMessage.innerText=message;popupContent.appendChild(popupMessage)}}else if(content_type==="html"){popupContent.innerHTML=content||""}popup.appendChild(popupContent);const popupContainer=document.createElement("div");popupContainer.classList.add("popup-container");popupContainer.appendChild(popup);popupContainer.id=custom_id;if(close_on_blur){popupContainer.addEventListener("click",e=>{if(e.target===popupContainer){popupContainer.remove()}})}document.body.appendChild(popupContainer)},makeToast:function(options){const{style:style="default",message:message,custom_id:custom_id,icon_left:icon_left,icon_left_type:icon_left_type="google_material_rounded",icon_left_action:icon_left_action,icon_right:icon_right,icon_right_type:icon_right_type="google_material_rounded",icon_right_action:icon_right_action,duration:duration=5e3,close_on_click:close_on_click=false,onclick:onclick}=options;let container=document.querySelector(".toast-container");if(!container){container=document.createElement("div");container.classList.add("toast-container");document.body.appendChild(container)}const existingToast=custom_id?document.getElementById(custom_id):null;if(existingToast){existingToast.remove()}const toast=document.createElement("div");toast.classList.add("toast");if(custom_id)toast.id=custom_id;if(style.startsWith("toast-")){toast.classList.add(style)}else{toast.classList.add(`toast-${style}`)}if(icon_left){const iconLeftElement=document.createElement("span");iconLeftElement.classList.add("toast-icon");if(icon_left_action){iconLeftElement.classList.add("action");iconLeftElement.style.setProperty("--cursor","pointer");iconLeftElement.addEventListener("click",e=>{e.stopPropagation();icon_left_action()})}switch(icon_left_type){case"google_material_rounded":iconLeftElement.classList.add("material-symbols-rounded");iconLeftElement.innerText=icon_left;break;case"google_material_outlined":iconLeftElement.classList.add("material-symbols-outlined");iconLeftElement.innerText=icon_left;break;case"svg":iconLeftElement.innerHTML=icon_left;break;case"image":const img=document.createElement("img");img.src=icon_left;img.classList.add("toast-icon-image");if(icon_left_action)img.classList.add("action");iconLeftElement.appendChild(img);break;case"text":iconLeftElement.innerText=icon_left;break;case"emoji":iconLeftElement.innerText=icon_left;break}toast.appendChild(iconLeftElement)}const toastText=document.createElement("span");toastText.classList.add("toast-text");toastText.innerHTML=message;toast.appendChild(toastText);if(icon_right){const iconRightElement=document.createElement("span");iconRightElement.classList.add("toast-icon");if(icon_right_action){iconRightElement.classList.add("action");iconRightElement.style.setProperty("--cursor","pointer");iconRightElement.addEventListener("click",e=>{e.stopPropagation();icon_right_action()})}switch(icon_right_type){case"google_material_rounded":iconRightElement.classList.add("material-symbols-rounded");iconRightElement.innerText=icon_right;break;case"google_material_outlined":iconRightElement.classList.add("material-symbols-outlined");iconRightElement.innerText=icon_right;break;case"svg":iconRightElement.innerHTML=icon_right;break;case"image":const img=document.createElement("img");img.src=icon_right;img.classList.add("toast-icon-image");if(icon_right_action)img.classList.add("action");iconRightElement.appendChild(img);break;case"text":iconRightElement.innerText=icon_right;break;case"emoji":iconRightElement.innerText=icon_right;break}toast.appendChild(iconRightElement)}if(duration>0){const toastDurationBar=document.createElement("div");toastDurationBar.classList.add("toast-duration-bar");toast.appendChild(toastDurationBar);setTimeout(()=>{toastDurationBar.style.width="100%";toastDurationBar.style.transition=`width ${duration}ms linear`},10);setTimeout(()=>{removeToast(toast)},duration)}if(close_on_click){toast.style.setProperty("--cursor","pointer");toast.addEventListener("click",e=>{if(e.target.classList.contains("action"))return;removeToast(toast)})}container.appendChild(toast);if(onclick){toast.style.setProperty("--cursor","pointer");toast.addEventListener("click",e=>{if(e.target.classList.contains("action"))return;onclick();updateToasts();container.dispatchEvent(new CustomEvent("mouseleave"))})}function updateToasts(){const toasts=Array.from(container.querySelectorAll(".toast"));if(!toasts.length)return;const cs=getComputedStyle(container);const gap=parseFloat(cs.gap||cs.rowGap||"0")||0;const originalHeights=toasts.map(t=>t.getBoundingClientRect().height);const lastIndex=toasts.length-1;toasts.forEach((t,i)=>{t.style.zIndex=String(100+i);t.classList.remove("stacked")});if(toasts.length===1){toasts[0].style.setProperty("--translate","0px");return}for(let i=0;i<toasts.length;i++){const toast=toasts[i];if(i===lastIndex){toast.style.setProperty("--translate","0px")}else if(i===lastIndex-1){const translate=originalHeights[i]+gap-5;toast.style.setProperty("--translate",`${translate}px`);toast.style.setProperty("--scale","0.97");toast.classList.add("stacked")}else{let delta=0;for(let k=i;k<=lastIndex-1;k++){delta+=originalHeights[k]+gap-2}toast.style.setProperty("--translate",`${delta}px`);toast.style.setProperty("--scale","0.97");toast.classList.add("stacked")}}}function removeToast(toast){const toasts=Array.from(container.querySelectorAll(".toast"));const index=toasts.indexOf(toast);const toastRects=toasts.map(t=>t.getBoundingClientRect());toasts.forEach((el,i)=>{if(i<index){el.style.transition="transform 0.25s ease";el.style.transform=`translateY(${toastRects[index].height+8}px)`}});toast.style.transition="transform 0.25s ease, opacity 0.25s ease";toast.style.transform=`translateX(100%)`;toast.style.opacity="0";toast.addEventListener("transitionend",()=>{toast.remove();toasts.forEach(el=>{el.style.transition="";el.style.transform=""})},{once:true})}let toastTimeout;function expandToastsOnContainerHover(container){const toasts=Array.from(container.querySelectorAll(".toast"));const expandedHeights=toasts.map(toast=>{const clone=toast.cloneNode(true);clone.style.position="absolute";clone.style.visibility="hidden";clone.style.height="auto";clone.style.whiteSpace="normal";clone.style.overflow="visible";clone.style.textOverflow="unset";clone.style.wordBreak="break-word";container.appendChild(clone);const height=clone.offsetHeight;container.removeChild(clone);return height});const expandAll=()=>{toasts.forEach((toast,i)=>{const textHeight=toast.querySelector(".toast-text").scrollHeight;const height=textHeight>expandedHeights[i]?expandedHeights[i]+20:expandedHeights[i]-20;toast.style.height=`${height}px`})};const resetAll=()=>{toasts.forEach(toast=>toast.style.height="")};container.addEventListener("mouseenter",()=>{if(toastTimeout){clearTimeout(toastTimeout);toastTimeout=null}expandAll()});container.addEventListener("mouseleave",()=>{resetAll();if(toastTimeout){clearTimeout(toastTimeout)}toastTimeout=setTimeout(()=>{toastTimeout=null},300)})}expandToastsOnContainerHover(container);updateToasts();window.addEventListener("resize",updateToasts);const mo=new MutationObserver(updateToasts);mo.observe(container,{childList:true})}};const currentScript=document.currentScript;const params=currentScript?new URL(currentScript.src).searchParams:new URLSearchParams;window.addEventListener("DOMContentLoaded",()=>{if(params.get("debug")==="true"){debugMode=true;if(currentScript&&!currentScript.src.includes("https://cdn.wokki20.nl/content")){console.log("Debug mode disabled.");console.log("This script is not hosted on cdn.wokki20.nl, debug mode is disabled. Please use https://cdn.wokki20.nl/content/jspt_latest/jspt.js instead.");console.log("If you are hosting this script yourself, you can manually enable debug mode by changing debugMode to true in the script.");debugMode=false;if(typeof jspt!=="undefined"){jspt.makeToast({style:"default-error",message:"This script is not hosted on cdn.wokki20.nl, please check console for more information.",duration:-1,close_on_click:true})}return}console.log("Debug mode enabled");if(typeof jspt!=="undefined"){jspt.makeToast({style:"default",message:"Debug mode enabled, do not use in production",duration:-1,close_on_click:true})}}});if(typeof window!=="undefined"){window.jspt=jspt}
|