您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
在NodeSeek论坛的编辑器中快速把图片上传到16图床并引用
// ==UserScript== // @name 16图床 for NodeSeek // @namespace https://www.nodeseek.com/ // @version 0.1.6 // @description 在NodeSeek论坛的编辑器中快速把图片上传到16图床并引用 // @author 湘铭呀! // @match *://www.nodeseek.com/* // @icon https://111666.best/favicon.ico // @grant GM_xmlhttpRequest // @license MPL-2.0 License // ==/UserScript== (function () { 'use strict'; // 图床配置 const imgHost = { type: "CustomImageHost", // 图床类型, 仅支持 16图床 (https://i.111666.best) url: "https://i.111666.best", // 图床地址, 带上协议头 token: "YOUR-TOKEN", // 图床 token, 可选, 用于 Auth-Token 头进行身份验证 }; const mdImgName = 0; // 0: 使用图片文件名, 其他值则名称固定为该值 const submitByKey = true; // 是否按下 Ctrl+Enter 后触发发帖动作 // 页面加载完毕后载入功能 window.addEventListener('load', initEditorEnhancer, false); function initEditorEnhancer() { // 监听粘贴事件 document.addEventListener('paste', (event) => handlePasteEvt(event)); // 给编辑器绑定拖拽事件 var dropZone = document.getElementById('code-mirror-editor'); // 阻止默认行为 dropZone.addEventListener('dragover', function (e) { e.preventDefault(); e.stopPropagation(); e.dataTransfer.dropEffect = 'copy'; // 显示为复制图标 }); // 处理文件拖放 dropZone.addEventListener('drop', function (e) { e.preventDefault(); e.stopPropagation(); log('正在处理拖放内容...'); let imageFiles = []; for (let file of e.dataTransfer.files) { if (/^image\//i.test(file.type)) { // 确保只处理图片文件 imageFiles.push(file); log(`拖放的文件名: ${file.name}`); } } log(`拖放的图片数量: ${imageFiles.length}`); if (imageFiles.length === 0) { log('你拖放的内容好像没有图片哦', 'red'); return; } // 调整uploadImage函数以接受File对象数组 uploadImage(imageFiles.map(file => { return { 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); // 监听 Ctrl+Enter 快捷键 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]; await uploadToCustomImageHost(file); } } else { log('你粘贴的内容好像没有图片哦', 'red'); } } // 上传到 16图床 async function uploadToCustomImageHost(file) { return new Promise((resolve, reject) => { let headers = { 'Accept': 'application/json' }; if (imgHost.token) { headers['Auth-Token'] = imgHost.token; } let formData = new FormData(); formData.append('image', file); GM_xmlhttpRequest({ method: 'POST', url: `${imgHost.url}/image`, headers: headers, data: formData, onload: (rsp) => { try { let rspJson = JSON.parse(rsp.responseText); if (rsp.status !== 200 || !rspJson.ok) { log(`图片上传失败: ${rsp.status} ${rsp.statusText}`, 'red'); reject(rspJson?.message || '上传失败'); return; } // Handle API response with 'src' field if (rspJson.src) { let imgName = mdImgName === 0 ? file.name : mdImgName; // Construct full URL by prepending imgHost.url to src let fullUrl = `${imgHost.url}${rspJson.src}`; insertToEditor(``); log('图片上传成功', 'green'); } else { log('图片上传成功, 但接口返回有误, 原始返回已粘贴到编辑器', 'red'); insertToEditor(`图片上传成功, 但接口返回有误: ${JSON.stringify(rspJson)}`); } resolve(); } catch (error) { log(`图片上传失败: 响应解析错误 ${error.message}`, 'red'); reject(error); } }, onerror: (error) => { log(`图片上传失败: ${error.status} ${error.statusText}`, 'red'); reject(error); } }); }); } // 插入到编辑器 function insertToEditor(markdownLink) { const codeMirrorElement = document.querySelector('.CodeMirror'); if (codeMirrorElement) { const codeMirrorInstance = codeMirrorElement.CodeMirror; if (codeMirrorInstance) { const cursor = codeMirrorInstance.getCursor(); codeMirrorInstance.replaceRange(`\n${markdownLink} \n`, cursor); } } 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};` : ''}"> ${message} </div>`; console.log(`[16ImageHosting-Editor-Enhancer] ${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); } })();