您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
为 NodeSeek 编辑器增加图片上传功能和Emoji选择器,使用个人CloudFlare ImgBed图床,支持上传频率限制
// ==UserScript== // @name NodeSeek 编辑器个人图床 + Emoji集成 // @namespace https://www.nodeseek.com/ // @version 0.1.2 // @description 为 NodeSeek 编辑器增加图片上传功能和Emoji选择器,使用个人CloudFlare ImgBed图床,支持上传频率限制 // @author astom // @match *://www.nodeseek.com/* // @icon https://www.nodeseek.com/static/image/favicon/android-chrome-192x192.png // @grant GM_xmlhttpRequest // @license MPL-2.0 License // ==/UserScript== (function () { 'use strict'; // 个人图床配置,先去部署图床,然后改下面前三个就行!!! const imgHost = { url: "https://xxxxx.org/upload", // 图床上传地址,将你的地址替换一下!!! authCode: "xxxxx", // 上传认证码,后台设置的,换成你自己的!!! domain: "https://xxxxx.org", // 图床域名,用于拼接完整链接,换成你自己的!!! uploadChannel: "telegram", // 上传渠道 serverCompress: true, // 服务端压缩 autoRetry: true, // 失败时自动重试 uploadNameType: "default", // 文件命名方式 returnFormat: "default" // 返回链接格式 }; const mdImgName = 0; // 0: 使用图床返回的原始名称, 其他值则名称固定为该值 const submitByKey = true; // 是否按下 Ctrl+Enter 后触发发帖动作 // 上传频率限制配置 let lastUploadTime = 0; // 记录上次上传时间戳 const uploadInterval = 10000; // 上传间隔限制:10秒(毫秒) // Emoji 配置 const emojiConfig = { pickerUrl: 'https://cdn.jsdelivr.net/npm/emoji-picker-element@^1/index.js', width: 380, height: 420 }; // 页面加载完毕后载入功能 window.addEventListener('load', initEditorEnhancer, false); function initEditorEnhancer() { // 监听粘贴事件 document.addEventListener('paste', (event) => handlePasteEvt(event)); // 给编辑器绑定拖拽事件 var dropZone = document.getElementById('code-mirror-editor'); if (dropZone) { // 阻止默认行为 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(imageFiles.map(file => { return { kind: 'file', type: file.type, getAsFile: () => file }; })); }); } // 修改图片按钮的行为并添加Emoji按钮 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); // 添加Emoji按钮 addEmojiButton(newElement.parentNode); } }, 200); // 监听 Ctrl+Enter 快捷键 if (submitByKey) { document.addEventListener('keydown', function (event) { if (event.ctrlKey && event.key === 'Enter') { const button = document.querySelector('.submit.btn'); if (button) button.click(); } }); } // 定期检查并确保Emoji按钮存在 setInterval(ensureEmojiButton, 2000); } // 粘贴事件处理 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) { // 检查上传频率限制 const now = Date.now(); const timeSinceLastUpload = now - lastUploadTime; if (lastUploadTime > 0 && timeSinceLastUpload < uploadInterval) { const remainingTime = Math.ceil((uploadInterval - timeSinceLastUpload) / 1000); log(`上传频率限制:请等待 ${remainingTime} 秒后再试`, 'orange'); return; } // 更新上传时间戳 lastUploadTime = now; 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 uploadToPersonalImgBed(file); } } else { log('你粘贴的内容好像没有图片哦', 'red'); } } // 上传到个人CloudFlare ImgBed图床 async function uploadToPersonalImgBed(file) { return new Promise((resolve, reject) => { let formData = new FormData(); formData.append('file', file); // 构建URL参数 const params = new URLSearchParams({ authCode: imgHost.authCode, serverCompress: imgHost.serverCompress, uploadChannel: imgHost.uploadChannel, autoRetry: imgHost.autoRetry, uploadNameType: imgHost.uploadNameType, returnFormat: imgHost.returnFormat }); GM_xmlhttpRequest({ method: 'POST', url: `${imgHost.url}?${params.toString()}`, data: formData, onload: (rsp) => { try { let rspJson = JSON.parse(rsp.responseText); if (rsp.status !== 200) { log(`图片上传失败: ${rsp.status} ${rsp.statusText}`, 'red'); reject(rsp.statusText); return; } if (Array.isArray(rspJson) && rspJson.length > 0 && rspJson[0].src) { // 图片上传成功 const imgUrl = `${imgHost.domain}${rspJson[0].src}`; const fileName = mdImgName === 0 ? file.name : mdImgName; insertToEditor(``); log('图片上传成功~', 'green'); // 确保Emoji按钮仍然存在 setTimeout(ensureEmojiButton, 100); } else { log('图片上传失败,接口返回格式异常', 'red'); insertToEditor(`图片上传失败,接口返回: ${JSON.stringify(rspJson)}`); } } catch (e) { log(`图片上传失败,解析响应出错: ${e.message}`, 'red'); reject(e); } resolve(); }, onerror: (error) => { log(`图片上传失败: ${error.status || 'Network Error'} ${error.statusText || ''}`, 'red'); reject(error); } }); }); } // 插入到编辑器 function insertToEditor(content, isEmoji = false) { const codeMirrorElement = document.querySelector('.CodeMirror'); if (codeMirrorElement) { const codeMirrorInstance = codeMirrorElement.CodeMirror; if (codeMirrorInstance) { const cursor = codeMirrorInstance.getCursor(); if (isEmoji) { // 表情符号直接插入,不添加换行符 codeMirrorInstance.replaceRange(content, cursor); } else { // 图片等其他内容保持原有格式(前后换行) codeMirrorInstance.replaceRange(`\n${content} \n`, cursor); } } } } // 在编辑器打印日志 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(`[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'); if (editorToolbarDiv) { editorToolbarDiv.appendChild(logDiv); } } // === Emoji 功能 === let emojiPicker = null; // 添加Emoji按钮 function addEmojiButton(toolbar) { // 检查是否已存在emoji按钮,如果不存在则创建 if (!toolbar.querySelector('.emoji-btn')) { const emojiBtn = document.createElement('span'); emojiBtn.className = 'toolbar-item emoji-btn'; emojiBtn.title = 'Emoji'; emojiBtn.innerHTML = '😊'; emojiBtn.style.cssText = 'cursor: pointer; font-size: 18px; margin-left: 8px;'; emojiBtn.addEventListener('click', toggleEmojiPicker); toolbar.appendChild(emojiBtn); } } // 确保Emoji按钮始终存在的检查函数 function ensureEmojiButton() { const toolbar = document.querySelector('.mde-toolbar'); if (toolbar && !toolbar.querySelector('.emoji-btn')) { addEmojiButton(toolbar); } } // 切换Emoji选择器 async function toggleEmojiPicker(event) { if (emojiPicker) { closeEmojiPicker(); } else { await createEmojiPicker(event); } event.stopPropagation(); } // 创建Emoji选择器 async function createEmojiPicker(event) { // 加载emoji-picker-element if (!window.customElements.get('emoji-picker')) { const script = document.createElement('script'); script.type = 'module'; script.src = emojiConfig.pickerUrl; document.head.appendChild(script); await new Promise(resolve => { const check = setInterval(() => { if (window.customElements.get('emoji-picker')) { clearInterval(check); resolve(); } }, 100); }); } // 计算位置 - 智能定位,避免超出屏幕 const rect = event.target.getBoundingClientRect(); const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // 计算水平位置 let left = rect.left - emojiConfig.width / 2; // 确保不超出左边界 left = Math.max(10, left); // 确保不超出右边界 left = Math.min(left, viewportWidth - emojiConfig.width - 10); // 计算垂直位置 - 优先显示在按钮下方,如果空间不够则显示在上方 let top; let actualHeight = emojiConfig.height; const spaceBelow = viewportHeight - rect.bottom; const spaceAbove = rect.top; if (spaceBelow >= emojiConfig.height + 10) { // 下方空间足够,显示在按钮下方 top = rect.bottom + 5; } else if (spaceAbove >= emojiConfig.height + 10) { // 上方空间足够,显示在按钮上方 top = rect.top - emojiConfig.height - 5; } else { // 上下空间都不够,选择空间较大的一边,并调整高度 if (spaceBelow > spaceAbove) { top = rect.bottom + 5; actualHeight = Math.min(emojiConfig.height, spaceBelow - 15); } else { top = 10; actualHeight = Math.min(emojiConfig.height, spaceAbove - 15); } } // 创建容器 emojiPicker = document.createElement('div'); emojiPicker.style.cssText = ` position: fixed; top: ${top}px; left: ${left}px; width: ${emojiConfig.width}px; height: ${actualHeight}px; background: #fff; border: 1px solid #ddd; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.15); z-index: 10001; animation: fadeIn 0.18s; `; const pickerElement = document.createElement('emoji-picker'); pickerElement.style.cssText = 'width: 100%; height: 100%;'; pickerElement.addEventListener('emoji-click', handleEmojiSelect); emojiPicker.appendChild(pickerElement); document.body.appendChild(emojiPicker); // 点击外部关闭 setTimeout(() => { document.addEventListener('click', outsideClickHandler); }, 10); } // 处理Emoji选择 function handleEmojiSelect(event) { const emoji = event.detail.unicode; insertToEditor(emoji, true); // 传递 isEmoji = true 参数 closeEmojiPicker(); } // 关闭Emoji选择器 function closeEmojiPicker() { if (emojiPicker) { emojiPicker.remove(); emojiPicker = null; document.removeEventListener('click', outsideClickHandler); // 确保Emoji按钮仍然存在 setTimeout(ensureEmojiButton, 50); } } // 外部点击处理 function outsideClickHandler(event) { if (emojiPicker && !emojiPicker.contains(event.target) && !event.target.classList.contains('emoji-btn') && !event.target.closest('.emoji-btn')) { closeEmojiPicker(); } } // 添加CSS动画 if (!document.getElementById('emoji-style')) { const style = document.createElement('style'); style.id = 'emoji-style'; style.innerHTML = ` @keyframes fadeIn { 0% { opacity: 0; transform: translateY(10px); } 100% { opacity: 1; transform: translateY(0); } } .emoji-btn:hover { background: #f2f2f2; border-radius: 4px; } `; document.head.appendChild(style); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址