// ==UserScript==
// @name amobbs 论坛增强:视频播放按钮 + 粘贴上传图片
// @namespace https://www.amobbs.com/
// @version 2.0.0
// @description 视频:为哔哩哔哩视频链接(b23.tv / bilibili.com)后面添加「▶ 播放」按钮,点击弹窗播放,支持横竖屏自适应、手动切换与最大化、Windows 风格窗口按钮,播放器 iframe 加 sandbox 点击倍速/弹幕等按钮不再跳转 B 站官网,PC 下页面最大宽度 1200px 居中。图片:帖子页(快速回复)和发帖页编辑框内直接粘贴截图,自动上传为论坛附件并插入 [attachimg],大于 1MB 自动压缩(最长边 1920px、JPEG),带进度提示与错误原因
// @author you
// @match https://www.amobbs.com/*
// @match https://amobbs.com/*
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @connect b23.tv
// @connect api.bilibili.com
// @connect bilibili.com
// @connect www.bilibili.com
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
var LINK_RE = /^(https?:\/\/)?((www\.)?bilibili\.com\/video\/(BV[0-9A-Za-z]+|av\d+)|b23\.tv\/[0-9A-Za-z]+)/i;
var POPUP_ID = 'tm-video-popup';
var PAGE_MAX_WIDTH = 1200;
var ICON_MAX = '';
var ICON_RESTORE = '';
var ICON_CLOSE = '';
var UPLOAD_URL = 'https://www.amobbs.com/misc.php?mod=swfupload&action=swfupload&operation=upload';
var STATUS_MSG = {
1: '不允许的文件扩展名',
2: '文件为空或读取失败',
3: '超过单文件大小上限',
4: '该扩展名被禁止',
5: '超过该文件类型的大小限制',
6: '附件数量超限或没有上传权限',
7: '不是有效的图片文件',
8: '服务器保存失败',
9: '服务器保存错误',
10: '上传参数(hash)校验失败,请刷新页面重试',
11: '超过每天附件总大小上限',
12: '文件名含敏感词',
13: '图片像素超出限制'
};
var DEFAULT_OPTS = { maxBytes: 1024 * 1024, maxDim: 1920, quality: 0.85 };
function extractPlayId(url) {
var m;
m = url.match(/bilibili\.com\/video\/(BV[0-9A-Za-z]+)/i);
if (m) return { bvid: m[1] };
m = url.match(/bilibili\.com\/video\/av(\d+)/i);
if (m) return { aid: m[1] };
m = url.match(/b23\.tv\/([0-9A-Za-z]+)/i);
if (m) return { short: m[1] };
return null;
}
function buildEmbedUrl(id) {
if (id.bvid) {
return 'https://player.bilibili.com/player.html?bvid=' + id.bvid + '&page=1&high_quality=1&danmaku=0';
}
if (id.aid) {
return 'https://player.bilibili.com/player.html?aid=' + id.aid + '&page=1&high_quality=1&danmaku=0';
}
return null;
}
function resolveShort(code) {
var cacheKey = 'b23_' + code;
var cached = GM_getValue(cacheKey, null);
if (cached) return Promise.resolve(cached);
return new Promise(function (resolve, reject) {
GM_xmlhttpRequest({
method: 'GET',
url: 'https://b23.tv/' + code,
redirect: 'follow',
timeout: 15000,
onload: function (resp) {
var finalUrl = (resp.finalUrl && resp.finalUrl !== '') ? resp.finalUrl : '';
if (!finalUrl) {
var m = resp.responseText && resp.responseText.match(/url\s*=\s*["']([^"']+)["']/i);
if (m) finalUrl = m[1];
}
var id = extractPlayId(finalUrl);
if (id && (id.bvid || id.aid)) {
GM_setValue(cacheKey, id);
resolve(id);
} else {
reject(new Error('短链接解析失败'));
}
},
onerror: function (e) { reject(new Error('短链接请求失败:' + ((e && e.error) ? e.error : '网络错误'))); },
ontimeout: function () { reject(new Error('短链接请求超时')); }
});
});
}
function fetchVideoMeta(id) {
var url;
if (id.bvid) {
url = 'https://api.bilibili.com/x/web-interface/view?bvid=' + id.bvid;
} else if (id.aid) {
url = 'https://api.bilibili.com/x/web-interface/view?aid=' + id.aid;
} else {
return Promise.resolve(null);
}
return new Promise(function (resolve) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
timeout: 10000,
onload: function (resp) {
try {
var data = JSON.parse(resp.responseText);
var dim = data && data.data && data.data.dimension;
resolve(dim || null);
} catch (e) {
resolve(null);
}
},
onerror: function () { resolve(null); },
ontimeout: function () { resolve(null); }
});
});
}
function applyOrientationSize(dim, box, mode) {
var vw = window.innerWidth;
var vh = window.innerHeight;
var barH = (box._bar && box._bar.offsetHeight) || 44;
var body = box._body;
var ratio = 16 / 9;
if (dim && dim.width && dim.height) {
var w = dim.width, h = dim.height, r = dim.rotate;
if (r === 90 || r === 270 || r === -90 || r === -270) { var t = w; w = h; h = t; }
if (w > 0 && h > 0) ratio = w / h;
}
if (mode === 'landscape') {
ratio = 16 / 9;
} else if (mode === 'portrait') {
ratio = 9 / 16;
}
var availW = vw - 32;
var availH = vh - barH - 48;
var bw, bh;
if (ratio < 1) {
bh = Math.min(availH, Math.round(vh * 0.9));
bw = Math.round(bh * ratio);
if (bw < 280) bw = 280;
if (bw > availW) { bw = availW; bh = Math.round(bw / ratio); }
} else {
bw = Math.min(availW, 1400);
bh = Math.round(bw / ratio);
if (bh > availH) { bh = availH; bw = Math.round(bh * ratio); }
}
body.style.width = '100%';
body.style.height = bh + 'px';
box.style.width = bw + 'px';
box.style.height = (bh + barH) + 'px';
box.style.marginLeft = '';
box.style.marginTop = '';
box._maximized = false;
var overlay = box.parentNode;
if (overlay && overlay.style) {
overlay.style.alignItems = 'center';
overlay.style.justifyContent = 'center';
}
if (box._btnAuto && box._btnLand && box._btnPort) {
box._btnAuto.style.background = (!mode || mode === 'auto') ? '#fb7299' : '#333';
box._btnLand.style.background = (mode === 'landscape') ? '#fb7299' : '#333';
box._btnPort.style.background = (mode === 'portrait') ? '#fb7299' : '#333';
}
if (box._btnMax) {
box._btnMax.innerHTML = ICON_MAX;
box._btnMax.style.background = 'transparent';
box._btnMax.title = '最大化';
}
}
function getOrCreatePopup() {
var popup = document.getElementById(POPUP_ID);
if (popup) return popup;
var overlay = document.createElement('div');
overlay.id = POPUP_ID;
overlay.style.cssText = [
'position:fixed', 'left:0', 'top:0', 'width:100%', 'height:100%',
'background:rgba(0,0,0,0.65)', 'z-index:2147483000',
'display:none', 'align-items:center', 'justify-content:center'
].join(';');
var box = document.createElement('div');
box.style.cssText = [
'position:relative', 'width:min(860px,92vw)', 'background:#0b0b0f',
'border-radius:10px', 'box-shadow:0 12px 48px rgba(0,0,0,0.7)',
'overflow:hidden', 'display:flex', 'flex-direction:column'
].join(';');
var bar = document.createElement('div');
bar.style.cssText = 'display:flex;align-items:stretch;background:#18181c;color:#eee;font-size:14px;cursor:move;';
var title = document.createElement('span');
title.textContent = '视频播放';
title.style.cssText = 'white-space:nowrap;overflow:hidden;text-overflow:ellipsis;';
function makeModeBtn(text, titleText) {
var b = document.createElement('button');
b.type = 'button';
b.textContent = text;
b.title = titleText;
b.style.cssText = 'cursor:pointer;padding:2px 10px;border:0;border-radius:6px;background:#333;color:#eee;font-size:12px;line-height:20px;';
b.addEventListener('mouseenter', function () { if (b.style.background === 'rgb(51, 51, 51)') b.style.background = '#555'; });
b.addEventListener('mouseleave', function () { if (b.style.background === 'rgb(85, 85, 85)') b.style.background = '#333'; });
return b;
}
var btnAuto = makeModeBtn('自动', '按视频原始比例自适应');
var btnLand = makeModeBtn('横屏', '切换为横屏 16:9');
var btnPort = makeModeBtn('竖屏', '切换为竖屏 9:16');
var btnGroup = document.createElement('div');
btnGroup.style.cssText = 'display:flex;gap:6px;';
btnGroup.appendChild(btnAuto);
btnGroup.appendChild(btnLand);
btnGroup.appendChild(btnPort);
btnAuto.addEventListener('click', function () {
box._mode = 'auto';
applyOrientationSize(box._meta, box, 'auto');
});
btnLand.addEventListener('click', function () {
box._mode = 'landscape';
applyOrientationSize(box._meta, box, 'landscape');
});
btnPort.addEventListener('click', function () {
box._mode = 'portrait';
applyOrientationSize(box._meta, box, 'portrait');
});
function makeWinBtn(html, titleText) {
var b = document.createElement('button');
b.type = 'button';
b.innerHTML = html;
b.title = titleText;
b.style.cssText = 'display:flex;align-items:center;justify-content:center;width:44px;border:0;background:transparent;cursor:default;flex-shrink:0;';
return b;
}
var btnMax = makeWinBtn(ICON_MAX, '最大化');
btnMax.addEventListener('mouseenter', function () { btnMax.style.background = '#e81123'; });
btnMax.addEventListener('mouseleave', function () { btnMax.style.background = 'transparent'; });
btnMax.addEventListener('click', function () {
if (box._maximized) {
applyOrientationSize(box._meta, box, box._mode);
} else {
box._maximized = true;
var barH = (box._bar && box._bar.offsetHeight) || 44;
box.style.width = '100%';
box.style.height = '100%';
box.style.marginLeft = '';
box.style.marginTop = '';
var overlay = box.parentNode;
if (overlay && overlay.style) {
overlay.style.alignItems = 'stretch';
overlay.style.justifyContent = 'stretch';
}
box._body.style.width = '100%';
box._body.style.height = 'calc(100vh - ' + barH + 'px)';
btnMax.innerHTML = ICON_RESTORE;
btnMax.title = '还原';
box._btnAuto.style.background = '#333';
box._btnLand.style.background = '#333';
box._btnPort.style.background = '#333';
}
});
var close = makeWinBtn(ICON_CLOSE, '关闭 (Esc)');
close.addEventListener('mouseenter', function () { close.style.background = '#e81123'; });
close.addEventListener('mouseleave', function () { close.style.background = 'transparent'; });
close.addEventListener('click', hidePopup);
var leftGroup = document.createElement('div');
leftGroup.style.cssText = 'display:flex;align-items:center;gap:10px;flex:1;min-width:0;padding:10px 14px;cursor:move;';
leftGroup.appendChild(title);
leftGroup.appendChild(btnGroup);
bar.appendChild(leftGroup);
bar.appendChild(btnMax);
bar.appendChild(close);
var body = document.createElement('div');
body.style.cssText = 'position:relative;width:100%;height:360px;background:#000;';
var msg = document.createElement('div');
msg.style.cssText = 'position:absolute;inset:0;display:flex;align-items:center;justify-content:center;color:#ccc;font-size:14px;';
msg.textContent = '解析视频链接中…';
body.appendChild(msg);
box.appendChild(bar);
box.appendChild(body);
overlay.appendChild(box);
box._title = title;
box._body = body;
box._msg = msg;
box._bar = bar;
box._btnAuto = btnAuto;
box._btnLand = btnLand;
box._btnPort = btnPort;
box._btnMax = btnMax;
box._mode = 'auto';
box._meta = null;
box._maximized = false;
overlay._box = box;
document.documentElement.appendChild(overlay);
return overlay;
}
function showPopup(url) {
var overlay = getOrCreatePopup();
var box = overlay._box;
box._mode = 'auto';
box._meta = null;
overlay.style.display = 'flex';
box._body.querySelectorAll('iframe').forEach(function (f) { f.remove(); });
box._msg.style.display = 'flex';
box._msg.textContent = '解析视频链接中…';
applyOrientationSize(null, box, 'auto');
var id = extractPlayId(url);
var promise;
if (id && id.short) {
promise = resolveShort(id.short);
} else if (id && (id.bvid || id.aid)) {
promise = Promise.resolve(id);
} else {
promise = Promise.reject(new Error('无法识别的视频链接'));
}
promise.then(function (resolved) {
var embedUrl = buildEmbedUrl(resolved);
if (!embedUrl) throw new Error('无法生成播放地址');
return Promise.all([embedUrl, fetchVideoMeta(resolved)]);
}).then(function (pair) {
var embedUrl = pair[0];
box._meta = pair[1];
applyOrientationSize(pair[1], box, box._mode);
var iframe = document.createElement('iframe');
iframe.src = embedUrl;
iframe.setAttribute('scrolling', 'no');
iframe.setAttribute('frameborder', '0');
iframe.setAttribute('allowfullscreen', 'true');
iframe.setAttribute('allow', 'autoplay; fullscreen');
iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
iframe.style.cssText = 'position:absolute;inset:0;width:100%;height:100%;border:0;';
box._msg.style.display = 'none';
box._body.appendChild(iframe);
}).catch(function (err) {
box._msg.style.display = 'flex';
box._msg.innerHTML = '';
var line = document.createElement('div');
line.style.cssText = 'text-align:center;';
line.textContent = '播放失败:' + err.message;
var openLink = document.createElement('a');
openLink.href = url;
openLink.target = '_blank';
openLink.rel = 'noopener';
openLink.textContent = ' 在新窗口打开链接';
openLink.style.cssText = 'display:inline-block;margin-top:8px;color:#5ad;';
line.appendChild(openLink);
box._msg.appendChild(line);
});
}
function hidePopup() {
var overlay = document.getElementById(POPUP_ID);
if (!overlay) return;
overlay.style.display = 'none';
var box = overlay._box;
if (box) {
box._body.querySelectorAll('iframe').forEach(function (f) { f.remove(); });
}
}
function makePlayButton(url) {
var btn = document.createElement('button');
btn.type = 'button';
btn.textContent = '▶ 播放';
btn.title = '弹窗播放:' + url;
btn.style.cssText = [
'display:inline-block', 'margin:0 4px', 'padding:2px 10px',
'background:#fb7299', 'color:#fff', 'border:0', 'border-radius:6px',
'font-size:12px', 'line-height:18px', 'cursor:pointer',
'vertical-align:middle', 'box-shadow:0 1px 3px rgba(0,0,0,0.25)'
].join(';');
btn.addEventListener('mouseenter', function () { btn.style.background = '#ff85a8'; });
btn.addEventListener('mouseleave', function () { btn.style.background = '#fb7299'; });
btn.addEventListener('click', function (e) {
e.preventDefault();
e.stopPropagation();
showPopup(url);
});
return btn;
}
function attachButtons() {
document.querySelectorAll('a[href]').forEach(function (a) {
if (a.dataset.tmPlayBtn === '1') return;
var href = a.href || '';
if (!LINK_RE.test(href)) return;
a.dataset.tmPlayBtn = '1';
a.insertAdjacentElement('afterend', makePlayButton(href));
});
}
function applyPageWidthLimit() {
var style = document.getElementById('tm-page-width-style');
if (!style) {
style = document.createElement('style');
style.id = 'tm-page-width-style';
document.documentElement.appendChild(style);
}
style.textContent = '@media (min-width: 769px) {' +
'html body {' +
'max-width: ' + PAGE_MAX_WIDTH + 'px !important;' +
'margin-left: auto !important;' +
'margin-right: auto !important;' +
'}' +
'}';
}
function getOpts() {
var saved = {};
try { saved = GM_getValue('amobbs_paste_opts', {}) || {}; } catch (e) { }
var opts = {};
for (var k in DEFAULT_OPTS) {
opts[k] = (typeof saved[k] === 'number') ? saved[k] : DEFAULT_OPTS[k];
}
return opts;
}
var toastEl = null;
var toastTimer = null;
function showToast(text, isError, stayMs) {
if (!toastEl) {
toastEl = document.createElement('div');
toastEl.style.cssText = [
'position:fixed', 'right:24px', 'bottom:32px', 'z-index:2147483647',
'max-width:440px', 'padding:10px 16px', 'border-radius:8px',
'font-size:13px', 'line-height:1.5', 'color:#fff',
'box-shadow:0 4px 18px rgba(0,0,0,0.35)', 'display:none'
].join(';');
document.documentElement.appendChild(toastEl);
}
toastEl.textContent = text;
toastEl.style.background = isError ? '#c0392b' : '#20242b';
toastEl.style.display = 'block';
if (toastTimer) clearTimeout(toastTimer);
if (stayMs) {
toastTimer = setTimeout(function () { toastEl.style.display = 'none'; }, stayMs);
}
}
var cachedParams = null;
function collectPageText() {
var out = [];
document.querySelectorAll('script').forEach(function (s) { out.push(s.textContent || ''); });
document.querySelectorAll('form').forEach(function (f) { out.push(f.getAttribute('action') || ''); });
return out.join('\n');
}
function extractParams() {
if (cachedParams) return cachedParams;
var all = collectPageText();
var uid = null;
var hash = null;
var fid = null;
var m = all.match(/"uid"\s*:\s*["'](\d+)["']\s*,\s*"hash"\s*:\s*["']([0-9a-fA-F]{32})["']/);
if (m) { uid = m[1]; hash = m[2]; }
if (!hash) {
m = all.match(/"hash"\s*:\s*["']([0-9a-fA-F]{32})["']\s*,\s*"uid"\s*:\s*["'](\d+)["']/);
if (m) { hash = m[1]; uid = uid || m[2]; }
}
if (!hash) {
m = all.match(/[?&]hash=([0-9a-fA-F]{32})/);
if (m) hash = m[1];
}
if (!uid) {
m = all.match(/discuz_uid\s*=\s*['"](\d+)['"]/);
if (m) uid = m[1];
}
m = all.match(/var\s+fid\s*=\s*parseInt\('(\d+)'\)/);
if (!m) m = all.match(/fid=(\d+)/);
if (!m) m = location.search.match(/[?&]fid=(\d+)/);
if (m) fid = m[1];
if (uid && hash && fid && uid !== '0') {
cachedParams = { uid: uid, hash: hash, fid: fid };
return cachedParams;
}
return null;
}
function findEditorTextarea(target) {
var tas = document.querySelectorAll('textarea[name="message"]');
if (!tas.length) return null;
for (var i = 0; i < tas.length; i++) {
if (tas[i] === target || (target && tas[i].contains(target))) return tas[i];
}
for (i = 0; i < tas.length; i++) {
if (tas[i].offsetWidth > 0 || tas[i].offsetHeight > 0) return tas[i];
}
return tas[0];
}
function insertIntoEditor(ta, txt) {
if (ta.id === 'fastpostmessage' && typeof window.seditor_insertunit === 'function') {
try {
window.seditor_insertunit('fastpost', txt);
return;
} catch (e) { }
}
var start = (ta.selectionStart != null) ? ta.selectionStart : ta.value.length;
var end = (ta.selectionEnd != null) ? ta.selectionEnd : ta.value.length;
var pre = ta.value.slice(0, start);
var post = ta.value.slice(end);
var nl = (pre && pre.charAt(pre.length - 1) !== '\n') ? '\n' : '';
ta.value = pre + nl + txt + post;
var pos = start + nl.length + txt.length;
ta.selectionStart = ta.selectionEnd = pos;
try { ta.dispatchEvent(new Event('input', { bubbles: true })); } catch (e) { }
ta.focus();
}
function addAttachHiddenFields(form, aid) {
['description', 'readperm', 'price'].forEach(function (f) {
var name = 'attachnew[' + aid + '][' + f + ']';
if (form.querySelector('input[name="' + name + '"]')) return;
var inp = document.createElement('input');
inp.type = 'hidden';
inp.name = name;
inp.value = '';
form.appendChild(inp);
});
}
function compressBlob(blob, opts) {
return new Promise(function (resolve, reject) {
var url = URL.createObjectURL(blob);
var img = new Image();
img.onload = function () {
var w = img.naturalWidth || img.width;
var h = img.naturalHeight || img.height;
var scale = Math.min(1, opts.maxDim / Math.max(w, h));
var cw = Math.max(1, Math.round(w * scale));
var ch = Math.max(1, Math.round(h * scale));
var canvas = document.createElement('canvas');
canvas.width = cw;
canvas.height = ch;
var ctx = canvas.getContext('2d');
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, cw, ch);
ctx.drawImage(img, 0, 0, cw, ch);
URL.revokeObjectURL(url);
canvas.toBlob(function (b) {
if (b && b.size) resolve(b);
else reject(new Error('压缩失败'));
}, 'image/jpeg', opts.quality);
};
img.onerror = function () {
URL.revokeObjectURL(url);
reject(new Error('图片解码失败,无法压缩'));
};
img.src = url;
});
}
function uploadBlob(blob, params, onProgress) {
return new Promise(function (resolve, reject) {
var fd = new FormData();
fd.append('uid', params.uid);
fd.append('hash', params.hash);
fd.append('Filedata', blob, blob.name || 'clipboard.png');
var url = UPLOAD_URL + '&fid=' + params.fid + '&type=image&uid=' + params.uid + '&hash=' + params.hash;
var xhr = new XMLHttpRequest();
xhr.open('POST', url);
xhr.timeout = 90000;
if (xhr.upload && onProgress) {
xhr.upload.onprogress = function (e) {
if (e.lengthComputable) onProgress(Math.min(0.95, e.loaded / e.total));
};
}
xhr.onload = function () { resolve(xhr.responseText || ''); };
xhr.onerror = function () { reject(new Error('网络错误')); };
xhr.ontimeout = function () { reject(new Error('上传超时')); };
xhr.send(fd);
});
}
function parseUploadResult(text) {
var t = String(text).trim();
if (!t) throw new Error('上传接口无响应,可能登录已过期,请刷新页面重试');
var parts = t.split('|');
if (parts[0] === 'DISCUZUPLOAD') {
var st = parseInt(parts[1], 10);
var aid = parseInt(parts[2], 10);
if (st === 0 && aid > 0) return { aid: aid, status: 0 };
return { aid: 0, status: (isNaN(st) ? -99 : st) };
}
var n = parseInt(t, 10);
if (!isNaN(n)) {
if (n > 0) return { aid: n, status: 0 };
return { aid: 0, status: -n };
}
throw new Error('上传响应无法识别');
}
function handleImageFile(file, ta, form, total, index) {
var opts = getOpts();
var label = (total > 1) ? ('图片 ' + index + '/' + total + ' ') : '';
return Promise.resolve().then(function () {
if (file.size > opts.maxBytes) {
showToast(label + '图片较大,压缩中…', false, 0);
return compressBlob(file, opts).then(function (b) {
return { blob: b, name: 'clipboard_compressed.jpg' };
});
}
return { blob: file, name: file.name || 'clipboard.png' };
}).then(function (prepared) {
showToast(label + '上传中…', false, 0);
var onP = function (p) {
showToast(label + '上传中… ' + Math.round(p * 100) + '%', false, 0);
};
return uploadBlob(prepared.blob, cachedParams, onP).then(function (respText) {
var r = parseUploadResult(respText);
if (r.status === 0) return { aid: r.aid };
if (r.status === 3 || r.status === 5) {
showToast(label + '体积超限,自动压缩后重试…', false, 0);
return compressBlob(prepared.blob, { maxDim: opts.maxDim, quality: 0.7 }).then(function (b2) {
return uploadBlob(b2, cachedParams, onP).then(function (respText2) {
var r2 = parseUploadResult(respText2);
if (r2.status === 0) return { aid: r2.aid };
throw new Error(STATUS_MSG[r2.status] || ('上传失败(状态' + r2.status + ')'));
});
});
}
if (r.status === 10) cachedParams = null;
throw new Error(STATUS_MSG[r.status] || ('上传失败(状态' + r.status + ')'));
}).then(function (ok) {
insertIntoEditor(ta, '[attachimg]' + ok.aid + '[/attachimg]\n');
addAttachHiddenFields(form, ok.aid);
showToast(label + '上传成功,已插入图片(附件号 ' + ok.aid + ')', false, 2600);
});
});
}
var uploadQueue = Promise.resolve();
function onPaste(e) {
var ta = findEditorTextarea(e.target);
if (!ta) return;
var cd = e.clipboardData;
if (!cd) return;
var items = cd.items || [];
var files = [];
for (var i = 0; i < items.length; i++) {
if (items[i].kind === 'file' && items[i].type.indexOf('image/') === 0) {
var f = items[i].getAsFile();
if (f && f.size) files.push(f);
}
}
if (!files.length && cd.files && cd.files.length) {
for (var j = 0; j < cd.files.length; j++) {
if (cd.files[j].type.indexOf('image/') === 0) files.push(cd.files[j]);
}
}
if (!files.length) return;
e.preventDefault();
e.stopPropagation();
var params = extractParams();
if (!params) {
var all = collectPageText();
if (!/"uid"\s*:\s*["']\d+["']/.test(all) || /discuz_uid\s*=\s*['"]0['"]/.test(all)) {
showToast('未登录,无法上传图片', true, 4000);
} else {
showToast('未在页面中找到上传参数(uid/hash),请刷新页面后重试', true, 5000);
}
return;
}
if (typeof window.wysiwyg !== 'undefined' && parseInt(window.wysiwyg, 10) === 1) {
showToast('当前是所见即所得模式,请先切换到纯文本模式再粘贴图片', true, 5000);
return;
}
var form = ta.closest ? ta.closest('form') : ta.form;
if (!form) {
showToast('未找到发帖表单', true, 4000);
return;
}
var plainText = '';
try { plainText = cd.getData('text/plain') || ''; } catch (ex) { }
if (plainText) insertIntoEditor(ta, plainText);
var total = files.length;
for (var k = 0; k < files.length; k++) {
(function (file, idx) {
uploadQueue = uploadQueue.then(function () {
return handleImageFile(file, ta, form, total, idx + 1);
}).catch(function (err) {
showToast('图片上传失败:' + (err && err.message ? err.message : err), true, 6000);
});
})(files[k], k);
}
}
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
var overlay = document.getElementById(POPUP_ID);
if (overlay && overlay.style.display === 'flex') hidePopup();
}
});
window.addEventListener('click', function (e) {
var overlay = document.getElementById(POPUP_ID);
if (overlay && e.target === overlay) hidePopup();
});
document.addEventListener('dragstart', function (e) {
var overlay = document.getElementById(POPUP_ID);
if (!overlay || overlay.style.display !== 'flex') return;
var box = overlay._box;
if (box._maximized) return;
var bar = box.querySelector('div');
if (!bar || !bar.contains(e.target)) return;
var shiftX = e.clientX - box.getBoundingClientRect().left;
var shiftY = e.clientY - box.getBoundingClientRect().top;
function move(ev) {
var x = ev.clientX - shiftX;
var y = ev.clientY - shiftY;
overlay.style.alignItems = 'flex-start';
overlay.style.justifyContent = 'flex-start';
box.style.marginLeft = Math.max(0, x) + 'px';
box.style.marginTop = Math.max(0, y) + 'px';
}
function up() {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', up);
}
document.addEventListener('mousemove', move);
document.addEventListener('mouseup', up);
});
document.addEventListener('paste', onPaste, true);
attachButtons();
var observerStarted = false;
function startObserver() {
var root = document.body || document.documentElement;
if (!root) {
document.addEventListener('DOMContentLoaded', startObserver, { once: true });
return;
}
if (observerStarted) return;
observerStarted = true;
new MutationObserver(function () {
attachButtons();
applyPageWidthLimit();
}).observe(root, { childList: true, subtree: true });
}
startObserver();
applyPageWidthLimit();
})();