Greasy Fork

lgx-tools

允许删除页面的元素,往页面上放置项目

当前为 2023-03-02 提交的版本,查看 最新版本

// ==UserScript==
// @name         lgx-tools
// @namespace    http://tampermonkey.net/
// @version      0.2.1
// @description  允许删除页面的元素,往页面上放置项目
// @author       You
// @match        *://*
// @match        *://*/*
// @match        *://*/*/*
// @match        *://*/*/*/*
// @match        *://*/*/*/*/*
// @match        *://*/*/*/*/*/*
// @match        *://*/*/*/*/*/*/*
// @match        *://*/*/*/*/*/*/*/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Your code here...
    if (!document.body) return
    const that = document.createElement("div")
    let el, k = !1, x = 0, y = 0
    let styleElement = document.createElement('style')
    that.className = 'lgx-front-hover-block'
    styleElement.innerHTML = '.lgx-front-hover-block{position:fixed;background:#09f3;border:dashed 1px #06f9;transit'
        + 'ion:all .3s cubic-bezier(.68,-0.55,.27,1.55);z-index:2147483647}.lgx-front-hover-block>div{position:absol'
        + 'ute;border:1px dashed #f11a;background:#f553;transition:inherit}.lgx-front-hover-block>div:hover{backgrou'
        + 'nd:#f556}.lgx-ctrl-block{width:6px;height:6px;position:fixed;background:red;bottom:-3px;left:-3px;rotate:'
        + '45deg;z-index:2147483647;cursor:pointer}.lgx-front-hover-no-transition,.lgx-front-hover-no-transition>*{t'
        + 'ransition:none !important}@keyframes shake{0%{transform:translateX(0)}10%{transform:translateX(-10px)}20%'
        + '{transform:translateX(10px)}35%{transform:translateX(-10px)}55%{transform:translateX(10px)}70%{transform:'
        + 'translateX(-10px)}100%{transform:translateX(0)}}.lgx-float-div{position:fixed;opacity:.5;border:none;box-'
        + 'shadow:0 0 5px #0003;cursor:grab;z-index:2147483647;user-select:none}.lgx-float-div>.lgx-resize{content:"'
        + '";display:block;position:absolute}.lgx-float-div>*{user-select:none}.lgx-float-div>.lgx-col-resize{width:'
        + '6px;height:calc(100% - 6px);right:-3px;top:0;cursor:col-resize}.lgx-float-div>.lgx-row-resize{width:calc('
        + '100% - 6px);height:6px;left:0;bottom:-3px;cursor:row-resize}.lgx-float-div>.lgx-nwse-resize{width:12px;he'
        + 'ight:12px;right:-6px;bottom:-6px;cursor:nwse-resize}.lgx-float-div>.lgx-float-item{width:100%;height:100%'
        + ';display:block}.lgx-float-div>.lgx-row-resize-ne{bottom:unset;top:-3px}.lgx-float-div>.lgx-col-resize-ne{'
        + 'left:-3px;right:unset}'
    document.head.appendChild(styleElement)
    styleElement = document.createElement('style')
    document.head.appendChild(styleElement)

    function mouseMoveHandler(ev) {
        if (that === ev.target) {
            clearTimeout(mouseMoveHandler.timeout0)
            return
        }
        if (el === (ev.target?.mock || ev.target)) return
        el = ev.target?.mock || ev.target

        that.classList[ ev.shiftKey ? 'add' : 'remove' ]('lgx-front-hover-no-transition')
        const fun1 = () => {
            for (const {style: s, l0, t0} of that.children) {
                [s.left, s.top, s.width, s.height] = [l0, t0, 0, 0].map(j => j + 'px')
            }
            const fun2 = () => {
                const {left: l0, top: t0, height: h, width: w} =
                el?.getBoundingClientRect() ?? {left: 0, top: 0, height: 0, width: 0}, s = that.style, arr = [];
                const fun3 = () => {
                    for (const i of el?.children ?? []) {
                        if (i === that || i === ctrl) continue
                        const {left: l, top: t, right: r, bottom: b, height: h, width: w} = i.getBoundingClientRect()
                        const [dh, dw] = [document.documentElement.clientHeight, document.documentElement.clientWidth]
                        if (b < 0 || t > dh || l > dw || r < 0) continue
                        const d = document.createElement('div');
                        [d.mock, d.l0, d.t0, d.l, d.t, d.w, d.h] =
                            [i, l + w / 2 - l0, t + h / 2 - t0, l - l0, t - t0, w - 2, h - 2]
                        that.appendChild(d)
                        arr.push(d)
                    }
                    const fun4 = () => {
                        for (const d of arr) {
                            [d.style.left, d.style.top, d.style.height, d.style.width] =
                                [d.l + 'px', d.t + 'px', d.h + 'px', d.w + 'px']
                        }
                    }
                    ev.shiftKey ? fun4() : setTimeout(fun4, 20)
                }
                [s.left, s.top, s.height, s.width] = [l0, t0, h - 2, w - 2].map(i => i + 'px')
                that.innerHTML = ''
                if (ev.shiftKey) {
                    fun3()
                } else {
                    clearTimeout(that.timeout)
                    that.timeout = setTimeout(fun3, 300)
                }
            }

            if (ev.shiftKey) {
                fun2()
            } else {
                clearTimeout(mouseMoveHandler.timeout)
                mouseMoveHandler.timeout = setTimeout(fun2, 300)
            }
        }
        if (ev.shiftKey) {
            fun1()
        } else {
            clearTimeout(mouseMoveHandler.timeout0)
            mouseMoveHandler.timeout0 = setTimeout(fun1, ev.target?.mock ? 500 : 0)
        }
    }

    window.addEventListener('mousemove', ev => {
        [x, y] = [ev.x, ev.y]
    })

    window.addEventListener('keydown', ev => {
        if (ev.key === 'Control') {
            that.style.display = 'none'
        }
    })

    window.addEventListener('keyup', ev => {
        if (ev.key === 'Control') {
            that.style.display = 'block'
        }
    })

    window.addEventListener('click', ev => {
        !ev.ctrlKey && k && el?.remove?.();
        that.style.left = x + 'px'
        that.style.top = y + 'px'
        that.style.width = '0px'
        that.style.height = '0px'
        that.innerHTML = ''
    })

    const ctrl = document.createElement('div')
    ctrl.className = 'lgx-ctrl-block'
    ctrl.onclick = () => {
        k = !k
        ctrl.style.background = k ? 'green' : 'red'
        if (k) {
            document.body?.addEventListener('mousemove', mouseMoveHandler)
            that.style.left = x + 'px'
            that.style.top = y + 'px'
            that.style.width = '0px'
            that.style.height = '0px'
            that.innerHTML = ''
            document.body?.insertBefore(that, ctrl)
            setTimeout(mouseMoveHandler, 20, new Event('mousemove'))
        } else {
            el = void 0
            that.remove()
            try { document.body?.removeEventListener('mousemove', mouseMoveHandler) } catch {}
        }
    }
    document.body.appendChild(ctrl)
    setInterval(() => {
        if (document.lastChild !== ctrl) {
            document.body.appendChild(ctrl)
        }
    }, 10000)

    ctrl.addEventListener('contextmenu', ev => { ev.preventDefault(); console.clear() });

    // !--- img dropping handler ---! //

    ['drop', 'dragleave', 'dragover', 'dragenter'].forEach(
        i => document.addEventListener(i, e => e.preventDefault())
    )

    const floatingContainer = document.createElement('div')
    document.body.insertBefore(floatingContainer, ctrl)

    Object.defineProperties(HTMLDivElement.prototype, {
        makeScaleable: {
            value() {
                let x, y, delta
                Object.defineProperties(this, {
                    // The method 'makeScaleable' is a once-call-method, once it has been called, it will be removed.
                    makeScaleable: { value: void 0 },
                    // scalingCoef: scaling coefficient
                    // The pointer closer to the center of this div element, the coefficient bigger.
                    scalingCoef: {
                        get() {
                            const d = Math.sqrt(
                                Math.pow(x - this.offsetLeft - this.clientWidth / 2, 2)
                                + Math.pow(y - this.offsetTop - this.clientHeight / 2, 2)
                            )
                            const h = this.clientHeight, w = this.clientWidth
                            const r = Math.sqrt(h ** 2 + w ** 2) / 2
                            const v = (1 - d / r) * .2
                            return { x: w * v * delta, y: h * v * delta }
                        }
                    },
                    // pperc: the percentage of the location of the pointer in this div element
                    // e.g. coordinate of the pointer in this div is (2, 3), and the size of this div is (5, 6),
                    //      then the percentage (pperc) is { left: 2 / 5, right: 3 / 5, top: 1 / 2, bottom: 1 / 2 }
                    pperc: {
                        get() {
                            const l = (x - this.offsetLeft) / this.clientWidth,
                                  t = (y - this.offsetTop) / this.clientHeight
                            return { left: l, right: 1 - l, top: t, bottom: 1 - top }
                        }
                    }
                })
                this.onwheel = ev => {
                    [ x, y, delta ] = [ ev.x, ev.y, -ev.deltaY / 114 ]
                    ev.preventDefault()
                    const coef = this.scalingCoef, pperc = this.pperc
                    if (Math.max(this.clientWidth + coef.x, this.clientHeight + coef.y) > 3000) {
                        if (!this.onwheel.flag) {
                            this.onwheel.flag = true
                            this.style.animation = 'shake .2s forwards'
                            setTimeout(() => {
                                this.style.animation = ''
                                this.onwheel.flag = false
                            }, 250)
                        }
                        return
                    }
                    this.style[ this.style.left ? 'left' : 'right' ] = this.style.left
                        ? (x - pperc.left * (this.clientWidth + coef.x)).toFixed(2) + 'px'
                        : (window.innerWidth - x - pperc.right * (this.clientWidth + coef.x)).toFixed(2) + 'px'
                    this.style[ this.style.top ? 'top' : 'bottom' ] = this.style.top
                        ? (y - pperc.top * (this.clientHeight + coef.y)).toFixed(2) + 'px'
                        : (window.innerHeight - y - pperc.bottom * (this.clientHeight + coef.y)).toFixed(2) + 'px'
                    this.style.width = (this.clientWidth + coef.x).toFixed(2) + 'px'
                    this.style.height = (this.clientHeight + coef.y).toFixed(2) + 'px'
                }
                this.ondblclick = ev => {
                    [ x, y, delta ] = [ ev.x, ev.y, 0 ]
                    const coef = { x: this.clientWidth - this.originalWidth, y: this.clientHeight - this.originalHeight }, pperc = this.pperc
                    this.style[ this.style.left ? 'left' : 'right' ] = this.style.left
                        ? (x - pperc.left * this.originalWidth).toFixed(2) + 'px'
                        : (window.innerWidth - x - pperc.right * this.originalWidth).toFixed(2) + 'px'
                    this.style[ this.style.top ? 'top' : 'bottom' ] = this.style.top
                        ? (y - pperc.top * this.originalHeight).toFixed(2) + 'px'
                        : (window.innerHeight - y - pperc.bottom * this.originalHeight).toFixed(2) + 'px'
                    this.style.width = this.originalWidth + 'px'
                    this.style.height = this.originalHeight + 'px'
                }
            },
            configurable: true
        },
        makeResizeable: {
            value() {
                // The method 'makeResizeable' is a once-call-method, once it has been called, it will be removed.
                Object.defineProperty(this, 'makeResizeable', { value: void 0 })
                const [ rowResizeBar, colResizeBar, nwseResizeBlock ] = [ document.createElement('div'),
                    document.createElement('div'), document.createElement('div') ]
                rowResizeBar.className = 'lgx-resize lgx-row-resize'
                colResizeBar.className = 'lgx-resize lgx-col-resize'
                nwseResizeBlock.className = 'lgx-resize lgx-nwse-resize'
                this.append(rowResizeBar, colResizeBar, nwseResizeBlock)

                Object.defineProperties(this, {
                    originalHeight: { value: this.clientHeight },
                    originalWidth: { value: this.clientWidth },
                    rowResizeBar: { value: rowResizeBar },
                    rowResizeBar: { value: rowResizeBar },
                    rowResizeBar: { value: rowResizeBar }
                })

                rowResizeBar.onmousedown = e => {
                    e.cancelBubble = true
                    if (rowResizeBar.ondblclick.flag || e.button !== 0) return
                    rowResizeBar.onmousedown.flag = true
                    const height = this.clientHeight, s = this.style, t = 'transform'
                    // 是否上下翻转
                    rowResizeBar.onmousedown.reversed = (s[t] === 'scaleY(-1)' || s[t] === 'scale(-1)') && (s.scale || 1) > 0

                    const rowResize = ev => {
                        let h = height + (rowResizeBar.onmousedown.reversed ? e.y - ev.y : ev.y - e.y)

                        if (rowResizeBar.onmousedown.reversed ? h > 0 : h < 0) {
                            if (this.style.bottom === '') {
                                s[t] = s[t] === 'scaleX(-1)' ? 'scale(-1)' : 'scaleY(-1)'
                                this.style.bottom = window.innerHeight - this.offsetTop + 'px'
                                this.style.top = ''
                            }
                        } else {
                            if (this.style.top === '') {
                                s[t] = s[t] === 'scale(-1)' ? 'scaleX(-1)' : s[t] === 'scaleY(-1)' ? '' : s[t]
                                this.style.top = this.offsetTop + this.clientHeight + 'px'
                                this.style.bottom = ''
                            }
                        }

                        if (h > 2) {
                            this.style.height = h + 'px'
                        } else if (h >= -2) {
                            this.style.height = '2px'
                        } else {
                            this.style.height = -h + 'px'
                        }
                    }, rowRes_ = e => {
                        if (e.button !== 0) return
                        styleElement.innerHTML = ''
                        const t = this.style.transform
                        rowResizeBar.onmousedown.flag = false
                        nwseResizeBlock.style.cursor = t === 'scaleX(-1)' || t === 'scaleY(-1)' ? 'nesw-resize' : ''
                        try {
                            window.removeEventListener('mousemove', rowResize)
                            window.removeEventListener('mouseup', rowRes_)
                        } catch {}
                    }
                    styleElement.innerHTML = '*{cursor:row-resize !important}'
                    window.addEventListener('mousemove', rowResize)
                    window.addEventListener('mouseup', rowRes_)
                }
                rowResizeBar.oncontextmenu = ev => {
                    ev.cancelBubble = true
                    ev.preventDefault()
                    if (rowResizeBar.oncontextmenu.flag || rowResizeBar.onmousedown.flag) return
                    rowResizeBar.oncontextmenu.flag = true

                    const oldT = this.style.transition
                    if (!ev.transition) {
                        this.style.transition = 'height .1s ease-out, top .1s ease-out, bottom .1s ease-out'
                    }
                    setTimeout(() => {
                        this.style.transition = oldT
                        rowResizeBar.oncontextmenu.flag = false
                    }, 100)

                    this.style.height = this.originalHeight + 'px'
                    this.style[ this.style.top ? 'top' : 'bottom' ] =
                        limit(this.style.top ? this.offsetTop : window.innerHeight - this.clientHeight - this.offsetTop,
                              this.scope?.top ?? 0, this.scope?.bottom ?? window.innerHeight - this.clientHeight) + 'px'
                }
                rowResizeBar.ondblclick = ev => {
                    ev.cancelBubble = true
                    if (rowResizeBar.ondblclick.flag) return
                    rowResizeBar.ondblclick.flag = true
                    const oldT = this.style.transition
                    if (!ev.transition) {
                        this.style.transition = 'height .1s ease-out, top .1s ease-out, bottom .1s ease-out'
                    }
                    setTimeout(() => {
                        this.style.transition = oldT
                        rowResizeBar.ondblclick.flag = false
                    }, 100)

                    const h = this.style.transform === 'scaleY(-1)' || this.style.transform === 'scale(-1)'
                    ? this.offsetTop + this.clientHeight : window.innerHeight - this.offsetTop
                    this.style.height = h + 'px'
                    this.style[ this.style.top ? 'top' : 'bottom' ] =
                        limit(this.style.top ? this.offsetTop : window.innerHeight - this.clientHeight - this.offsetTop,
                            this.scope?.top ?? 0, this.scope?.bottom ?? window.innerHeight - this.clientHeight) + 'px'
                }

                colResizeBar.onmousedown = e => {
                    e.cancelBubble = true
                    if (colResizeBar.ondblclick.flag || e.button !== 0) return
                    colResizeBar.onmousedown.flag = true
                    const width = this.clientWidth, s = this.style, t = 'transform'
                    // 是否左右翻转
                    colResizeBar.onmousedown.reversed = (s[t] === 'scaleX(-1)' || s[t] === 'scale(-1)') && (s.scale || 1) > 0

                    const colResize = ev => {
                        let w = width + (colResizeBar.onmousedown.reversed ? e.x - ev.x : ev.x - e.x)

                        if (colResizeBar.onmousedown.reversed ? w > 0 : w < 0) {
                            if (this.style.right === '') {
                                s[t] = s[t] === 'scaleY(-1)' ? 'scale(-1)' : 'scaleX(-1)'
                                this.style.right = window.innerWidth - this.offsetLeft + 'px'
                                this.style.left = ''
                            }
                        } else {
                            if (this.style.left === '') {
                                s[t] = s[t] === 'scale(-1)' ? 'scaleY(-1)' : s[t] === 'scaleX(-1)' ? '' : s[t]
                                this.style.left = this.offsetLeft + this.clientWidth + 'px'
                                this.style.right = ''
                            }
                        }

                        if (w > 2) {
                            this.style.width = w + 'px'
                        } else if (w >= -2) {
                            this.style.width = '2px'
                        } else {
                            this.style.width = -w + 'px'
                        }

                    }, colRes_ = e => {
                        if (e.button !== 0) return
                        styleElement.innerHTML = ''
                        const t = this.style.transform
                        colResizeBar.onmousedown.flag = false
                        nwseResizeBlock.style.cursor = t === 'scaleX(-1)' || t === 'scaleY(-1)' ? 'nesw-resize' : ''
                        try {
                            window.removeEventListener('mousemove', colResize)
                            window.removeEventListener('mouseup', colRes_)
                        } catch {}
                    }
                    styleElement.innerHTML = '*{cursor:col-resize !important}'
                    window.addEventListener('mousemove', colResize)
                    window.addEventListener('mouseup', colRes_)
                }
                colResizeBar.oncontextmenu = ev => {
                    ev.cancelBubble = true
                    ev.preventDefault()
                    if (colResizeBar.oncontextmenu.flag || colResizeBar.onmousedown.flag) return
                    colResizeBar.oncontextmenu.flag = true

                    const oldT = this.style.transition
                    if (!ev.transition) {
                        this.style.transition = 'width .1s ease-out, left .1s ease-out, right .1s ease-out'
                    }
                    setTimeout(() => {
                        this.style.transition = oldT
                        colResizeBar.oncontextmenu.flag = false
                    }, 100)

                    this.style.width = this.originalWidth + 'px'
                    this.style[ this.style.left ? 'left' : 'right' ] =
                        limit(this.style.left ? this.offsetLeft : window.innerWidth - this.clientWidth - this.offsetLeft,
                            this.scope?.left ?? 0, this.scope?.right ?? window.innerWidth - this.clientWidth) + 'px'
                }
                colResizeBar.ondblclick = ev => {
                    ev.cancelBubble = true
                    if (colResizeBar.ondblclick.flag) return
                    colResizeBar.ondblclick.flag = true
                    const oldT = this.style.transition
                    if (!ev.transition) {
                        this.style.transition = 'width .1s ease-out, left .1s ease-out, right .1s ease-out'
                    }
                    setTimeout(() => {
                        this.style.transition = oldT
                        colResizeBar.ondblclick.flag = false
                    }, 100)

                    const w = this.style.transform === 'scaleX(-1)' || this.style.transform === 'scale(-1)'
                    ? this.offsetLeft + this.clientWidth : window.innerWidth - this.offsetLeft
                    this.style.width = w + 'px'
                    this.style[ this.style.left ? 'left' : 'right' ] =
                        limit(this.style.left ? this.offsetLeft : window.innerWidth - this.clientWidth - this.offsetLeft,
                            this.scope?.left ?? 0, this.scope?.right ?? window.innerWidth - this.clientWidth) + 'px'
                }

                nwseResizeBlock.onmousedown = e => {
                    rowResizeBar.onmousedown(e)
                    colResizeBar.onmousedown(e)
                    const nwseResize = () => {
                        const t = this.style.transform
                        styleElement.innerHTML = t === 'scaleX(-1)' || t === 'scaleY(-1)'
                            ? '*{cursor:nesw-resize !important}' : '*{cursor:nwse-resize !important}'
                    }, nwseRes_ = () => {
                        styleElement.innerHTML = ''
                        try {
                            window.removeEventListener('mousemove', nwseResize)
                            window.removeEventListener('mouseup', nwseRes_)
                        } catch {}
                    }
                    nwseResize()
                    window.addEventListener('mousemove', nwseResize)
                    window.addEventListener('mouseup', nwseRes_)
                }
                nwseResizeBlock.oncontextmenu = e => {
                    const oldT = this.style.transition
                    e.transition = true
                    this.style.transition = 'all .1s ease-out'
                    rowResizeBar.oncontextmenu(e)
                    colResizeBar.oncontextmenu(e)
                    setTimeout(() => {
                        this.style.transition = oldT
                    }, 100)
                }
                nwseResizeBlock.ondblclick = e => {
                    const oldT = this.style.transition
                    e.transition = true
                    this.style.transition = 'all .1s ease-out'
                    rowResizeBar.ondblclick(e)
                    colResizeBar.ondblclick(e)
                    setTimeout(() => {
                        this.style.transition = oldT
                    }, 100)
                }
            },
            configurable: true
        },
        makeMoveable: {
            value(scope) {
                let dx = 0, dy = 0
                this.scope = scope
                Object.defineProperty(this, 'makeMoveable', { value: void 0 })
                const mousemoveF = e => {
                    this.style[ this.style.left ? 'left' : 'right' ] = limit(this.style.left ? dx + e.x : window.innerWidth - e.x - dx - this.clientWidth, scope.left, scope.right) + 'px'
                    this.style[ this.style.top ? 'top' : 'bottom' ] = limit(this.style.top ? dy + e.y : window.innerHeight - e.y - dy - this.clientHeight, scope.top, scope.bottom) + 'px'
                }, mouseupF = () => {
                    this.style.cursor = 'grab'
                    styleElement.innerHTML = ''
                    try {
                        window.removeEventListener('mousemove', mousemoveF)
                    } catch {}
                }
                this.onmousedown = ev => {
                    this.parentElement.appendChild(this)
                    if (ev.buttons !== 1) return
                    styleElement.innerHTML = '*{cursor:grabbing !important}';
                    [ dx, dy ] = [ this.offsetLeft - ev.x, this.offsetTop - ev.y ]
                    this.style.cursor = 'grabbing'
                    window.addEventListener('mousemove', mousemoveF)
                    window.addEventListener('mouseup', mouseupF, { once: true })
                }
                this.oncontextmenu = e => {
                    e.preventDefault()
                    this.oncontextmenu = e => e.preventDefault()
                    mouseupF()
                    this.style.transition = 'all .2s cubic-bezier(.68,-0.55,.5,.5)'
                    this.style.scale = (this.style.scale || 1) / 2 + ''
                    this.style.opacity = '0'
                    setTimeout(() => this.remove(), 250)
                }
            },
            configurable: true
        }
    })

    class Scope {
        constructor(o) {
            const props = {}, mapper = { 'function': 'get', 'number': 'value' };
            [ 'left', 'right', 'top', 'bottom' ].forEach(i => Object.keys(mapper)
                .includes(typeof o?.[i]) && (props[i] = { [ mapper[typeof o[i]] ]: o[i] }))
            Object.defineProperties(this, props)
        }
    }

    class ItemCreater {

        image(x, y, data) {
            const img = document.createElement('img'),
                div = document.createElement('div')
            img.classList.add('lgx-float-item')
            div.classList.add('lgx-float-div')
            div.appendChild(img)

            let w, h
            img.draggable = false
            img.src = URL.createObjectURL(data)
            img.onload = () => {
                // init
                [w, h] = [img.width, img.height];
                [div.style.left, div.style.top, div.style.width, div.style.height] =
                    [x - w / 4 + 'px', y - h / 4 + 'px', w / 2 + 'px', h / 2 + 'px']
                div.style.transition = 'all .3s cubic-bezier(.5,.5,.27,1.55)'
                setTimeout(() => {
                    [div.style.width, div.style.height] = [w + 'px', h + 'px']
                    div.style.left = limit(0, x - w / 2, window.innerWidth - w) + 'px'
                    div.style.top = limit(0, y - h / 2, window.innerHeight - h) + 'px'
                    div.style.opacity = '1'
                }, 50)
                // add event handler
                setTimeout(() => {
                    const scope = new Scope({
                        left:   () => -(parseInt(div.style.width) || div.clientWidth) / 2,
                        right:  () => window.innerWidth - (parseInt(div.style.width) || div.clientWidth) / 2,
                        top:    () => -(parseInt(div.style.height) || div.clientHeight) / 2,
                        bottom: () => window.innerHeight - (parseInt(div.style.height) || div.clientHeight) / 2
                    })
                    div.style.transition = ''
                    div.makeScaleable()
                    div.makeResizeable()
                    div.makeMoveable(scope)
                }, 400)
            }
            return div
        }

        audio() {

        }

        video() {

        }

        text() {

        }

    }

    const creater = new ItemCreater

    Object.defineProperty(DataTransfer.prototype, 'toElements', {
        value({ x, y }) {
            const retArr = []
            if (this.files.length) {
                for (const f of this.files) {
                    const item = creater[ f.type.substring(0, f.type.indexOf('/')) ]?.(x, y, f)
                    item && retArr.push(item)
                }
            }
            // dataTransfer.types
            return retArr
        },
    })

    window.addEventListener('drop', ev => {
        ev.preventDefault()
        const elements = ev.dataTransfer.toElements(ev)
        elements.forEach(el => floatingContainer.appendChild(el))
    })

    window.addEventListener('resize', () => {
        for (const div of floatingContainer.children) {
            div.style[ div.style.left ? 'left' : 'right' ] =
                limit(div.style.left ? div.offsetLeft : window.innerWidth - div.clientWidth - div.offsetLeft,
                    div.scope?.left ?? 0, div.scope?.right ?? window.innerWidth - div.clientWidth) + 'px'
            div.style[ div.style.top ? 'top' : 'bottom' ] =
                limit(div.style.top ? div.offsetTop : window.innerHeight - div.clientHeight - div.offsetTop,
                    div.scope?.top ?? 0, div.scope?.bottom ?? window.innerHeight - div.clientHeight) + 'px'
        }
    })

    function limit(value, min, max) {
        return Math.min(Math.max(value, min), max)
    }

})();