BBS_blogscn 编辑器增强

管理员已认证 湘铭呀! 1月前 63


本脚本目前支持:
粘贴图片上传
多图片批量上传
拖拽图片到编辑器内上传

图床

脚本内置的是 这位大佬 的 LskyPro 图床, 感谢大佬免费高速的图片托管服务
目前支持 LskyPro, Telegraph 和 EasyImages 程序, 可以打开脚本按照说明修改配置为其他同程序的图床

以下代码添加到油猴插件即可使用

// ==UserScript==
// @name         BBS_blogscn 编辑器增强
// @namespace    https://bbs.blogscn.fun/
// @version      0.0.1
// @description  为 BBS_blogscn 编辑器增加图片上传功能
// @author       xiangmingya
// @match        *://bbs.blogscn.fun/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        GM_xmlhttpRequest
// @license      MPL-2.0 License
// ==/UserScript==

(function () {
    'use strict';

    const imgHost = {
        type: "LskyPro",
        url: "https://image.dooo.ng",
        token: null,
        storageId: null,
    };
    const mdImgName = 0;
    const submitByKey = true;

    window.addEventListener('load', initEditorEnhancer, false);

    function initEditorEnhancer() {
        document.addEventListener('paste', handlePasteEvt);

        var dropZone = document.querySelector('textarea#message');
        dropZone.addEventListener('dragover', function (e) {
            e.preventDefault();
            e.stopPropagation();
            e.dataTransfer.dropEffect = 'copy';
        });

        dropZone.addEventListener('drop', function (e) {
            e.preventDefault();
            e.stopPropagation();

            let imageFiles = [];
            for (let file of e.dataTransfer.files) {
                if (/^image\//i.test(file.type)) {
                    imageFiles.push(file);
                }
            }
            if (imageFiles.length === 0) {
                log('你拖放的内容好像没有图片哦', 'red');
                return;
            }

            uploadImage(imageFiles.map(file => ({
                kind: 'file',
                type: file.type,
                getAsFile: () => file
            })));
        });

        let checkExist = setInterval(function () {
            const oldElement = document.querySelector('.toolbar-item.i-icon.i-icon-pic[title="图片"]');
            if (oldElement) {
                clearInterval(checkExist);
                const newElement = oldElement.cloneNode(true);
                oldElement.parentNode.replaceChild(newElement, oldElement);
                newElement.addEventListener('click', handleImgBtnClick);
            }
        }, 200);

        if (submitByKey) {
            document.addEventListener('keydown', function (event) {
                if (event.ctrlKey && event.key === 'Enter') {
                    const button = document.querySelector('.submit.btn');
                    button.click();
                }
            });
        }
    }

    function handlePasteEvt(event) {
        log('正在处理粘贴内容...');
        const items = (event.clipboardData || event.originalEvent.clipboardData).items;
        if (items.length === 0) {
            log('你粘贴的内容好像没有图片哦', 'red');
            return;
        }
        uploadImage(items);
    }

    function handleImgBtnClick() {
        const input = document.createElement('input');
        input.type = 'file';
        input.multiple = true;
        input.accept = 'image/*';

        input.onchange = e => {
            const files = e.target.files;
            if (files.length) {
                const items = [...files].map(file => ({
                    kind: 'file',
                    type: file.type,
                    getAsFile: () => file
                }));

                uploadImage(items);
            }
        };

        input.click();
    }

    async function uploadImage(items) {
        let imageFiles = [];

        for (let item of items) {
            if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
                let blob = item.getAsFile();
                imageFiles.push(blob);
            }
        }

        if (imageFiles.length > 0) {
            event.preventDefault();
            for (let i = 0; i < imageFiles.length; i++) {
                if (imageFiles.length > 1) {
                    log(`上传第 ${i + 1} / ${imageFiles.length} 张图片...`);
                } else {
                    log(`上传图片...`);
                }
                let file = imageFiles[i];
                let formData = new FormData();
                formData.append('file', file);
                if (imgHost.type === 'LskyPro') {
                    if (imgHost.storageId) formData.append('strategy_id', imgHost.storageId);
                    await uploadToLsky(formData);
                } else if (imgHost.type === 'Telegraph') {
                    await uploadToTelegraph(formData);
                } else if (imgHost.type === 'EasyImages') {
                    await uploadToEasyImages(file);
                } else {
                    log(`暂不支持的图床类型: ${imgHost.type}, 取消上传`, 'red');
                    return;
                }
            }
        } else {
            log('你粘贴的内容好像没有图片哦', 'red');
        }
    }

    async function uploadToLsky(formData) {
        return new Promise((resolve, reject) => {
            let headers = {
                'Accept': 'application/json'
            };
            if (imgHost.token) {
                headers['Authorization'] = `Bearer ${imgHost.token}`;
            }

            GM_xmlhttpRequest({
                method: 'POST',
                url: `${imgHost.url}/api/v1/upload`,
                headers: headers,
                data: formData,
                onload: (rsp) => {
                    let rspJson = JSON.parse(rsp.responseText);
                    if (rsp.status !== 200) {
                        log(`图片上传失败: ${rsp.status} ${rsp.statusText}`, 'red');
                        reject(rspJson.message);
                    }
                    if (rspJson.status === true) {
                        if (rspJson?.data?.links?.markdown) {
                            insertToEditor(mdImgName === 0 ? rspJson.data.links.markdown : `![${mdImgName}](${rspJson.data.links.url})`);
                        } else {
                            log('图片上传成功, 但接口返回有误, 原始返回已粘贴到编辑器', 'red');
                            insertToEditor(`图片上传成功, 但接口返回有误: ${JSON.stringify(rspJson)})`);
                        }
                    } else {
                        log(`图片上传失败: ${rspJson.message}`, 'red');
                    }
                    resolve();
                },
                onerror: (error) => {
                    log(`图片上传失败: ${error.status} ${error.statusText}`, 'red');
                    reject(error);
                }
            });
        });
    }

    async function uploadToTelegraph(formData) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: `${imgHost.url}/upload`,
                data: formData,
                onload: (rsp) => {
                    let rspJson = JSON.parse(rsp.responseText);
                    rspJson = rspJson[0];
                    if (rsp.status !== 200) {
                        log(`图片上传失败: ${rsp.status} ${rsp.statusText}`, 'red');
                        reject(rspJson.message);
                    }
                    if (rspJson) {
                        if (rspJson?.src) {
                            insertToEditor(`![${mdImgName}](${imgHost.url}${rspJson.src})`);
                        } else {
                            log('图片上传成功, 但接口返回有误, 原始返回已粘贴到编辑器', 'red');
                            insertToEditor(`图片上传成功, 但接口返回有误: ${JSON.stringify(rspJson)})`);
                        }
                    } else {
                        log(`图片上传失败: ${JSON.stringify(rspJson)}`, 'red');
                    }
                    resolve();
                },
                onerror: (error) => {
                    log(`图片上传失败: ${error.status} ${error.statusText}`, 'red');
                    reject(error);
                }
            });
        });
    }

    async function uploadToEasyImages(file) {
        return new Promise((resolve, reject) => {
            let url = imgHost.url;
            let formData = new FormData();
            if (imgHost.token) {
                url += '/api/index.php';
                formData.append('token', imgHost.token);
                formData.append('image', file);
            } else {
                url += '/app/upload.php';
                formData.append('file', file);
                formData.append('sign', Math.floor(Date.now() / 1000));
            }

            GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                data: formData,
                onload: (rsp) => {
                    let rspJson = JSON.parse(rsp.responseText);
                    if (rsp.status !== 200) {
                        log(`图片上传失败: ${rsp.status} ${rsp.statusText}`, 'red');
                        reject(rspJson.result);
                    }
                    if (rspJson.code === 200) {
                        if (rspJson?.url) {
                            insertToEditor(`![${(mdImgName === 0 ? rspJson.srcName : mdImgName)}](${rspJson.url})`);
                        } else {
                            log('图片上传成功, 但接口返回有误, 原始返回已粘贴到编辑器', 'red');
                            insertToEditor(`图片上传成功, 但接口返回有误: ${JSON.stringify(rspJson)})`);
                        }
                    } else {
                        log(`图片上传失败: ${JSON.stringify(rspJson)}`, 'red');
                    }
                    resolve();
                },
                onerror: (error) => {
                    log(`图片上传失败: ${error.status} ${error.statusText}`, 'red');
                    reject(error);
                }
            });
        });
    }

    function insertToEditor(markdownLink) {
        const editor = document.querySelector('textarea#message');
        if (editor) {
            editor.value += `\n${markdownLink}`;
        }
        if (markdownLink.startsWith('![')) {
            log('图片已插入到编辑器~', 'green');
        }
    }

    function log(message, color = '') {
        if (!document.getElementById('editor-enhance-logs')) {
            initEditorLogDiv();
        }
        const logDiv = document.getElementById('editor-enhance-logs');
        logDiv.innerHTML = `<div${color ? ` style="color: ${color};` : ''}">&nbsp;&nbsp;&nbsp;${message}&nbsp;</div>`;

        console.log(`[NodeSeek-Editor-Enhance] ${message}`);
    }

    function initEditorLogDiv() {
        const logDiv = document.createElement('div');
        logDiv.id = 'editor-enhance-logs';
        logDiv.innerHTML = '';
        document.body.appendChild(logDiv);

        const editorToolbarDiv = document.querySelector('.mde-toolbar');
        editorToolbarDiv.appendChild(logDiv);
    }

})();
最新回复 (0)
返回
发新帖