PageSourceSearch

https://xn---1-6kcd9arpa7n.xn--p1ai/assets/admin/tree-6t6nq8Q.js

js xn---1-6kcd9arpa7n.xn--p1ai collected 2026-10-04 09:40:01 UTC 6,457 bytes, 168 lines download raw bytes

1import $ from 'jquery';
2import { htmlspecialchars } from './escape.js';
3
4/**
5 * The category tree of the admin forms — port of getThreeChapters()/
6 * getChapterHtml() from admin/js/generate.js. The category screen renders it
7 * with radios (one parent), the product form with checkboxes (many sections);
8 * both stitch children into their parent's html while looping levels down, so a
9 * category whose `parent` is not a JSON number lands at the root.
10 */
11export function renderTree(list, maxLevel, inputType) {
12    const wrap = document.querySelector('.form__tree-list');
13    if (wrap === null) {
14        return;
15    }
16    const childs = [];
17    let code = '';
18
19    for (let lev = maxLevel; lev > 0; lev--) {
20        for (const key in list) {
21            const cat = list[key];
22            if (cat.level != lev) {
23                continue;
24            }
25            const html = chapterHtml(cat, childs[cat.id], cat.level, inputType);
26            if (typeof cat.parent === 'number') {
27                childs[cat.parent] = typeof childs[cat.parent] !== 'undefined' ? childs[cat.parent] + html : html;
28            } else {
29                code += html;
30            }
31        }
32    }
33    wrap.innerHTML = code;
34}
35
36/** Port of getChapterHtml(cat, child, level, inputType). */
37export function chapterHtml(cat, child, level, inputType) {
38    const childLevel = level + 1;
39    let item = typeof child !== 'undefined'
40        ? '<li class="form__tree-parent form__tree-item"><span class="form__tree-toggle"><i class="ico__select"></i></span>'
41        : '<li class="form__tree-item">';
42
43    item += '<label class="form__tree-label">' +
44        '<input type="' + inputType + '" value="' + cat['id'] + '" data-text=\'' + htmlspecialchars(cat.chaptername) + '\' name="chapter">' +
45        '<i class="form__tree-check"></i>' +
46        '<span>' + htmlspecialchars(cat.chaptername) + '</span>' +
47        '</label>';
48    if (typeof child !== 'undefined') {
49        item = item + '<ul class="form__tree-children form__tree-children-' + inputType + '" data-level="' + childLevel + '">' + child + '</ul>';
50    }
51
52    return item + '</li>';
53}
54
55/**
56 * Port of checkedTree(): every checked box becomes a tag carrying the hidden
57 * `chapters[]` field the product form submits.
58 */
59export function checkedTree() {
60    let html = '';
61    $('.form__tree-item').removeClass('parentActive');
62    $('.form__tree input[type="checkbox"]:checked').each((_, input) => {
63        const level = parseInt($(input).closest('ul').data('level'), 10);
64        html += '<div class="form__tree-tag"><input type="hidden" name="chapters[]" value="' + input.value + '"><span>' + htmlspecialchars($(input).data('text')) + '</span><button type="button" data-value="' + input.value + '" class="form__tree-tag-remove form-tree-tag-group-remove"><i class="ico__close"></i></button></div>';
65        // legacy highlights the ancestors of every checked box
66        if (level > 1) {
67            const parentli = $(input).closest('ul').parent('.form__tree-item');
68            parentli.addClass('parentActive');
69            if (level > 2) {
70                parentli.closest('ul').parent('.form__tree-item').addClass('parentActive');
71            }
72        }
73    });
74    $('.form__tree-tags').html(html).addClass('selected');
75}
76
77/** Close every open category-tree dropdown. */
78export function closeTreePanel() {
79    $('.form__tree').removeClass('active');
80    $('.form__tree-item').removeClass('opened');
81    $('.form__tree ul').removeClass('active');
82    $('.form__tree-title').removeClass('toback');
83    $('.form__tree-wrap').removeClass('is-open');
84    // Drop any leftover body backdrop from older builds — it sat above
85    // .admin__wrap (isolation:isolate) and stole checkbox clicks.
86    document.getElementById('form-tree-backdrop')?.remove();
87}
88
89/** Open one tree panel. Outside clicks are handled by bindTreeOutsideClose. */
90export function openTreePanel(treeEl) {
91    closeTreePanel();
92    if (!treeEl) {
93        return;
94    }
95    treeEl.classList.add('active');
96    treeEl.closest('.form__tree-wrap')?.classList.add('is-open');
97}
98
99let treeOutsideCloseBound = false;
100
101/** Collapse the tree when clicking anywhere outside the open popup panel. */
102export function bindTreeOutsideClose() {
103    if (treeOutsideCloseBound) {
104        return;
105    }
106    treeOutsideCloseBound = true;
107    document.addEventListener('click', (e) => {
108        const tree = document.querySelector('.form__tree.active');
109        if (!tree) {
110            return;
111        }
112        if (tree.contains(e.target)) {
113            return;
114        }
115        // Keep open while interacting with the field chrome (tags, toggle).
116        if (e.target.closest('.form__tree-wrap')) {
117            return;
118        }
119        closeTreePanel();
120    }, true);
121}
122
123/** The panel behaviour shared by both forms (toggle, back, close). */
124export function bindTreePanel(state) {
125    bindTreeOutsideClose();
126    if (bindTreePanel.bound) {
127        return;
128    }
129    bindTreePanel.bound = true;
130
131    $(document).on('click', '.form__tree-toggle', (e) => {
132        e.preventDefault();
133        const toggle = $(e.currentTarget);
134        toggle.closest('li').toggleClass('opened').siblings('li').removeClass('opened').find('ul').removeClass('active');
135        toggle.siblings('.form__tree-children').toggleClass('active');
136        state.level = toggle.siblings('.form__tree-children').data('level');
137        $('.form__tree-title').addClass('toback');
138    });
139
140    $(document).on('click', '.form-tree-back', (e) => {
141        e.preventDefault();
142        $(document).find('.form__tree-children[data-level="' + state.level + '"]')
143            .removeClass('active').closest('li').removeClass('opened');
144        state.level--;
145        if (state.level === 1) {
146            $('.form__tree-title').removeClass('toback');
147        }
148    });
149
150    $(document).on('click', '.form__tree-select-toggle', (e) => {
151        e.preventDefault();
152        e.stopPropagation();
153        const $wrap = $(e.currentTarget).closest('.form__tree-wrap');
154        const $tree = $wrap.find('.form__tree');
155        if ($tree.hasClass('active')) {
156            closeTreePanel();
157            return;
158        }
159        openTreePanel($tree.get(0));
160        $wrap.find('.form__tree-item').removeClass('opened');
161        $wrap.find('.form__tree ul').removeClass('active');
162    });
163
164    $(document).on('click', '.form-tree-close', (e) => {
165        e.preventDefault();
166        closeTreePanel();
167    });
168}

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.