uploadImgeToGitlab

upload imge to gitlab

// ==UserScript==
// @name         uploadImgeToGitlab
// @namespace    http://tampermonkey.net/
// @version      0.12
// @description  upload imge to gitlab
// @author       shinwoow & axia
// @match        https://knowledgeplanet.genew.com/-/ide/project/visulization/project-web-components-store/**/publish/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @require      https://code.jquery.com/jquery-2.1.4.min.js
// @license      MIT
// ==/UserScript==


const imgSub = ['png', 'jpg', 'jpeg', 'tif', 'psd', 'icon']

const preUrl = '/store/api/' // http://newdev.rdapp.com:10066
const gitPreUrl = 'https://knowledgeplanet.genew.com/visulization/project-web-components-store/raw/publish/dist/'

const listenDist = ['dist/coverImages/', 'dist/images']

const listNameMap = {
    coverImages: '封面图',
    images: '定制库'
}

let jsonData = []

let curUploadPath = ''
let curDisplayPath = ''
let curDisplayStyle = ''
let showStatus = ''

// 拦截响应
var originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
    // 全部请求相关信息
    var self = this;

    // 监听readystatechange事件
    this.addEventListener('readystatechange', function() {
        // 当readyState变为4时获取响应
        if (self.readyState === 4) {
            if(self.responseURL.includes('publish?format=json')){
                jsonData = JSON.parse(self.response).filter(item => listenDist.some(i => item.startsWith(i)))
            }
        }

    })

    // 调用原始的send方法
    originalSend.apply(this, arguments);
};

async function sleep(ms){
    return new Promise(resolve => setTimeout(() => {console.log('等待' + ms + 'ms');resolve()}, ms))
}

function replacePreCustomStr(str) {
    const list = Object.keys(listNameMap)
    for(let i = 0; i< list.length; i++) {
        if(str.startsWith(list[i])) {
            return str.replace(list[i], listNameMap[list[i]])
        }
    }

    return str
}

function resetPreCustomStr(str) {
    let list = []
    let keys = []

    Object.entries(listNameMap).forEach(([key, val]) => {
        list.push(val)
        keys.push(key)
    })
    for(let i = 0; i< list.length; i++) {
        if(str.startsWith(list[i])) {
            return str.replace(list[i], keys[i])
        }
    }
}

function findElByInnerText(el, text) {
    if(Array.isArray(text)) {
        for(let i = 0; i< text.length; i++) {
            const f = Array.prototype.filter.call(el, item => item.innerText.trim() === text[i]).at(0)

            if(f) return f
        }
    } else {
        return Array.prototype.filter.call(el, item => item.innerText.trim() === text).at(0)
    }
}


function toast(msg) {
    const toastEl = document.createElement('div')
    toastEl.className = 'shin-toast'

    toastEl.innerHTML = `
        <p>${msg}</p>
    `
    document.body.append(toastEl)

    setTimeout(() => {toastEl.remove()}, 1500)
}

// 展开所有 folder
async function openImageFolder (level = 0) {
    const folderList = document.getElementsByClassName('file-row folder')

    let count = 0
    Array.prototype.forEach.call(folderList, item => {
        if(!item.classList.contains('is-open')){
            item.click()
            count++
        }
    })

    if(level < 3 && count) {
        await sleep(100)
        await openImageFolder(++level)
    }
}



async function uploadImge() {
    // 等待页面 dom 加载完毕
    while(!document.getElementsByClassName('file-row folder')){
        await sleep(1000)
    }

    await openImageFolder()

    await sleep(100)

    const folderElList = document.getElementsByClassName('file-row folder')
    const transPath = resetPreCustomStr(curUploadPath)
    const imageFolderEl = transPath.split('/').reduce((pre, cur) => findElByInnerText(folderElList, cur), folderElList)
    console.log(transPath)

    const menuListEl = imageFolderEl.getElementsByClassName('dropdown-menu dropdown-menu-right')[0]
    const uploadBtn = findElByInnerText(menuListEl.children, ['上传文件', 'Upload file'])
    const uploadInput = uploadBtn.querySelector('#file-upload')
    uploadInput.click()

    async function inputLinsten (...rest) {
        uploadInput.removeEventListener("input", inputLinsten) // 取消监听

        const commitBtn = document.querySelector('.qa-begin-commit-button')

        try {
            // 自动提交
            await sleep(100)
            const commitBtn = document.querySelector('.qa-begin-commit-button')
            commitBtn.click()

            await sleep(100)
            const pushBtn = document.querySelector('.qa-commit-button')
            pushBtn.click()
        } catch(err) {
            alert('自动提交失败,请点击左下角手动提交')

        }
    }


    uploadInput.addEventListener("input", inputLinsten)
}

function getImgList() {
    curDisplayPath ||= getfolderList().at(0)
    const imgNamelist = jsonData.reduce((pre, cur) => {
        if(cur.replace(`dist/${curDisplayPath}/`, '').indexOf('/') === -1) {
            pre.push(cur.split('/').pop())
        }
        return pre
    }, [])

    return imgNamelist
}

const getImgItem = (name) => {
    if(!name) {
        return '<div style="width: 200px;height:0;"></div>'
    }

    return `
        <div class="img-item-content">
            <img loading="lazy" class="img-item" src="${gitPreUrl + curDisplayPath}/${name}" alt="加载失败,请稍后再试" />
            <div style="cursor: pointer;color: rgba(0,0,0,0.8); font-size: 20px;" class="copy-name singe-line" title="${name}">
                ${name}
            </div>
        </div>
    `
}

const copy = (text) => {
    // 挂载一个不可见元素
    const target = document.createElement('input');
    target.readOnly = 'readonly';
    target.setAttribute('value', text);
    target.id = 'tempTarget';
    target.style.opacity = '0';
    target.style.position = 'fixed';
    target.style.left = '-9999px';
    target.style.top = '0px';
    target.style.zIndex = '-9999';
    document.body.appendChild(target);

    if (target.value === '') {
        console.log('copy 空字符串无法被复制');
        toast('空字符串无法被复制')
    } else {
        try {
            // 选中、复制操作
            const selected =
                  document.getSelection().rangeCount > 0
            ? document.getSelection().getRangeAt(0)
            : false;
            target.focus();
            target.select();
            document.execCommand('copy');
            target.setSelectionRange(0, 0);
            target.blur();

            if (selected) {
                window.getSelection().removeAllRanges();
                document.getSelection().addRange(selected);
            }

            toast('复制成功')
        } catch (e) {
            toast('复制失败')
        }
    }

    // 移除添加的元素
    document.body.removeChild(target);
}

function setStyle () {
    // 样式
    const styleEl = document.createElement('style')
    styleEl.setAttribute('type', 'text/css')
    styleEl.innerHTML = `
        .white-background {
            background: #fff;
            opacity: 1 !important;
        }
        .shin-close {
            font-size: 66px;
            font-weight: 100;
            position: absolute;
            z-index: 10087;
            right: 8px;
            top: -18px;
        }
        .shin-scroll-bar::-webkit-scrollbar-track {
            -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
            border-radius: 10px;
            background-color: #fff;
        }
        .shin-scroll-bar::-webkit-scrollbar {
            width: 5px;
            height: 5px;
            background-color: #fff;
        }
        .shin-scroll-bar::-webkit-scrollbar-thumb {
            border-radius: 10px;
            -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, .3);
            background-color: #555;
        }
        .img-list {
            position: fixed;
            bottom: 0;
            height: 180px;
            display: flex;
            overflow: auto;
            white-space: nowrap;
            background: #fff;
            padding: 4px;
            width:100%;
        }
        .img-item-content {
            flex: 1;
            text-align: center;
            border: 1px solid transparent;
        }
        .img-item-content:hover .img-item {
            border-color: blue;
        }
        .img-item {
            width: 200px;
            height: 120px;
            display: inline-block;
            cursor: pointer;
            border: 1px solid transparent;
            border-radius: 4px;
            box-shadow: 12px 17px 51px rgba(0, 0, 0, 0.22);
            transition: .3s all ease-in-out;
        }
        .img-item-content:hover .img-item {
            border-color: #000;
            transform: scale(1.05);
            box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.22);
        }
        .img-preview {
            position: relative;
            margin: 0 55px;
            height: calc(100% - 226px);
        }
        .img-title-wrapper {
            background: #fff;
            padding: 8px 16px;
            width: 100%;
            overflow: hidden;
            box-shadow: 0px 1px 2px #1e1f21;
            position: relative;
        }
        #img-title {
            font-size: 20px;
            color: rgba(0,0,0,0.9);
        }
        #shin-img-size {
            border: 1px solid green;
            background: green;
            padding: 2px 4px;
            color: #fff;
            border-radius: 4px;
        }
        #shin-img-preview {
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%, -50%);
            max-height: calc(100% - 180px);
            cursor: pointer;
        }
        .shin-before,
        .shin-after{
            color: #000;
            opacity: 0.1;
            font-size: 50px;
            font-weight: 100;
            margin: auto 0;
            cursor: pointer;
            padding-bottom: 50px;
            transform: translateX(0px);
            transition: all .1s;
        }
        .shin-before:hover,
        .shin-after:hover {
            opacity: 0.8;
        }
        .shin-before:hover {
            transform: translateX(-3px);
        }
        .shin-after:hover {
            transform: translateX(3px);
        }
        .shin-selete-display-folder {
            position: absolute;
            top: 50%;
            right: 180px;
            transform: translateY(-50%);
        }
        .img-list-content {
            flex: 1;
            display: flex;
            padding: 4px 0;
        }
        .shin-toast {
            border: 1px solid #ebeef5;
            border-radius: 4px;
            padding: 8px 10px;
            z-index: 1080;
            color: #fff;
            box-sizing: border-box;
            min-width: 380px;
            position: fixed;
            left: 50%;
            top: 20px;
            transform: translateX(-50%);
            background-color: #edf2fc;
            transition: opacity 0.3s, transform .4s, top 0.4s;
            overflow: hidden;
            padding: 15px 15px 15px 20px;
            display: flex;
            align-items: center;
        }
        .shin-toast p {
            padding-right: 16px;
            margin: 0;
            color: #909399;
        }
        .img-list-wrap {
            display: flex;
            flex-wrap: wrap;
            padding: 24px;
            height: calc(100vh - 26px);
            overflow: auto;
            scrollbar-width: none; // firefox
            -ms-overflow-style: none; // ie10+
        }
        .img-list-wrap::-webkit-scrollbar {
            display: none; // chrome safari
        }
        .singe-line {
            max-width: 200px;
            text-overflow: ellipsis;
            overflow: hidden;
            word-break: break-all;
            white-space: nowrap;
        }
        .img-display-mode {
            position: absolute;
            top: 50%;
            right: 80px;
            transform: translateY(-50%);
            display: flex;
            font-size: 14px;
            border: 2px solid #8b8b8b;
            border-radius: 4px;


        }
        .img-display-mode-item {
            flex: 1;
            padding: 2px 8px;
            cursor: pointer;
            color: #666666;
        }
        .img-display-mode > .is-active {
            color: #ffaa00;
            transform: scale(1.05);
            background: #01499c;
        }
    `

    document.body.append(styleEl)
}
const sizeLevel = ['b', 'kb', 'mb']
function calcSize(size, level = 0) {
    if( size < 1024 || level >= 2) {
        return size.toFixed(2) + sizeLevel[level]
    }else{
        return calcSize(size/1024, level + 1)
    }
}

async function setTitle(str) {
    const imgTitle= document.querySelector('#img-title')
    const imgSize = document.querySelector('#shin-img-size')
    imgTitle.innerText = str
    imgSize.innerText = '0'

    try {
        const { size } = await (await fetch(`http://git.rdapp.com/visulization/project-web-components-store/blob/publish/dist/${curDisplayPath}/${str}?format=json&viewer=none`)).json()
        imgSize.innerText = calcSize(size)
        imgSize.style.background = size <= 200 * 1024 ? 'green' : '#ffaa00';
    } catch(e) {
        imgSize.innerText = '加载失败'
    }
}

async function showImg() {
    // mask
    const maskEl = document.createElement('div')
    maskEl.className = 'modal-backdrop fade show shin-modal white-background'

    // dialog
    const wrapEl = document.createElement('div')
    wrapEl.className = 'modal fade show'
    wrapEl.style = 'display: block;'
    wrapEl.id = "shin-script-wrapper"

    let arr = getImgList()

    const browserWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
    const useWidth = browserWidth - 33.5 * 2
    const imgWidth = 200 + 4 // width + margin

    const maxImgCount = Math.floor(useWidth / imgWidth)

    // 页码配置
    let curPage = 0
    let totalPage = Math.ceil(arr.length / maxImgCount)

    const getSinglePageImg = () => {
        const a = arr.slice(curPage * maxImgCount, (curPage + 1) * maxImgCount)
        a.push(...new Array(maxImgCount - a.length))
        return a
    }

    // 展示状态
    let displayStatus = curDisplayStyle || 'preview' // 'list'

    const imgListEl = getSinglePageImg().reduce((pre, cur) => pre + getImgItem(cur), '')

    function replaceImgList (index = 0) {
        const cil = getSinglePageImg()
        const ilEL = cil.reduce((pre, cur) => pre + getImgItem(cur), '')
        document.querySelector('.img-list-content').innerHTML = ilEL

        // 替换大图
        const imgPreview = document.querySelector('#shin-img-preview')
        imgPreview.setAttribute('src', gitPreUrl + curDisplayPath + '/' + cil.at(index))

        // 替换标题
        setTitle(cil.at(index))
    }

    function addPage () {
        curPage = curPage >= (totalPage - 1) ? 0 : ++curPage
    }

    function subPage() {
        curPage = curPage <= 0 ? totalPage - 1 : --curPage
    }

    function setCurPageByIndex(index) {
        curPage = Math.floor(index / maxImgCount)
    }

    function changeMode (type) {
        if(!type) return

        const elList = document.querySelector('#img-display-mode').children

        // 修改高亮
        for(let i = 0; i < elList.length; i++) {
            const el = elList[i]
            if(el.classList.contains('is-active')) {
                el.classList.remove('is-active')
            }

            if(el.dataset.type === type) {
                el.classList.add('is-active')
            }
        }

        const wrapEl = document.querySelector('#shin-script-wrapper')

        curDisplayStyle = type

        // 展示状态
        switch(type) {
            case 'preview': {
                wrapEl.querySelector('.img-preview').style.display = 'block'
                wrapEl.querySelector('.img-list').style.display = 'flex'
                wrapEl.querySelector('.img-list-wrap').style.display = 'none'
                break;
            }
            case 'list': {
                wrapEl.querySelector('.img-preview').style.display = 'none'
                wrapEl.querySelector('.img-list').style.display = 'none'
                wrapEl.querySelector('.img-list-wrap').style.display = 'flex'
                break;
            }
        }

    }

    function setPage (arr) {
        const dialogStr = `
        <div class="img-title-wrapper">
            <span id="img-title" class="singe-line">
                ${arr.at(0)}
            </span>

            <span id="shin-img-size" style="background: green;">
                0
            </span>

            <select class="shin-selete-display-folder">
                ${getfolderList().reduce((pre, cur) => pre + `<option value=${cur}>${replacePreCustomStr(cur)}</option>`, '')}
            </select>

            <div id="img-display-mode" class="img-display-mode">
                <div class="img-display-mode-item" data-type="list">
                    列表
                </div>
                <div class="img-display-mode-item is-active" data-type="preview">
                    相册
                </div>
            </div>
            <button aria-label="关闭" type="button" data-dismiss="modal" class="close js-modal-close-action shin-close">
                <span aria-hidden="true">×</span>
            </button>
        </div>

        <div class="img-preview" style="display: block;">
            <img id="shin-img-preview" src="${gitPreUrl}${curDisplayPath}/${arr.at(0)}" alt="加载失败,请稍后再试" />
        </div>

        <div class="img-list" style="display: flex;">
            <span class="shin-before"><</span>
            <div class="img-list-content" >
                ${imgListEl}
            </div>
            <span class="shin-after">></span>
        </div>

        <div class="img-list-wrap" style="display: none;">
            ${[...arr, ...new Array(10)].reduce((pre, cur) => pre + getImgItem(cur), '')}
        </div>
        `
         wrapEl.innerHTML = dialogStr
    }

    setPage(getImgList())

    function close () {
        maskEl.remove()
        wrapEl.remove()
    }

    function changeDisplayState (state) {
        displayStatus = state
        switch(displayStatus) {
            case 'preview': {
                wrapEl.querySelector('.img-preview').style.display = 'block'
                wrapEl.querySelector('.img-list').style.display = 'flex'
                wrapEl.querySelector('.img-list-wrap').style.display = 'none'
                break;
            }
            case 'list': {
                wrapEl.querySelector('.img-preview').style.display = 'none'
                wrapEl.querySelector('.img-list').style.display = 'none'
                wrapEl.querySelector('.img-list-wrap').style.display = 'flex'
                break;
            }
        }
    }

    function setImgListDom () {
        wrapEl.querySelector('.img-list-wrap').innerHTML = [...arr, ...new Array(10)].reduce((pre, cur) => pre + getImgItem(cur), '')
    }

    wrapEl.querySelector('.img-display-mode').onclick = e=> changeMode(e.target.dataset.type)

    wrapEl.querySelector('.js-modal-close-action').onclick = close

    wrapEl.querySelector('.img-preview').onclick = function (e) {
        if(e.target.className === 'img-preview') {
            close()
        }
        if(e.target.id) {
            const imgName = e.target.innerText || e.target.src.split('/').pop()
            copy(preUrl+ curDisplayPath + '/' + imgName)
        }
        e.stopPropagation()
        e.preventDefault()
    }

    wrapEl.querySelector('.shin-before').onclick = function() {
        // 上一页
        subPage()
        replaceImgList()
    }
    wrapEl.querySelector('.shin-after').onclick = function() {
        // 下一页
        addPage()
        replaceImgList()
    }

    wrapEl.querySelector('.img-list').onclick = function(e) {
        // 点击图片
        if(e.target.nodeName === 'IMG') {
            // 替换大图
            const imgPreview = document.querySelector('#shin-img-preview')
            imgPreview.setAttribute('src', '#')
            imgPreview.setAttribute('src', e.target.currentSrc)

            // 替换标题
            setTitle(e.target.currentSrc.split('/').pop())
        }

        if(e.target.classList.contains('copy-name')) {
            // 复制链接
            const imgName = e.target.innerText
            copy(preUrl + curDisplayPath + '/' + imgName)
        }

        e.stopPropagation()
        e.preventDefault()
    }

    const folderEl = wrapEl.querySelector('.shin-selete-display-folder')
    folderEl.onchange = function(e) {
        curDisplayPath = folderEl.value
        arr = getImgList()
        curPage = 0
        totalPage = Math.ceil(arr.length / maxImgCount)
        replaceImgList()
        setImgListDom()
    }
    folderEl.value = curDisplayPath

    wrapEl.querySelector('.img-list-wrap').onclick = function(e) {
        // 点击图片
        if(e.target.nodeName === 'IMG') {
            // 查询、修改页码
            const imgName = e.target.currentSrc.split('/').pop()
            const imgIndex = arr.indexOf(imgName)
            setCurPageByIndex(imgIndex)
            replaceImgList(imgIndex % maxImgCount)

            changeMode('preview')
        }

        if(e.target.classList.contains('copy-name')) {
            // 复制链接
            const imgName = e.target.innerText
            copy(preUrl + curDisplayPath + '/' + imgName)
        }

        e.stopPropagation()
        e.preventDefault()
    }

    document.body.append(maskEl)
    document.body.append(wrapEl)

    setTitle(arr.at(0))

    setStyle()

    // 恢复预览状态
    changeMode(curDisplayStyle)
}



async function showImgeList() {
    const imgList = getImgList() || []
    showImg(imgList)
}

function getfolderList() {
    return jsonData.reduce((pre, cur) => {
        const path = cur.split('/').slice(1,-1).join('/')
        if(!pre.includes(path)) {
            pre.push(path)
        }
        return pre
    }, [])
}

function createEl () {
    const ul = document.getElementsByClassName('navbar-sub-nav')[0];

    const uploadBtn = document.createElement('li')

    uploadBtn.innerHTML = `
        <button data-toggle="dropdown" type="button">
            上传图片
    <svg class="caret-down"><use xlink:href="/assets/icons-24aaa921aa9e411162e6913688816c79861d0de4bee876cf6fc4c794be34ee91.svg#angle-down"></use></svg>
        </button>

    <div class="dropdown-menu frequent-items-dropdown-menu">
        <div class="frequent-items-list-container">
            <ul class="list-unstyled">
                <li class="frequent-items-list-item-container">
                    <a href="/visulization/visualization-display-platform" class="clearfix">
                    </a>
                </li>
            </ul>
        </div>
    </div>
    `

    uploadBtn.className = 'home dropdown header-projects qa-projects-dropdown'

    uploadBtn.querySelector('button').onclick = () => {
        const str = getfolderList().reduce((pre, cur) => {
            return `
                ${pre}
    <li class="frequent-items-list-item-container">
        <a class="clearfix">
            ${replacePreCustomStr(cur)}
    </a>
    </li>
    `
        }, '')

        uploadBtn.querySelector('.list-unstyled').innerHTML = str
    }

    uploadBtn.querySelector('.list-unstyled').onclick = (e) => {
        curUploadPath = e.target.innerText
        uploadImge()
    }

    ul.append(uploadBtn)

    const imgDisplayBtn = document.createElement('li')
    imgDisplayBtn.className = 'd-none d-sm-block'

    const imgEl = document.createElement('a')

    imgEl.text = '显示封面图'
    imgEl.onclick = showImgeList
    imgDisplayBtn.append(imgEl)
    ul.append(imgDisplayBtn)
}

(function() {
    'use strict';

    createEl()
})();