MediaWiki:Gadget-CopyTextTool.js
来自卡厄思梦境WIKI
注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的更改的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5。
/**
* 文本复制工具 - 增强版
* 支持直接插入和包裹选中文字
*/
(function($, mw) {
'use strict';
// ===== 自定义配置 =====
var config = {
'蓝色': {
template: '{{文本|蓝|{text}}}',
fallback: '{{文本|蓝|'
},
'绿色': {
template: '{{文本|绿|{text}}}',
fallback: '{{文本|绿|'
},
'蓝色下划线': {
template: '{{文本|蓝|下划线|{text}}}',
fallback: '{{文本|蓝|下划线|'
},
'绿色描边': {
template: '{{描边|绿|{text}}}',
fallback: '{{描边|绿|'
},
'词典': {
template: '{{词典|{text}}}',
fallback: '{{词典|'
},
'红色': {
template: '{{文本|红|{text}}}',
fallback: '{{文本|红|'
},
'黄色': {
template: '{{文本|黄|{text}}}',
fallback: '{{文本|黄|'
}
};
// 默认位置配置
var defaultPosition = {
top: '100px',
right: '20px'
};
// ====================
var toolPanel = null;
var floatButton = null;
var isVisible = false;
var isMinimized = false;
var currentMode = 'auto'; // auto, insert, copy
// 页面加载完成后初始化
$(function() {
var savedState = localStorage.getItem('copyTextTool_visible');
isVisible = savedState === 'true';
createFloatButton();
createToolPanel();
if (isVisible) {
showTool();
}
});
// 创建浮动按钮
function createFloatButton() {
floatButton = $('<div>')
.attr('id', 'copy-tool-fab')
.attr('title', '文本复制工具')
.html('📋')
.css({
position: 'fixed',
bottom: '30px',
right: '30px',
width: '56px',
height: '56px',
borderRadius: '50%',
background: 'linear-gradient(135deg, #2196F3 0%, #1976D2 100%)',
color: 'white',
fontSize: '24px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
boxShadow: '0 4px 12px rgba(33, 150, 243, 0.4)',
zIndex: 9998,
transition: 'all 0.3s ease',
userSelect: 'none'
})
.on('click', toggleTool)
.hover(
function() {
$(this).css({
transform: 'scale(1.1)',
boxShadow: '0 6px 16px rgba(33, 150, 243, 0.6)'
});
},
function() {
$(this).css({
transform: 'scale(1)',
boxShadow: '0 4px 12px rgba(33, 150, 243, 0.4)'
});
}
)
.appendTo('body');
}
// 创建工具面板
function createToolPanel() {
var savedPos = localStorage.getItem('copyTextTool_position');
var position = savedPos ? JSON.parse(savedPos) : defaultPosition;
toolPanel = $('<div>')
.attr('id', 'copy-text-tool')
.css({
position: 'fixed',
top: position.top,
right: position.right,
left: position.left || 'auto',
width: '340px',
background: 'white',
border: '2px solid #2196F3',
borderRadius: '12px',
boxShadow: '0 8px 24px rgba(0,0,0,0.15)',
zIndex: 9999,
display: 'none',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
});
// 创建标题栏
var header = $('<div>')
.attr('id', 'tool-header')
.css({
background: 'linear-gradient(135deg, #2196F3 0%, #1976D2 100%)',
color: 'white',
padding: '12px 15px',
borderRadius: '10px 10px 0 0',
cursor: 'move',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
userSelect: 'none'
})
.appendTo(toolPanel);
// 标题
$('<div>')
.css({
fontWeight: 'bold',
fontSize: '15px',
display: 'flex',
alignItems: 'center',
gap: '8px'
})
.html('<span style="font-size: 18px">📋</span> 文本工具')
.appendTo(header);
// 控制按钮组
var controls = $('<div>')
.css({
display: 'flex',
gap: '8px'
})
.appendTo(header);
// 最小化按钮
$('<button>')
.attr('title', '最小化')
.html('−')
.css({
background: 'rgba(255,255,255,0.2)',
border: 'none',
color: 'white',
width: '24px',
height: '24px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
})
.on('click', minimizeTool)
.hover(
function() { $(this).css('background', 'rgba(255,255,255,0.3)'); },
function() { $(this).css('background', 'rgba(255,255,255,0.2)'); }
)
.appendTo(controls);
// 关闭按钮
$('<button>')
.attr('title', '关闭')
.html('×')
.css({
background: 'rgba(255,255,255,0.2)',
border: 'none',
color: 'white',
width: '24px',
height: '24px',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '20px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
})
.on('click', hideTool)
.hover(
function() { $(this).css('background', 'rgba(255,255,255,0.3)'); },
function() { $(this).css('background', 'rgba(255,255,255,0.2)'); }
)
.appendTo(controls);
// 内容区域
var body = $('<div>')
.attr('id', 'tool-body')
.css({
padding: '15px'
})
.appendTo(toolPanel);
// 模式选择
var modeSelector = $('<div>')
.css({
display: 'flex',
gap: '8px',
marginBottom: '12px',
padding: '8px',
background: '#f5f5f5',
borderRadius: '6px'
})
.appendTo(body);
$('<div>')
.text('模式:')
.css({
fontSize: '13px',
color: '#666',
lineHeight: '28px',
fontWeight: '500'
})
.appendTo(modeSelector);
var modes = [
{ id: 'auto', name: '智能', title: '自动检测:有选中文字则插入,否则复制' },
{ id: 'insert', name: '插入', title: '始终插入到编辑器' },
{ id: 'copy', name: '复制', title: '始终复制到剪贴板' }
];
var modeButtons = $('<div>')
.css({
display: 'flex',
gap: '4px',
flex: '1'
})
.appendTo(modeSelector);
modes.forEach(function(mode) {
$('<button>')
.attr({
'data-mode': mode.id,
'title': mode.title
})
.text(mode.name)
.css({
flex: '1',
padding: '6px 8px',
background: mode.id === 'auto' ? '#2196F3' : 'white',
color: mode.id === 'auto' ? 'white' : '#666',
border: '1px solid #ddd',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
transition: 'all 0.2s'
})
.on('click', function() {
currentMode = mode.id;
modeButtons.find('button').css({
background: 'white',
color: '#666'
});
$(this).css({
background: '#2196F3',
color: 'white'
});
updateStatus('模式:' + mode.name);
})
.appendTo(modeButtons);
});
// 按钮容器
var buttonContainer = $('<div>')
.css({
display: 'grid',
gridTemplateColumns: 'repeat(2, 1fr)',
gap: '8px',
marginBottom: '10px'
})
.appendTo(body);
// 添加功能按钮
$.each(config, function(name, conf) {
$('<button>')
.text(name)
.css({
padding: '12px 8px',
background: '#2196F3',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '13px',
fontWeight: '500',
transition: 'all 0.2s ease'
})
.on('click', function() {
handleButtonClick(name, conf);
})
.hover(
function() {
$(this).css({
background: '#1976D2',
transform: 'translateY(-1px)',
boxShadow: '0 2px 8px rgba(33, 150, 243, 0.3)'
});
},
function() {
$(this).css({
background: '#2196F3',
transform: 'translateY(0)',
boxShadow: 'none'
});
}
)
.appendTo(buttonContainer);
});
// 状态栏
$('<div>')
.attr('id', 'copy-status')
.css({
textAlign: 'center',
color: '#666',
padding: '10px',
fontSize: '12px',
minHeight: '20px',
background: '#f9f9f9',
borderRadius: '6px'
})
.html('💡 <b>智能模式</b>:选中文字后点击按钮即可包裹')
.appendTo(body);
toolPanel.appendTo('body');
// 使工具面板可拖动
makeDraggable(toolPanel[0], header[0]);
}
// 处理按钮点击
function handleButtonClick(name, conf) {
var selectedText = getSelectedText();
var activeElement = document.activeElement;
var isInEditor = isEditableElement(activeElement);
// 决定使用哪种模式
var shouldInsert = false;
if (currentMode === 'insert') {
shouldInsert = true;
} else if (currentMode === 'copy') {
shouldInsert = false;
} else { // auto
shouldInsert = isInEditor;
}
if (shouldInsert) {
// 插入模式
if (selectedText) {
// 有选中文字:包裹模板
var text = conf.template.replace('{text}', selectedText);
insertAtCursor(activeElement, text, true);
updateStatus('✓ 已插入包裹: ' + name, true);
} else {
// 无选中文字:插入模板前缀
insertAtCursor(activeElement, conf.fallback, false);
updateStatus('✓ 已插入: ' + name, true);
}
} else {
// 复制模式
var textToCopy = selectedText ?
conf.template.replace('{text}', selectedText) :
conf.fallback;
copyToClipboard(textToCopy);
updateStatus('✓ 已复制: ' + name, true);
}
}
// 获取选中的文本
function getSelectedText() {
var activeElement = document.activeElement;
// 检查是否在输入框或文本区域中
if (activeElement && (activeElement.tagName === 'TEXTAREA' || activeElement.tagName === 'INPUT')) {
var start = activeElement.selectionStart;
var end = activeElement.selectionEnd;
if (start !== end) {
return activeElement.value.substring(start, end);
}
}
// 检查页面选中的文本
var selection = window.getSelection();
if (selection && selection.toString()) {
return selection.toString();
}
return '';
}
// 检查是否是可编辑元素
function isEditableElement(element) {
if (!element) return false;
var tagName = element.tagName;
if (tagName === 'TEXTAREA') return true;
if (tagName === 'INPUT' && element.type === 'text') return true;
if (element.contentEditable === 'true') return true;
if (element.classList && element.classList.contains('CodeMirror')) return true;
return false;
}
// 在光标位置插入文本
function insertAtCursor(element, text, replaceSelection) {
if (!element) {
updateStatus('❌ 未找到输入框', false);
return;
}
if (element.tagName === 'TEXTAREA' || element.tagName === 'INPUT') {
var start = element.selectionStart;
var end = element.selectionEnd;
var value = element.value;
if (replaceSelection) {
// 替换选中的文本
element.value = value.substring(0, start) + text + value.substring(end);
element.selectionStart = element.selectionEnd = start + text.length;
} else {
// 在光标位置插入
element.value = value.substring(0, start) + text + value.substring(start);
element.selectionStart = element.selectionEnd = start + text.length;
}
// 触发输入事件
var event = new Event('input', { bubbles: true });
element.dispatchEvent(event);
// 聚焦回编辑器
element.focus();
} else if (element.contentEditable === 'true') {
// 处理可编辑div (如VisualEditor)
document.execCommand('insertText', false, text);
} else {
updateStatus('❌ 不支持的编辑器类型', false);
}
}
// 复制到剪贴板
function copyToClipboard(text) {
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function() {
// 成功
}).catch(function() {
fallbackCopy(text);
});
} else {
fallbackCopy(text);
}
}
// 备用复制方法
function fallbackCopy(text) {
var $temp = $('<textarea>')
.val(text)
.css({
position: 'fixed',
opacity: 0,
top: '-9999px'
})
.appendTo('body');
$temp[0].select();
$temp[0].setSelectionRange(0, 99999);
try {
document.execCommand('copy');
} catch(err) {
updateStatus('❌ 复制失败', false);
}
$temp.remove();
}
// 更新状态显示
function updateStatus(message, success) {
var icon = success ? '✓' : '❌';
var color = success ? '#2196F3' : '#f44336';
$('#copy-status')
.html(icon + ' ' + message)
.css({
color: color,
fontWeight: 'bold'
});
// 3秒后恢复默认提示
setTimeout(function() {
$('#copy-status')
.html('💡 <b>智能模式</b>:选中文字后点击按钮即可包裹')
.css({
color: '#666',
fontWeight: 'normal'
});
}, 3000);
}
// 切换工具显示
function toggleTool() {
if (isVisible) {
hideTool();
} else {
showTool();
}
}
// 显示工具
function showTool() {
if (isMinimized) {
isMinimized = false;
$('#tool-body').show();
toolPanel.css('width', '340px');
}
toolPanel.fadeIn(200);
isVisible = true;
localStorage.setItem('copyTextTool_visible', 'true');
}
// 隐藏工具
function hideTool() {
toolPanel.fadeOut(200);
isVisible = false;
localStorage.setItem('copyTextTool_visible', 'false');
}
// 最小化工具
function minimizeTool() {
if (isMinimized) {
$('#tool-body').slideDown(200);
toolPanel.css('width', '340px');
isMinimized = false;
} else {
$('#tool-body').slideUp(200);
toolPanel.css('width', 'auto');
isMinimized = true;
}
}
// 拖动功能
function makeDraggable(element, handle) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
handle.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
$(element).css('transition', 'none');
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
var newTop = (element.offsetTop - pos2);
var newLeft = (element.offsetLeft - pos1);
element.style.top = newTop + "px";
element.style.left = newLeft + "px";
element.style.right = "auto";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
var position = {
top: element.style.top,
left: element.style.left
};
localStorage.setItem('copyTextTool_position', JSON.stringify(position));
}
}
})(jQuery, mediaWiki);