import './css/dropdown.scss';
import { r as lang } from "../utility/lgres";
import { contains, nullOrEmpty } from "../utility/strings";
import { global, isPositive, throttle } from "../utility";
import { createElement } from "../functions";
import { createCheckbox } from "./checkbox";
import { createIcon } from "./icon"

const SymbolDropdown = Symbol.for('ui-dropdown');
const DropdownItemHeight = 30;

let r = lang;
let dropdownGlobal = global[SymbolDropdown];

if (dropdownGlobal == null) {
    // init
    dropdownGlobal = {};
    Object.defineProperty(dropdownGlobal, 'clear', {
        writable: false,
        configurable: false,
        enumerable: false,
        value: function () {
            const panels = document.querySelectorAll('.ui-drop-box.active');
            for (let panel of [...panels]) {
                if (panel == null) {
                    continue;
                }
                panel.classList.remove('active');
                const dropId = panel.parentElement.dataset.dropId;
                if (dropId == null) {
                    continue;
                }
                const dropdown = this[dropId];
                if (dropdown?.multiSelect && typeof dropdown.onCollapsed === 'function') {
                    dropdown.onCollapsed();
                }
            }
        }
    })
    global[SymbolDropdown] = dropdownGlobal;

    document.addEventListener('mousedown', e => {
        let parent = e.target;
        while (parent != null) {
            if (parent.classList.contains('ui-drop-box')) {
                e.stopPropagation();
                return;
            }
            parent = parent.parentElement;
        }
        dropdownGlobal.clear();
    });
}

function selectItems(label, itemlist, template, htmlkey, textkey) {
    let htmls;
    if (typeof template === 'function') {
        htmls = itemlist.map(it => template.call(this, it));
    } else {
        htmls = itemlist.map(it => it[htmlkey]);
    }
    if (htmls.some(it => it instanceof HTMLElement)) {
        label.replaceChildren(...htmls.filter(it => it != null).map(it => it.cloneNode(true)));
    } else {
        let text = itemlist.map(it => it[textkey]).join(', ');
        if (nullOrEmpty(text)) {
            text = r('noneItem', '( None )');
        }
        label.innerText = text;
    }
}

function filterSource(searchkeys, textkey, key, source) {
    if (!Array.isArray(searchkeys) || searchkeys.length === 0) {
        searchkeys = [textkey];
    }
    if (key.length > 0) {
        source = source.filter(it => {
            for (let k of searchkeys) {
                if (contains(it[k].toLowerCase(), key)) {
                    return true;
                }
            }
            return false;
        });
    }
    return source;
}

function getValue(it, valuekey, textkey) {
    if (it == null) {
        return null;
    }
    const value = it[valuekey];
    if (value == null || value === '') {
        return it[textkey];
    }
    return value;
}

/**
 * 下拉列表参数对象
 * @typedef DropdownOptions
 * @property {string} [textKey=text] - 文本关键字
 * @property {string} [valueKey=value] - 值关键字
 * @property {string} [htmlKey=html] - 源码显示的关键字
 * @property {Function} [htmlTemplate] - 模板创建函数
 * @property {number} [maxLength=500] - 最大输入长度
 * @property {boolean} [multiSelect] - 是否允许多选
 * @property {string} [selected] - 选中值
 * @property {string[]} [selectedList] - 选中的数组
 * @property {boolean} [disabled] - 是否禁用
 * @property {boolean} [input] - 是否支持输入
 * @property {boolean} [search] - 是否支持搜索
 * @property {string[]} [searchKeys] - 搜索的关键字数组
 * @property {string} [searchPlaceholder] - 搜索提示文本，默认值取语言资源 `searchHolder` "Search..."
 * @property {number} [tabIndex] - 焦点索引
 * @property {string} [placeholder] - 输入框的提示文本
 * @property {boolean} [slideFixed] - 是否固定为向下展开
 * @property {HTMLElement} [wrapper] - 父元素，默认添加到头元素之后
 */

export class Dropdown {
    _var = {};
    // _var.options;

    // _var.wrapper;
    // _var.container;
    // _var.label;

    // _var.allChecked;
    // _var.source;
    // _var.lastSelected;
    // _var.selected;
    // _var.selectedList;

    sourceFilter;
    onSelectedList;
    onSelected;
    onExpanded;
    onCollapsed;

    constructor(options = {}) {
        options.textKey ??= 'text';
        options.valueKey ??= 'value';
        options.htmlKey ??= 'html';
        options.maxLength ??= 500;
        this._var.options = options;
        const getText = options.getText;
        if (typeof getText === 'function') {
            r = getText;
        } else if (typeof GetTextByKey === 'function') {
            r = GetTextByKey;
        }
        options.searchPlaceholder ??= r('searchHolder', 'Search...');
    }

    create() {
        const options = this._var.options;

        // wrapper
        const wrapper = createElement('div', 'ui-drop-wrapper');
        const dropId = String(Math.random()).substring(2);
        if (options.wrapper instanceof HTMLElement) {
            options.wrapper.dataset.dropId = dropId;
        }
        wrapper.dataset.dropId = dropId;
        dropdownGlobal[dropId] = this;
        this._var.wrapper = wrapper;

        // header
        const header = createElement('div', 'ui-drop-header');
        header.addEventListener('keypress', e => {
            if (e.key === ' ' || e.key === 'Enter') {
                header.dispatchEvent(new MouseEvent('click'));
            }
        });
        header.addEventListener('keydown', e => {
            const up = e.key === 'ArrowUp';
            const down = e.key === 'ArrowDown';
            if (up || down) {
                const source = this.source;
                const count = source.length;
                const valuekey = this._var.options.valueKey;
                const textkey = this._var.options.textKey;
                let index = source?.indexOf(this._var.selected);
                if (isNaN(index) || index < -1) {
                    index = -1;
                } else if (index >= count) {
                    index = count - 1;
                }
                if (up) {
                    if (index > 0) {
                        index--;
                    } else {
                        index = 0;
                    }
                } else if (down) {
                    if (index < 0) {
                        index = 0;
                    } else if (index < count) {
                        index++;
                    } else {
                        index = count - 1;
                    }
                }
                const target = getValue(source[index], valuekey, textkey);
                if (target != null) {
                    this.select(target);
                }
            } else if (e.key === 'Tab') {
                this._dropdown(false);
            }
        });
        header.addEventListener('click', () => {
            if (this.disabled) {
                return;
            }
            const active = this._expanded;
            const label = this._var.label;
            if (active && label.ownerDocument.activeElement === label) {
                return;
            }
            this._dropdown(!active);
            if (!active && typeof this.onExpanded === 'function') {
                setTimeout(() => this.onExpanded(), 120);
            }
        });

        // label or input
        let label;
        if (options.input) {
            label = createElement('input', 'ui-drop-text');
            label.type = 'text';
            label.autocomplete = 'off';
            label.draggable = false;
            options.placeholder && label.setAttribute('placeholder', options.placeholder);
            isPositive(options.maxLength) && label.setAttribute('maxlength', options.maxLength);
            isPositive(options.tabIndex) && label.setAttribute('tabindex', options.tabIndex);
            label.addEventListener('input', e => {
                const key = e.target.value.toLowerCase();
                const source = filterSource(options.searchKeys, options.textKey, key, this.source);
                this._filllist(source);
                this._var.container.classList.add('active');
            });
            label.addEventListener('blur', e => this.select(e.target.value));
            label.addEventListener('mousedown', e => this._expanded && e.stopPropagation());
        } else {
            isPositive(options.tabIndex) && header.setAttribute('tabindex', options.tabIndex);
            label = createElement('label', 'ui-drop-text');
        }
        this._var.label = label;
        if (options.multiSelect) {
            if (Array.isArray(options.selectedList)) {
                this.selectlist(options.selectedList, true);
            } else {
                this._var.allChecked = true;
                label.innerText = r('allItem', '( All )');
            }
        } else if (options.selected != null) {
            this.select(options.selected, true);
        }
        header.append(label, createElement('label', 'ui-drop-caret'));
        wrapper.appendChild(header);

        this.disabled = options.disabled || false;
        return wrapper;
    }

    get multiSelect() { return this._var.options.multiSelect }

    get ignoreAll() { return this._var.options.ignoreAll }

    get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }

    set disabled(flag) {
        if (this._var.wrapper == null) {
            return;
        }
        if (flag) {
            this._var.wrapper.querySelector('.ui-drop-header').classList.add('disabled');
        } else {
            this._var.wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
        }
    }

    get source() {
        let source = this._var.source;
        if (source == null || !Array.isArray(source)) {
            if (typeof this.sourceFilter === 'function') {
                source = this.sourceFilter();
            }
            if (!Array.isArray(source)) {
                source = [];
            }
            this._var.source = source;
        }
        return source;
    }

    set source(list) {
        if (!Array.isArray(list)) {
            return;
        }
        const valuekey = this._var.options.valueKey;
        function reduceItems(list, id, level = 0) {
            if (!Array.isArray(list)) {
                return [];
            }
            return list.reduce((array, item) => [...array, { __p: id, __level: level, ...item }, ...reduceItems(item.children, item[valuekey], level + 1)], []);
        }
        this._var.source = reduceItems(list);
        if (this._expanded) {
            setTimeout(() => this._dropdown(), 120);
        }
    }

    get selected() { return this._var.selected }

    get selectedList() { return this._var.selectedList || [] }

    select(selected, silence, ignoreCase) {
        if (typeof selected !== 'string') {
            selected = String(selected);
        }
        if (ignoreCase) {
            selected = selected.toLowerCase();
        }
        if (this._var.lastSelected === selected) {
            return;
        }
        this._var.lastSelected = selected;
        const valuekey = this._var.options.valueKey;
        const textkey = this._var.options.textKey;
        const template = this._var.options.htmlTemplate;
        const htmlkey = this._var.options.htmlKey;
        let item = this.source.find(it => (ignoreCase ? String(getValue(it, valuekey, textkey)).toLowerCase() : String(getValue(it, valuekey, textkey))) === selected);
        if (this._var.options.input) {
            if (item == null) {
                item = { [valuekey]: selected };
            }
            this._var.label.value = selected;
        } else {
            const expanded = this._expanded;
            if (expanded) {
                this._var.container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
            }
            if (item == null) {
                this._var.selected = null;
                this._var.label.innerText = ' ';
                return false;
            }
            let html;
            if (typeof template === 'function') {
                html = template.call(this, item);
            } else {
                html = item[htmlkey];
            }
            if (html instanceof HTMLElement) {
                this._var.label.replaceChildren(html.cloneNode(true));
            } else if (typeof html === 'string') {
                this._var.label.innerHTML = html;
            } else {
                let text = item[textkey];
                if (nullOrEmpty(text)) {
                    text = ' ';
                }
                this._var.label.innerText = text;
            }
            if (expanded) {
                for (let li of this._var.container.querySelectorAll('li[data-value]')) {
                    if ((ignoreCase ? li.dataset.value.toLowerCase() : li.dataset.value) === selected) {
                        li.classList.add('selected');
                        break;
                    }
                }
                // const val = selected.replace(/"/g, '\\"');
                // const li = this._var.container.querySelector(`li[data-value="${val}"]`);
                // if (li != null) {
                //     li.classList.add('selected');
                // }
            }
        }
        this._var.selected = item;
        if (!silence && typeof this.onSelected === 'function') {
            this.onSelected(item);
        }
        return true;
    }

    selectlist(selectedlist, silence) {
        const source = this.source;
        const valuekey = this._var.options.valueKey;
        const textkey = this._var.options.textKey;
        const template = this._var.options.htmlTemplate;
        const htmlkey = this._var.options.htmlKey;
        const itemlist = selectedlist.map(a => {
            const v = typeof a === 'string' ? a : String(a);
            let item = source.find(it => String(getValue(it, valuekey, textkey)) === v);
            if (item == null) {
                item = {
                    [valuekey]: v,
                    [textkey]: v
                };
            }
            return item;
        });
        this._var.allChecked = false;
        source.forEach(it => delete it.__checked);
        if (itemlist.length === 0) {
            this._var.selectedList = null;
            this._var.label.innerText = r('none', '( None )');
            return false;
        }
        selectItems(this._var.label, itemlist, template, htmlkey, textkey);
        this._var.selectedList = itemlist;
        if (!silence && typeof this.onSelectedList === 'function') {
            this.onSelectedList(itemlist);
        }
    }

    get _expanded() { return this._var.container?.classList?.contains('active') }

    _dropdown(flag = true) {
        const options = this._var.options;
        let panel = this._var.container;
        if (panel == null) {
            panel = createElement('div', 'ui-drop-box');
            // search box
            if (!options.input && options.search) {
                const search = createElement('div', 'ui-drop-search');
                const input = createElement('input');
                input.type = 'text';
                input.className = 'ui-input';
                isPositive(options.tabIndex) && input.setAttribute('tabindex', options.tabIndex);
                !nullOrEmpty(options.searchPlaceholder) && input.setAttribute('placeholder', options.searchPlaceholder);
                input.addEventListener('input', e => {
                    const key = e.target.value.toLowerCase();
                    const source = filterSource(options.searchKeys, options.textKey, key, this.source);
                    this._filllist(source);
                })
                search.append(input, createIcon('fa-light', 'search'));
                panel.appendChild(search);
            }
            // list
            const list = createElement('div', 'ui-drop-list');
            list.addEventListener('scroll', e => throttle(this._onlistscroll, 10, this, list, e.target.scrollTop), { passive: true });
            if (!this.multiSelect) {
                list.addEventListener('click', e => {
                    let li = e.target;
                    while (li.tagName !== 'LI') {
                        li = li.parentElement;
                        if (li == null) {
                            return;
                        }
                    }
                    const value = li.dataset.value;
                    if (this.select(value) !== false) {
                        dropdownGlobal.clear();
                    }
                });
            }
            panel.appendChild(list);
            this._var.container = panel;
            if (options.wrapper instanceof HTMLElement) {
                options.wrapper.appendChild(panel);
            } else {
                this._var.wrapper.appendChild(panel);
            }
        }
        if (flag) {
            let source = this.source;
            if (!options.input && options.search) {
                const search = panel.querySelector('.ui-drop-search > input');
                if (!nullOrEmpty(search?.value)) {
                    source = filterSource(options.searchKeys, options.textKey, search.value, source);
                }
            }
            this._filllist(source);
            // slide direction
            if (!options.slideFixed) {
                const parent = options.wrapper ?? document.body;
                let p = this._var.wrapper;
                panel.style.minWidth = `${p.offsetWidth}px`;
                const headerHeight = p.offsetHeight;
                let top = p.offsetTop + headerHeight;
                let left = p.offsetLeft;
                if (p !== parent) {
                    while ((p = p.parentElement) != null && p !== parent) {
                        top -= p.scrollTop;
                        left -= p.scrollLeft;
                    }
                }
                p = this._var.wrapper;
                if (p !== parent) {
                    while ((p = p.offsetParent) != null && p !== parent) {
                        top += p.offsetTop;
                        left += p.offsetLeft;
                    }
                }
                const slideUp = top - parent.scrollTop + panel.offsetHeight >= parent.offsetHeight;
                if (options.wrapper instanceof HTMLElement) {
                    if (slideUp) {
                        panel.style.top = '';
                        panel.style.bottom = `${parent.offsetHeight - top + headerHeight - 4}px`;
                    } else {
                        panel.style.top = `${top}px`;
                        panel.style.bottom = '';
                    }
                    panel.style.left = `${left}px`;
                }
                if (slideUp) {
                    panel.classList.add('slide-up');
                } else {
                    panel.classList.remove('slide-up');
                }
            }
            panel.classList.add('active');
            this._var.dropTop = 0;
            panel.querySelector('.ui-drop-list').dispatchEvent(new Event('scroll'));
        } else {
            panel.classList.remove('active');
        }
    }

    _onlistscroll(list, top) {
        const offset = (this.multiSelect && !this.ignoreAll) ? DropdownItemHeight : 0;
        top -= (top % (DropdownItemHeight * 2)) + offset;
        if (top < 0) {
            top = 0;
        } else {
            let bottomTop = this._var.dropHeight - (20 * DropdownItemHeight);
            if (bottomTop < 0) {
                bottomTop = 0;
            }
            if (top > bottomTop) {
                top = bottomTop;
            }
        }
        if (this._var.dropTop !== top) {
            this._var.dropTop = top;
            const startIndex = top / DropdownItemHeight;
            let array = this._var.currentSource;
            if (startIndex + 20 < array.length) {
                array = array.slice(startIndex, startIndex + 20);
            } else {
                array = array.slice(-20);
            }
            const content = list.querySelector('.drop-content');
            content.replaceChildren();
            this._dofilllist(content, array);
            content.style.top = `${top + offset}px`;
        }
    }

    _filllist(source) {
        const list = this._var.container.querySelector('.ui-drop-list');
        list.replaceChildren();
        const height = source.length * DropdownItemHeight;
        this._var.dropHeight = height;
        this._var.currentSource = source;
        const holder = createElement('div', 'drop-holder');
        holder.style.height = `${height}px`;
        const content = createElement('div', 'drop-content');
        if (this.multiSelect && !this.ignoreAll) {
            list.appendChild(
                createElement('li', null,
                    createCheckbox({
                        label: r('allItem', '( All )'),
                        checked: this._var.allChecked,
                        customAttributes: { 'isall': '1' },
                        onchange: e => this._triggerselect(e.target)
                    })
                )
            );
            content.style.top = `${DropdownItemHeight}px`;
        } else {
            content.style.top = '0px';
        }
        const multiselect = this.multiSelect;
        const valuekey = this._var.options.valueKey;
        const textkey = this._var.options.textKey;
        const allchecked = this._var.allChecked;
        const selectedlist = this.selectedList;
        source.forEach((item, i) => {
            let val = getValue(item, valuekey, textkey);
            if (typeof val !== 'string') {
                val = String(val);
            }
            if (multiselect) {
                const selected = selectedlist.some(s => String(getValue(s, valuekey, textkey)) === val);
                if (allchecked || selected) {
                    item.__checked = 1;
                } else {
                    const indeterminate = selectedlist.some(s => this._contains(String(getValue(s, valuekey, textkey)), item, valuekey, textkey));
                    if (indeterminate) {
                        item.__checked = 2;
                    }
                }
            }
        });
        if (source.length > 20) {
            source = source.slice(0, 20);
        }
        const scrolled = this._dofilllist(content, source);
        list.append(holder, content);
        if (scrolled != null) {
            setTimeout(() => list.scrollTop = scrolled, 10);
        }
    }

    _contains(it, item, valuekey, textkey) {
        if (Array.isArray(item.children)) {
            for (let t of item.children) {
                if (it === getValue(t, valuekey, textkey)) {
                    return true;
                }
                if (this._contains(it, t, valuekey, textkey)) {
                    return true;
                }
            }
        }
        return false;
    }

    _dofilllist(content, array) {
        const multiselect = this.multiSelect;
        const valuekey = this._var.options.valueKey;
        const textkey = this._var.options.textKey;
        const template = this._var.options.htmlTemplate;
        const htmlkey = this._var.options.htmlKey;
        const selected = this.selected;
        let scrolled;
        array.forEach((item, i) => {
            let val = getValue(item, valuekey, textkey);
            if (typeof val !== 'string') {
                val = String(val);
            }
            const li = createElement('li');
            li.dataset.value = val;
            li.title = item[textkey];
            if (item.__level > 0) {
                li.style.marginLeft = `${item.__level * 24}px`;
            }
            const wrapper = createElement('span', 'li-wrapper',
                createElement('span', span => {
                    // events
                    span.className = 'ui-expandor';
                    if (Array.isArray(item.children) && item.children.length > 0) {
                        span.classList.add('active');
                        function hideChildren(children) {
                            for (let c of children) {
                                c.__visible = false;
                                if (Array.isArray(c.children)) {
                                    hideChildren(c.children);
                                }
                            }
                        }
                    }
                },
                    createIcon('fa-solid', 'caret-down')
                )
            );
            li.appendChild(wrapper);
            let label;
            let html;
            if (typeof template === 'function') {
                html = template.call(this, item);
            } else {
                html = item[htmlkey];
            }
            if (html instanceof HTMLElement) {
                label = html;
            } else if (typeof html === 'string') {
                label = createElement('span');
                label.innerHTML = html;
            }
            if (multiselect) {
                if (label == null) {
                    label = createElement('span');
                    label.innerText = item[textkey];
                }
                const box = createCheckbox({
                    label,
                    checked: item.__checked === 1,
                    indeterminate: item.__checked === 2,
                    customAttributes: {
                        'class': 'dataitem',
                        'data-value': val
                    },
                    onchange: e => this._triggerselect(e.target, item)
                });
                wrapper.appendChild(box);
            } else {
                if (label == null) {
                    label = createElement('span');
                    label.innerHTML = item[textkey];
                }
                wrapper.appendChild(label);
                if (selected != null && String(selected[valuekey]) === val) {
                    scrolled = DropdownItemHeight * i;
                    li.classList.add('selected');
                }
            }
            content.appendChild(li);
        });
        return scrolled;
    }

    _triggerselect(checkbox, item) {
        let list;
        const valuekey = this._var.options.valueKey;
        const textkey = this._var.options.textKey;
        const template = this._var.options.htmlTemplate;
        const htmlkey = this._var.options.htmlKey;
        const source = this.source;
        if (checkbox.getAttribute('isall') === '1') {
            const allchecked = this._var.allChecked = checkbox.checked;
            const boxes = this._var.container.querySelectorAll('input.dataitem');
            boxes.forEach(box => box.checked = allchecked);
            source.forEach(it => it.__checked = allchecked ? 1 : 0);
            list = [];
        } else {
            const all = this._var.container.querySelector('input[isall="1"]');
            const val = checkbox.dataset.value;
            const getSelectedList = () => {
                const list = [];
                const cache = {};
                source.filter(it => it.__p == null).forEach(it => {
                    if (it.__checked === 1) {
                        list.push(it);
                        cache[getValue(it, valuekey, textkey)] = true;
                    }
                });
                source.filter(it => it.__p != null).forEach(it => {
                    if (cache[it.__p]) {
                        return;
                    }
                    if (it.__checked === 1) {
                        list.push(it);
                    }
                });
                return list;
            };
            if (checkbox.checked) {
                item.__checked = 1;
                if (item.__p != null) {
                    // parent
                    if (!source.filter(it => it.__p === item.__p).some(it => it.__checked !== 1)) {
                        const parent = source.filter(it => String(getValue(it, valuekey, textkey)) === item.__p)[0];
                        if (parent != null) {
                            parent.__checked = 1;
                        }
                    }
                }
                if (item.children?.length > 0) {
                    // children
                    source.filter(it => it.__p === val).forEach(it => it.__checked = 1);
                }
                if (!source.some(it => it.__checked !== 1)) {
                    this._var.allChecked = true;
                    if (all != null) {
                        all.checked = true;
                    }
                    list = [];
                } else {
                    list = getSelectedList();
                }
            } else {
                if (item.children?.length > 0) {
                    if (item.__checked === 2) {
                        item.__checked = 0;
                        // children
                        source.filter(it => it.__p === val).forEach(it => it.__checked = 0);
                    } else {
                        item.__checked = 2;
                    }
                } else {
                    item.__checked = 0;
                }
                if (item.__p != null) {
                    // parent
                    const parent = source.filter(it => String(getValue(it, valuekey, textkey)) === item.__p)[0];
                    if (parent != null) {
                        const some = source.filter(it => it.__p === item.__p).some(it => it.__checked === 1 || it.__checked === 2);
                        parent.__checked = some ? 2 : 0;
                    }
                }
                if (this._var.allChecked) {
                    this._var.allChecked = false;
                    if (all != null) {
                        all.checked = false;
                    }
                }
                list = getSelectedList();
            }
        }
        // refresh selection
        const checkboxes = this._var.container.querySelectorAll('input.dataitem');
        checkboxes.forEach(c => {
            const val = c.dataset.value;
            const it = source.filter(it => String(getValue(it, valuekey, textkey)) === val)[0];
            if (it != null) {
                c.checked = it.__checked === 1 || it.__checked === 2;
                c.indeterminate = it.__checked === 2;
            }
        });

        if (this._var.allChecked) {
            this._var.label.innerText = r('allItem', '( All )');
        } else {
            selectItems(this._var.label, list, template, htmlkey, textkey);
        }
        this._var.selectedList = list;
        if (typeof this.onSelectedList === 'function') {
            this.onSelectedList(list);
        }
    }

    static resolve(dom = document.body, trigger) {
        const selects = dom.querySelectorAll('select');
        for (let sel of selects) {
            const source = [...sel.children].map(it => {
                return { value: it.value, text: it.innerText }
            });
            const drop = new Dropdown({
                selected: sel.value,
                disabled: sel.disabled,
                tabIndex: sel.tabIndex
            });
            drop.source = source;
            if (typeof trigger === 'function') {
                drop.onSelected = item => trigger.call(drop, item);
            }
            sel.parentElement.replaceChild(drop.create(), sel);
        }
        return dom;
    }
}