// ==UserScript==
// @name 超星多编辑器智能粘贴神器
// @namespace https://mooc1-api.chaoxing.com/
// @match *://*.chaoxing.com/*
// @version 1.0.1
// @description 支持多编辑器识别、选择、批量粘贴(修复重复加载,带拖拽功能)
// @icon http://pan-yz.chaoxing.com/favicon.ico
// @author User
// @author Muyue -imuyue.com
// @connect mooc1-1.chaoxing.com
// @connect mooc1-1.neauce.com
// @connect mooc1.chaoxing.com
// @connect mooc1.neauce.com
// @connect mooc1-2.chaoxing.com
// @connect mooc1-2.neauce.com
// @connect mooc1-api.chaoxing.com
// @connect mooc2-ans.chaoxing.com
// @connect passport2-api.chaoxing.com
// @connect sso.chaoxing.com
// @grant none
// @tag 学习通
// ==/UserScript==
(function() {
'use strict';
console.log('%c[多编辑器神器] 已加载', 'background: #ff00ff; color: white; font-size: 16px; font-weight: bold;');
// ==================== 核心功能:暴力注入 ====================
const pasteToUEditor = (content, targetIndex = null) => {
const iframes = getAllEditors();
if (iframes.length === 0) {
console.error('[暴力粘贴] 未找到任何UEditor编辑器');
alert('未检测到编辑器,请确保已进入答题页面');
return false;
}
if (targetIndex !== null && iframes[targetIndex]) {
return injectToIframe(iframes[targetIndex].iframe, content, targetIndex);
}
iframes.forEach((info, i) => {
injectToIframe(info.iframe, content, i);
});
return true;
};
const injectToIframe = (iframe, content, index) => {
try {
const doc = iframe.contentDocument;
const body = doc.body;
body.onpaste = null;
iframe.contentWindow.editorPaste = () => true;
const htmlContent = content.replace(/\n/g, '
');
body.innerHTML = '
' + htmlContent + '
';
if (window.UE && window.UE.instants) {
const editorKey = Object.keys(window.UE.instants).find(function(k) {
return window.UE.instants[k].document === doc;
});
if (editorKey) {
const editor = window.UE.instants[editorKey];
editor.fireEvent('contentchange');
editor.fireEvent('afterpaste');
}
}
console.log('✅ [暴力粘贴] 成功注入编辑器' + index);
return true;
} catch(e) {
console.error('❌ [暴力粘贴] 编辑器' + index + '注入失败:', e.message);
return false;
}
};
// ==================== 智能识别所有编辑器 ====================
const getAllEditors = () => {
const iframes = Array.from(document.querySelectorAll('iframe[id^="ueditor_"]'));
const editors = [];
iframes.forEach(function(iframe, index) {
try {
const doc = iframe.contentDocument;
const body = doc.body;
const questionText = findQuestionText(iframe, index);
editors.push({
index: index,
iframe: iframe,
body: body,
question: questionText,
element: iframe,
visible: iframe.offsetParent !== null
});
} catch(e) {
console.warn('[识别] 编辑器' + index + '无法访问');
}
});
return editors;
};
const findQuestionText = (iframe, index) => {
try {
const parent = iframe.parentElement;
const prev = iframe.previousElementSibling;
const questionElement = parent.querySelector('.mark_name, .questionName, .Zy_question') ||
(prev ? prev.querySelector('.mark_name, .questionName, .Zy_question') : null);
if (questionElement) {
return questionElement.textContent.trim().substring(0, 20) + '...';
}
return '第' + (index + 1) + '题';
} catch(e) {
return '第' + (index + 1) + '题';
}
};
// ==================== 拖拽功能核心(独立函数) ====================
function initDragFunction(dragPanel) {
if (!dragPanel) {
console.warn('拖拽面板元素未找到');
return;
}
const dragHandle = dragPanel.querySelector('#drag-handle');
if (!dragHandle) return;
var isDragging = false;
var startX = 0;
var startY = 0;
var panelLeft = 0;
var panelTop = 0;
dragHandle.addEventListener('mousedown', function(e) {
e.preventDefault();
e.stopPropagation();
isDragging = true;
startX = e.clientX;
startY = e.clientY;
panelLeft = parseInt(window.getComputedStyle(dragPanel).left, 10) || 0;
panelTop = parseInt(window.getComputedStyle(dragPanel).top, 10) || 0;
dragHandle.style.background = 'rgba(255, 0, 255, 0.1)';
dragPanel.style.userSelect = 'none';
});
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
var dx = e.clientX - startX;
var dy = e.clientY - startY;
var maxLeft = window.innerWidth - dragPanel.offsetWidth - 20;
var maxTop = window.innerHeight - dragPanel.offsetHeight - 20;
var newLeft = Math.max(0, Math.min(panelLeft + dx, maxLeft));
var newTop = Math.max(0, Math.min(panelTop + dy, maxTop));
dragPanel.style.left = newLeft + 'px';
dragPanel.style.top = newTop + 'px';
dragPanel.style.right = 'auto';
});
function endDrag() {
if (!isDragging) return;
isDragging = false;
dragHandle.style.background = '';
dragPanel.style.userSelect = '';
}
document.addEventListener('mouseup', endDrag);
document.addEventListener('mouseleave', endDrag);
}
// ==================== 功能:输入框UI(增强版) ====================
let clipboardCache = '';
const addUI = () => {
const panel = document.createElement('div');
panel.innerHTML = `
🚀 多编辑器智能粘贴
正在识别编辑器...
0 字符
`;
document.body.appendChild(panel);
const dragPanel = document.getElementById('draggable-paste-panel');
initDragFunction(dragPanel);
const input = document.getElementById('paste-input');
const count = document.getElementById('char-count');
const status = document.getElementById('status');
const selector = document.getElementById('editor-selector');
const editorList = document.getElementById('editor-list');
input.addEventListener('input', function() {
count.textContent = this.value.length;
localStorage.setItem('superPaste_content', this.value);
});
const saved = localStorage.getItem('superPaste_content');
if (saved) {
input.value = saved;
count.textContent = saved.length;
}
const updateEditorList = () => {
const editors = getAllEditors();
if (editors.length === 0) {
editorList.innerHTML = '未找到编辑器
';
return;
}
while (selector.options.length > 2) {
selector.remove(2);
}
var listHtml = '';
editors.forEach(function(info) {
var opacityStyle = info.visible ? '' : 'opacity: 0.6;';
listHtml += '' +
'编辑器' + info.index + ': ' + info.question +
(info.visible ? '✅' : '⏸️') +
'
';
});
editorList.innerHTML = listHtml;
editors.forEach(function(info) {
const option = document.createElement('option');
option.value = info.index;
option.textContent = '📝 编辑器' + info.index + ': ' + info.question;
selector.appendChild(option);
});
};
updateEditorList();
setInterval(updateEditorList, 5000);
document.getElementById('refresh-editors').onclick = updateEditorList;
document.getElementById('load-clipboard').onclick = async function() {
try {
const text = await navigator.clipboard.readText();
if (text) {
input.value = text;
count.textContent = text.length;
clipboardCache = text;
status.textContent = '✅ 剪贴板内容已读取';
status.style.color = '#0c0';
localStorage.setItem('superPaste_content', text);
} else {
status.textContent = '❌ 剪贴板为空';
status.style.color = '#f00';
}
} catch(err) {
status.textContent = '❌ 无法访问剪贴板';
status.style.color = '#f00';
}
setTimeout(function() {
status.textContent = '';
status.style.color = '#666';
}, 3000);
};
document.getElementById('clear-input').onclick = function() {
input.value = '';
count.textContent = '0';
localStorage.removeItem('superPaste_content');
status.textContent = '🗑️ 已清空';
status.style.color = '#f90';
setTimeout(function() {
status.textContent = '';
status.style.color = '#666';
}, 2000);
};
document.getElementById('execute-paste').onclick = async function() {
const content = input.value.trim() || clipboardCache;
if (!content) {
status.textContent = '❌ 请输入内容';
status.style.color = '#f00';
input.focus();
return;
}
const target = selector.value;
let success = false;
if (target === 'all') {
success = pasteToUEditor(content);
status.textContent = success ? '✅ 已粘贴到全部' : '❌ 粘贴失败';
} else if (target === 'current') {
const active = document.activeElement;
const editors = getAllEditors();
let currentIndex = null;
if (active.tagName === 'IFRAME') {
var match = null;
for (var i = 0; i < editors.length; i++) {
if (editors[i].iframe === active) {
match = editors[i];
break;
}
}
if (match) currentIndex = match.index;
}
if (currentIndex === null && window.lastClickedEditor !== undefined) {
currentIndex = window.lastClickedEditor;
}
if (currentIndex === null) {
var visible = null;
for (var j = 0; j < editors.length; j++) {
if (editors[j].visible) {
visible = editors[j];
break;
}
}
if (visible) currentIndex = visible.index;
}
success = pasteToUEditor(content, currentIndex);
status.textContent = success ? '✅ 已粘贴到编辑器' + currentIndex : '❌ 粘贴失败';
} else {
success = pasteToUEditor(content, parseInt(target));
status.textContent = success ? '✅ 已粘贴到编辑器' + target : '❌ 粘贴失败';
}
status.style.color = success ? '#0c0' : '#f00';
setTimeout(function() {
status.textContent = '';
status.style.color = '#666';
}, 3000);
};
document.addEventListener('click', function(e) {
const iframe = e.target.closest('iframe[id^="ueditor_"]');
if (iframe) {
const editors = getAllEditors();
var match = null;
for (var k = 0; k < editors.length; k++) {
if (editors[k].iframe === iframe) {
match = editors[k];
break;
}
}
if (match) {
window.lastClickedEditor = match.index;
console.log('[暴力粘贴] 记录点击: 编辑器' + match.index);
}
}
}, true);
};
// 延迟添加UI,先检查是否已存在面板(防重复加载)
setTimeout(() => {
if (document.getElementById('draggable-paste-panel')) {
console.warn('[多编辑器神器] 检测到重复加载,已跳过');
return;
}
addUI();
}, 1500);
// ==================== 快捷键 ====================
document.addEventListener('keydown', async function(e) {
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'v') {
e.preventDefault();
try {
const text = await navigator.clipboard.readText();
if (text) {
clipboardCache = text;
pasteToUEditor(text);
showNotification('✅ 内容已粘贴到全部');
}
} catch(err) {
showNotification('❌ 无法访问剪贴板');
}
}
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'c') {
var clearBtn = document.getElementById('clear-input');
if (clearBtn) clearBtn.click();
}
});
// ==================== 通知 ====================
const showNotification = function(message) {
const div = document.createElement('div');
div.textContent = message;
div.style.cssText = 'position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);' +
'background: #00ff00; color: #000; padding: 15px 25px; border-radius: 8px;' +
'z-index: 999999; font-size: 16px; font-weight: bold;' +
'box-shadow: 0 0 20px rgba(0,255,0,0.5); animation: fadeOut 2s forwards;';
const style = document.createElement('style');
style.textContent = '@keyframes fadeOut {' +
'0% { opacity: 1; transform: translate(-50%, -50%) scale(1); }' +
'70% { opacity: 1; transform: translate(-50%, -50%) scale(1); }' +
'100% { opacity: 0; transform: translate(-50%, -50%) scale(0.8); pointer-events: none; }' +
'}';
if (!document.querySelector('#superPaste_animation')) {
style.id = 'superPaste_animation';
document.head.appendChild(style);
}
document.body.appendChild(div);
setTimeout(function() {
div.remove();
}, 2000);
};
// ==================== API ====================
window.superPaste = {
all: function(content) {
return pasteToUEditor(content);
},
to: function(content, index) {
return pasteToUEditor(content, index);
},
count: function() {
return getAllEditors().length;
},
status: function() {
const editors = getAllEditors();
console.log('[暴力粘贴] 找到 ' + editors.length + ' 个编辑器');
editors.forEach(function(info) {
try {
console.log('编辑器' + info.index + ': ' + info.question + ' ' + (info.visible ? '✅' : '⏸️'));
} catch(e) {
console.log('编辑器' + info.index + ': 无法访问');
}
});
},
clearAll: function() {
var iframes = document.querySelectorAll('iframe[id^="ueditor_"]');
iframes.forEach(function(iframe, i) {
try {
iframe.contentDocument.body.innerHTML = '';
console.log('编辑器' + i + '已清空');
} catch(e) {}
});
},
getEditors: function() {
return getAllEditors();
}
};
})();