PageSourceSearch

https://pupwife.com/scripts/desktop/citrusShellDialogs.js

js pupwife.com collected 2026-09-30 15:04:53 UTC 6,386 bytes, 164 lines download raw bytes

1/**
2 * CitrusOS themed prompt / confirm / alert dialogs (replaces window.prompt & confirm on shell).
3 */
4(function (global) {
5    'use strict';
6
7    var activeOverlay = null;
8    var zBase = 125000;
9    var zCounter = 0;
10
11    function escapeHtml(str) {
12        return String(str == null ? '' : str)
13            .replace(/&/g, '&')
14            .replace(/</g, '&lt;')
15            .replace(/>/g, '&gt;')
16            .replace(/"/g, '&quot;');
17    }
18
19    function normalizeOpts(opts, type, extraDefault) {
20        if (typeof opts === 'string') {
21            if (type === 'prompt') {
22                return normalizeOpts({
23                    title: opts,
24                    label: opts,
25                    defaultValue: extraDefault || ''
26                }, type);
27            }
28            return normalizeOpts({ message: opts }, type);
29        }
30        opts = opts || {};
31        return {
32            title: opts.title || (type === 'alert' ? 'Notice' : type === 'confirm' ? 'Confirm' : 'Enter a value'),
33            message: opts.message || '',
34            label: opts.label || opts.fieldLabel || 'Name',
35            defaultValue: opts.defaultValue != null ? String(opts.defaultValue) : '',
36            confirmLabel: opts.confirmLabel || 'OK',
37            cancelLabel: opts.cancelLabel || 'Cancel',
38            okLabel: opts.okLabel || 'OK',
39            danger: !!opts.danger
40        };
41    }
42
43    function teardown(overlay) {
44        if (!overlay) return;
45        if (overlay._keydownHandler) {
46            document.removeEventListener('keydown', overlay._keydownHandler);
47        }
48        if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
49        if (activeOverlay === overlay) activeOverlay = null;
50    }
51
52    function openDialog(type, opts, extraDefault) {
53        opts = normalizeOpts(opts, type, extraDefault);
54
55        return new Promise(function (resolve) {
56            if (activeOverlay) teardown(activeOverlay);
57
58            var overlay = document.createElement('div');
59            overlay.className = 'citrus-shell-dialog';
60            overlay.setAttribute('role', 'dialog');
61            overlay.setAttribute('aria-modal', 'true');
62            overlay.setAttribute('aria-label', opts.title);
63            overlay.style.zIndex = String(zBase + (++zCounter));
64
65            var confirmClass = 'citrus-shell-dialog__btn citrus-shell-dialog__btn--primary';
66            if (opts.danger) confirmClass += ' citrus-shell-dialog__btn--danger';
67
68            var html = '<div class="citrus-shell-dialog__panel">';
69            html += '<h2 class="citrus-shell-dialog__title">' + escapeHtml(opts.title) + '</h2>';
70            if (opts.message) {
71                html += '<p class="citrus-shell-dialog__text">' + escapeHtml(opts.message) + '</p>';
72            }
73            if (type === 'prompt') {
74                html += '<label class="citrus-shell-dialog__field">';
75                html += '<span class="citrus-shell-dialog__field-label">' + escapeHtml(opts.label) + '</span>';
76                html += '<input type="text" class="citrus-shell-dialog__input" data-shell-dialog-input autocomplete="off" spellcheck="false">';
77                html += '</label>';
78            }
79            html += '<div class="citrus-shell-dialog__actions">';
80            if (type !== 'alert') {
81                html += '<button type="button" class="citrus-shell-dialog__btn" data-shell-dialog-cancel>' +
82                    escapeHtml(opts.cancelLabel) + '</button>';
83            }
84            html += '<button type="button" class="' + confirmClass + '" data-shell-dialog-confirm>' +
85                escapeHtml(type === 'alert' ? opts.okLabel : opts.confirmLabel) + '</button>';
86            html += '</div></div>';
87
88            overlay.innerHTML = html;
89            document.body.appendChild(overlay);
90            activeOverlay = overlay;
91
92            var input = overlay.querySelector('[data-shell-dialog-input]');
93            var confirmBtn = overlay.querySelector('[data-shell-dialog-confirm]');
94            var cancelBtn = overlay.querySelector('[data-shell-dialog-cancel]');
95
96            if (input) {
97                input.value = opts.defaultValue;
98            }
99
100            function finish(value) {
101                teardown(overlay);
102                resolve(value);
103            }
104
105            function onConfirm() {
106                if (type === 'prompt') finish(input ? input.value : '');
107                else finish(true);
108            }
109
110            function onCancel() {
111                if (type === 'alert') finish(true);
112                else if (type === 'prompt') finish(null);
113                else finish(false);
114            }
115
116            overlay._keydownHandler = function (ev) {
117                if (ev.key === 'Escape') {
118                    ev.preventDefault();
119                    onCancel();
120                } else if (ev.key === 'Enter') {
121                    if (type === 'prompt' && document.activeElement === input) {
122                        ev.preventDefault();
123                        onConfirm();
124                    } else if (type === 'confirm') {
125                        ev.preventDefault();
126                        onConfirm();
127                    } else if (type === 'alert') {
128                        ev.preventDefault();
129                        onConfirm();
130                    }
131                }
132            };
133            document.addEventListener('keydown', overlay._keydownHandler);
134
135            confirmBtn.addEventListener('click', onConfirm);
136            if (cancelBtn) cancelBtn.addEventListener('click', onCancel);
137
138            overlay.addEventListener('click', function (ev) {
139                if (ev.target === overlay && type !== 'alert') onCancel();
140            });
141
142            global.setTimeout(function () {
143                if (input) {
144                    input.focus();
145                    input.select();
146                } else if (confirmBtn) {
147                    confirmBtn.focus();
148                }
149            }, 0);
150        });
151    }
152
153    global.CitrusShellDialogs = {
154        prompt: function (opts, defaultValue) {
155            return openDialog('prompt', opts, defaultValue);
156        },
157        confirm: function (opts) {
158            return openDialog('confirm', opts);
159        },
160        alert: function (opts) {
161            return openDialog('alert', opts);
162        }
163    };
164})(typeof window !== 'undefined' ? window : this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.