PageSourceSearch

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

js pupwife.com collected 2026-09-30 15:05:53 UTC 5,228 bytes, 156 lines download raw bytes

1/**
2 * Shared DOM builders for CitrusOS tool UIs (retro panels, toolbars, forms).
3 */
4(function (global) {
5    'use strict';
6
7    function el(tag, className, attrs) {
8        var node = document.createElement(tag);
9        if (className) node.className = className;
10        if (attrs) {
11            Object.keys(attrs).forEach(function (key) {
12                if (key === 'text') node.textContent = attrs[key];
13                else if (key === 'html') node.innerHTML = attrs[key];
14                else if (key === 'id') node.id = attrs[key];
15                else node.setAttribute(key, attrs[key]);
16            });
17        }
18        return node;
19    }
20
21    function icon(name, label) {
22        var i = el('i', 'fa-solid ' + name, { 'aria-hidden': 'true' });
23        if (label) {
24            var wrap = el('span', 'citrus-tool-icon-label');
25            wrap.appendChild(i);
26            wrap.appendChild(document.createTextNode(' ' + label));
27            return wrap;
28        }
29        return i;
30    }
31
32    function appShell(mount, className) {
33        var root = el('div', 'citrus-tool-app' + (className ? ' ' + className : ''));
34        mount.appendChild(root);
35        return root;
36    }
37
38    function header(title, iconClass) {
39        var h = el('header', 'citrus-tool-header');
40        var h1 = el('h1', 'citrus-tool-title');
41        if (iconClass) h1.appendChild(icon(iconClass));
42        h1.appendChild(document.createTextNode(title));
43        h.appendChild(h1);
44        return h;
45    }
46
47    function panel(className) {
48        return el('section', 'citrus-tool-panel tool-widget' + (className ? ' ' + className : ''));
49    }
50
51    function sectionTitle(text, iconClass) {
52        var h = el('h2', 'citrus-tool-section-title tool-section-title');
53        if (iconClass) h.appendChild(icon(iconClass));
54        h.appendChild(document.createTextNode(text));
55        return h;
56    }
57
58    function toolbar(className) {
59        return el('div', 'citrus-tool-toolbar' + (className ? ' ' + className : ''));
60    }
61
62    function scrollRegion(className) {
63        return el('div', 'citrus-tool-scroll' + (className ? ' ' + className : ''));
64    }
65
66    function btn(label, opts) {
67        opts = opts || {};
68        var tag = opts.href ? 'a' : 'button';
69        var node = el(tag, 'citrus-tool-btn tool-btn' + (opts.className ? ' ' + opts.className : ''));
70        if (tag === 'button') node.type = opts.type || 'button';
71        if (opts.href) node.href = opts.href;
72        if (opts.id) node.id = opts.id;
73        if (opts.ariaLabel) node.setAttribute('aria-label', opts.ariaLabel);
74        if (opts.icon) node.appendChild(icon(opts.icon));
75        node.appendChild(document.createTextNode(label));
76        return node;
77    }
78
79    function tabBar(tabs, activeId) {
80        var bar = el('div', 'citrus-tool-tabs', { role: 'tablist' });
81        tabs.forEach(function (tab) {
82            var b = el('button', 'citrus-tool-tab tab-button' + (tab.id === activeId ? ' active' : ''), {
83                type: 'button',
84                role: 'tab',
85                'aria-selected': tab.id === activeId ? 'true' : 'false',
86                'data-tab': tab.id
87            });
88            if (tab.icon) b.appendChild(icon(tab.icon));
89            b.appendChild(document.createTextNode(tab.label));
90            bar.appendChild(b);
91        });
92        return bar;
93    }
94
95    function formRow(labelText, control) {
96        var row = el('label', 'citrus-tool-form-row');
97        var lbl = el('span', 'citrus-tool-form-label tool-label', { text: labelText });
98        row.appendChild(lbl);
99        row.appendChild(control);
100        return row;
101    }
102
103    function fieldInput(type, id, attrs) {
104        var input = el('input', 'citrus-tool-input tool-select', attrs || {});
105        input.type = type || 'text';
106        if (id) input.id = id;
107        return input;
108    }
109
110    function hint(text) {
111        return el('p', 'citrus-tool-hint tool-hint', { text: text });
112    }
113
114    function grid(className) {
115        return el('div', 'citrus-tool-grid tool-grid' + (className ? ' ' + className : ''));
116    }
117
118    function layoutSplit(sidebar, main) {
119        var wrap = el('div', 'citrus-tool-split');
120        if (sidebar) {
121            sidebar.classList.add('citrus-tool-split__side');
122            sidebar.id = sidebar.id || 'sidebar';
123            wrap.appendChild(sidebar);
124        }
125        if (main) {
126            main.classList.add('citrus-tool-split__main');
127            if (main.tagName === 'MAIN' || !main.tagName) {
128                if (main.tagName !== 'MAIN') {
129                    var m = el('main');
130                    m.appendChild(main);
131                    main = m;
132                }
133            }
134            wrap.appendChild(main);
135        }
136        return wrap;
137    }
138
139    global.CitrusToolUi = {
140        el: el,
141        icon: icon,
142        appShell: appShell,
143        header: header,
144        panel: panel,
145        sectionTitle: sectionTitle,
146        toolbar: toolbar,
147        scrollRegion: scrollRegion,
148        btn: btn,
149        tabBar: tabBar,
150        formRow: formRow,
151        fieldInput: fieldInput,
152        hint: hint,
153        grid: grid,
154        layoutSplit: layoutSplit
155    };
156})(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.