@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/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, '&lt;')
78
+ .replace(/>/g, '&gt;');
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, '&nbsp;')
100
+ .replace(/\t/g, '&nbsp;&nbsp;&nbsp;&nbsp;');
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
+ }
@@ -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,"&amp;").replace(/"/g,"&quot;").replace(/'/g,"&#39;").replace(/</g,"&lt;").replace(/>/g,"&gt;");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,"&nbsp;").replace(/\t/g,"&nbsp;&nbsp;&nbsp;&nbsp;");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}