PageSourceSearch

https://vestnikkui.ru/bundles/asutemplate/Base/js/editorum-ui/edit…?v=20260328-layout-assets-eob

js vestnikkui.ru collected 2026-10-02 20:03:13 UTC 14,159 bytes, 359 lines download raw bytes

1(function (window, document) {
2    var namespace = window.EditorumUi = window.EditorumUi || {};
3    var internals = namespace.__internals = namespace.__internals || {};
4    var flashStylesInjected = false;
5
6    function injectFlashStyles() {
7        var style;
8
9        if (flashStylesInjected) {
10            return;
11        }
12
13        style = document.createElement('style');
14        style.textContent = ''
15            // Toast stack: top-right, narrow column, stacked vertically.
16            + '.editorum-flashes{position:fixed;top:16px;right:16px;left:auto;transform:none;display:flex;flex-direction:column;gap:10px;width:min(380px,calc(100% - 32px));z-index:10000;pointer-events:none;}'
17            // Individual toast: compact card with colored left accent and an icon.
18            + '.editorum-flash{align-items:flex-start;background:#fff;border:1px solid #e5e7eb;border-left:4px solid #6b7280;border-radius:8px;box-shadow:0 10px 25px -5px rgba(15,23,42,0.15),0 4px 6px -2px rgba(15,23,42,0.05);display:grid;grid-template-columns:auto minmax(0,1fr) auto;gap:10px;padding:10px 12px;pointer-events:auto;animation:editorum-flash-in 0.25s cubic-bezier(0.16,1,0.3,1);}'
19            + '@keyframes editorum-flash-in{from{opacity:0;transform:translateX(24px);}to{opacity:1;transform:translateX(0);}}'
20            + '.editorum-flash__icon{align-items:center;color:#6b7280;display:inline-flex;flex:0 0 auto;height:20px;justify-content:center;margin-top:1px;width:20px;}'
21            + '.editorum-flash__icon svg{height:20px;width:20px;}'
22            + '.editorum-flash__body{display:flex;flex-direction:column;gap:2px;min-width:0;}'
23            // Title: sentence-case, subtle weight — no more SHOUTY UPPERCASE banner.
24            + '.editorum-flash__title{color:#111827;display:block;font-size:13px;font-weight:600;letter-spacing:0;line-height:1.35;margin:0;text-transform:none;}'
25            + '.editorum-flash__title:empty{display:none;}'
26            + '.editorum-flash__message{color:#4b5563;font-size:13px;line-height:1.5;min-width:0;word-wrap:break-word;}'
27            + '.editorum-flash__close{align-items:center;background:transparent;border:0;border-radius:4px;color:#9ca3af;cursor:pointer;display:inline-flex;flex:0 0 auto;font-size:18px;font-weight:400;height:20px;justify-content:center;line-height:1;margin:0;padding:0;transition:background-color 0.15s,color 0.15s;width:20px;}'
28            + '.editorum-flash__close:hover,.editorum-flash__close:focus{background:#f3f4f6;color:#111827;outline:none;}'
29            // Type variants: accent-color left border + icon color only.
30            + '.editorum-flash--success{border-left-color:#16a34a;}'
31            + '.editorum-flash--success .editorum-flash__icon{color:#16a34a;}'
32            + '.editorum-flash--info{border-left-color:#2563eb;}'
33            + '.editorum-flash--info .editorum-flash__icon{color:#2563eb;}'
34            + '.editorum-flash--warning{border-left-color:#d97706;}'
35            + '.editorum-flash--warning .editorum-flash__icon{color:#d97706;}'
36            + '.editorum-flash--danger{border-left-color:#dc2626;}'
37            + '.editorum-flash--danger .editorum-flash__icon{color:#dc2626;}';
38        document.head.appendChild(style);
39        flashStylesInjected = true;
40    }
41
42    function getFlashIconSvg(type) {
43        var icons = {
44            success: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/></svg>',
45            info: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>',
46            warning: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/>
46<line x1="12" y1="17" x2="12.01" y2="17"/></svg>',
47            danger: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10"/><line x1="15" y1="9" x2="9" y2="15"/><line x1="9" y1="9" x2="15" y2="15"/></svg>'
48        };
49        return icons[type] || icons.info;
50    }
51
52    function normalizeFlashType(type) {
53        if (type === 'error') {
54            return 'danger';
55        }
56
57        if (type === 'notice') {
58            return 'info';
59        }
60
61        return type || 'info';
62    }
63
64    // Titles like "ИНФОРМАЦИЯ!", "Информация", "Success", "Error" are redundant
65    // when we already have a typed icon and colored accent bar — drop them so
66    // the toast shows just the useful message text.
67    function isGenericTitle(title, type) {
68        if (!title) return true;
69        var normalized = String(title).replace(/[!:\s]+/g, '').toLowerCase();
70        var generic = ['info', 'information', 'информация', 'success', 'успех',
71                       'warning', 'внимание', 'error', 'ошибка', 'danger'];
72        for (var i = 0; i < generic.length; i++) {
73            if (normalized === generic[i]) return true;
74        }
75        return false;
76    }
77
78    function createFlashMessage(group) {
79        var alert = document.createElement('div');
80        var iconEl = document.createElement('span');
81        var body = document.createElement('div');
82        var title = document.createElement('strong');
83        var messageContainer = document.createElement('div');
84        var button = document.createElement('button');
85        var type = normalizeFlashType(group.type);
86
87        alert.className = 'editorum-flash editorum-flash--' + type;
88        alert.setAttribute('role', 'alert');
89        alert.setAttribute('data-flash-timer', typeof group.timer === 'number' ? group.timer : 4000);
90
91        iconEl.className = 'editorum-flash__icon';
92        iconEl.setAttribute('aria-hidden', 'true');
93        iconEl.innerHTML = getFlashIconSvg(type);
94
95        body.className = 'editorum-flash__body';
96        title.className = 'editorum-flash__title';
97        messageContainer.className = 'editorum-flash__message';
98
99        if (!isGenericTitle(group.title, type)) {
100            title.textContent = group.title;
101            body.appendChild(title);
102        }
103
104        internals.each(group.messages || [], function (message, index) {
105            if (index > 0) {
106                messageContainer.appendChild(document.createElement('br'));
107            }
108
109            messageContainer.appendChild(document.createTextNode(message));
110        });
111
112        body.appendChild(messageContainer);
113
114        button.type = 'button';
115        button.className = 'editorum-flash__close';
116        button.setAttribute('aria-label', 'Close');
117        button.innerHTML = '&times;';
118        button.addEventListener('click', function () {
119            if (alert.parentNode) {
120                alert.parentNode.removeChild(alert);
121            }
122        });
123
124        alert.appendChild(iconEl);
125        alert.appendChild(body);
126        alert.appendChild(button);
127
128        window.setTimeout(function () {
129            if (alert.parentNode) {
130                alert.parentNode.removeChild(alert);
131            }
132        }, typeof group.timer === 'number' ? group.timer : 4000);
133
134        return alert;
135    }
136
137    function showFlashMessages(groups) {
138        var container;
139
140        if (!groups || !groups.length) {
141            return false;
142        }
143
144        injectFlashStyles();
145        container = internals.query('.editorum-flashes');
146
147        if (!container) {
148            container = document.createElement('div');
149            container.className = 'editorum-flashes';
150            document.body.appendChild(container);
151        }
152
153        internals.each(groups, function (group) {
154            container.appendChild(createFlashMessage(group));
155        });
156
157        return true;
158    }
159
160    function resolveWithin(root, selector) {
161        if (!selector) {
162            return null;
163        }
164
165        if (selector.charAt(0) === '#') {
166            return internals.query(selector);
167        }
168
169        return internals.query(selector, root) || internals.query(selector);
170    }
171
172    function resolveListItems(settings) {
173        var root = settings.root || document;
174        var itemRootSelector = settings.itemRootSelector || settings.itemsRoot;
175        var itemRoot = itemRootSelector ? resolveWithin(root, itemRootSelector) : document;
176
177        return internals.queryAll(settings.itemSelector || '.author-list-item', itemRoot || document);
178    }
179
180    function initListFilter(options) {
181        var settings = options || {};
182        var root = settings.searchSelector ? internals.query(settings.searchSelector) : settings.root || null;
183        var input = resolveWithin(root || document, settings.inputSelector || 'input[type="search"], input[type="text"]');
184        var reset = resolveWithin(root || document, settings.resetSelector || '');
185        var counter = resolveWithin(root || document, settings.counterSelector || '');
186        var noResults = resolveWithin(root || document, settings.noResultsSelector || '');
187        var textCache = [];
188        var debounceTimer = null;
189        var debounceMs = 200;
190
191        function buildCache() {
192            var items = resolveListItems(settings);
193            var i;
194
195            textCache = [];
196            for (i = 0; i < items.length; i++) {
197                textCache.push({
198                    el: items[i],
199                    text: (items[i].textContent || items[i].innerText || '').toLowerCase()
200                });
201            }
202        }
203
204        function updateCounter(visibleCount, totalCount) {
205            var strong;
206            var shouldHideCounter = visibleCount <= 0 || visibleCount === totalCount || visibleCount === totalCount - 1;
207
208            if (!counter) {
209                return;
210            }
211
212            if (shouldHideCounter) {
213                internals.hide(counter);
214                return;
215            }
216
217            internals.show(counter);
218            strong = internals.query('strong', counter);
219            if (strong) {
220                strong.textContent = visibleCount;
221            } else {
222                counter.textContent = visibleCount;
223            }
224        }
225
226        function filter() {
227            var value;
228            var visibleCount = 0;
229            var isEmpty;
230            var i, entry;
231
232            if (!input) {
233                return;
234            }
235
236            value = (input.value || '').toLowerCase();
237            isEmpty = value === '';
238
239            for (i = 0; i < textCache.length; i++) {
240                entry = textCache[i];
241
242                if (isEmpty || entry.text.indexOf(value) !== -1) {
243                    entry.el.style.display = '';
244                    visibleCount += 1;
245                } else {
246                    entry.el.style.display = 'none';
247                }
248            }
249
250            updateCounter(visibleCount, textCache.length);
251
252            if (noResults) {
253                internals.toggle(noResults, !isEmpty && visibleCount === 0);
254            }
255
256            if (reset) {
257                internals.toggle(reset, !isEmpty);
258            }
259
260            if (typeof settings.onAfter === 'function') {
261                settings.onAfter({
262                    total: textCache.length,
263                    visible: visibleCount,
264                    root: root,
265                    input: input
266                });
267            }
268        }
269
270        function debouncedFilter() {
271            if (debounceTimer) {
272                clearTimeout(debounceTimer);
273            }
274
275            debounceTimer = setTimeout(function () {
276                debounceTimer = null;
277                filter();
278            }, debounceMs);
279        }
280
281        if (!root || !input) {
282            return false;
283        }
284
285        buildCache();
286        input.value = '';
287        input.addEventListener('input', debouncedFilter);
288        input.addEventListener('keyup', debouncedFilter);
289
290        if (reset) {
291            reset.addEventListener('click', function (event) {
292                event.preventDefault();
293                input.value = '';
294
295                if (typeof settings.onReset === 'function') {
296                    settings.onReset(root, input, reset);
297                }
298
299                filter();
300            });
301        }
302
303        filter();
304
305        return {
306            refresh: function () {
307                buildCache();
308                filter();
309            },
310            reset: function () {
311                input.value = '';
312                filter();
313            }
314        };
315    }
316
317    function getJson(url, onSuccess, onError) {
318        var request = new window.XMLHttpRequest();
319
320        request.open('GET', url, true);
321        request.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
322        request.onreadystatechange = function () {
323            var response;
324
325            if (request.readyState !== 4) {
326                return;
327            }
328
329            if (request.status >= 200 && request.status < 300) {
330                try {
331                    response = JSON.parse(request.responseText);
332                } catch (error) {
333                    if (typeof onError === 'function') {
334                        onError(error, request);
335                    }
336                    return;
337                }
338
339                if (typeof onSuccess === 'function') {
340                    onSuccess(response, request);
341                }
342
343                return;
344            }
345
346            if (typeof onError === 'function') {
347                onError(request);
348            }
349        };
350        request.send();
351
352        return request;
353    }
354
355    internals.showFlashMessages = showFlashMessages;
356    internals.resolveWithin = resolveWithin;
357    internals.initListFilter = initListFilter;
358    internals.getJson = getJson;
359})(window, document);

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.