匿名
未登录
创建账号
登录
Postal Wiki
搜索
查看“︁用户:Hehua/filepreview.js”︁的源代码
来自Postal Wiki
命名空间
用户页
讨论
更多
更多
页面操作
阅读
查看源代码
历史
用户贡献
←
用户:Hehua/filepreview.js
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您没有权限编辑此JavaScript页面,因为此页面包含另一位用户的个人设置。
您可以查看和复制此页面的源代码。
/** * FilePreview — 文件链接悬停预览脚本 * 鼠标悬停在 File: 蓝色链接上时,弹出浮窗显示: * - 缩略图预览 * - 文件信息(尺寸、大小、上传者) * - 分类 (categories) * - 版权/许可模板 (templates) * * 安装方式:将本脚本的内容复制到你的 * User:<你的名字>/common.js 或 MediaWiki:Common.js * 或通过 importScript 引入。 */ mw.loader.using(['mediawiki.api', 'mediawiki.util']).then(function () { 'use strict'; var CONFIG = { delay: 400, // 悬停延迟 (ms) thumbWidth: 220, // 缩略图宽度 maxPopupWidth: 460, maxPopupHeight: 520 }; /* ---------- state ---------- */ var state = { link: null, fetchTimer: null, hideTimer: null, popup: null }; /* ---------- popup DOM ---------- */ function getPopup() { if (!state.popup) { var el = document.createElement('div'); el.id = 'filepreview-popup'; el.style.cssText = [ 'display:none', 'position:fixed', 'z-index:9999', 'background:#fff', 'border:1px solid #c8ccd1', 'border-radius:6px', 'box-shadow:0 4px 16px rgba(0,0,0,0.2)', 'padding:14px', 'max-width:' + CONFIG.maxPopupWidth + 'px', 'max-height:' + CONFIG.maxPopupHeight + 'px', 'overflow-y:auto', 'font:13px/1.5 sans-serif', 'color:#222', 'pointer-events:auto' ].join(';'); document.body.appendChild(el); state.popup = el; // 鼠标移到浮窗上时不隐藏 el.addEventListener('mouseenter', function () { clearTimeout(state.hideTimer); clearTimeout(state.fetchTimer); }); el.addEventListener('mouseleave', function (e) { if (e.relatedTarget && e.relatedTarget.closest && e.relatedTarget.closest('a') === state.link) { return; } hidePopup(); }); } return state.popup; } function showPopup(x, y, html) { var popup = getPopup(); popup.innerHTML = html; popup.style.display = 'block'; var rect = popup.getBoundingClientRect(); var left = Math.max(6, Math.min(x + 12, window.innerWidth - rect.width - 6)); var top = Math.max(6, Math.min(y + 12, window.innerHeight - rect.height - 6)); popup.style.left = left + 'px'; popup.style.top = top + 'px'; } function hidePopup() { clearTimeout(state.hideTimer); clearTimeout(state.fetchTimer); if (state.popup) state.popup.style.display = 'none'; state.link = null; } /* ---------- helpers ---------- */ function getFileTitle(link) { var title = link.getAttribute('title') || link.getAttribute('data-title') || ''; var m = title.match(/^(File|Image):(.+)$/i); if (m) return m[1] + ':' + m[2]; // 后备:从 href 解析 var href = link.getAttribute('href') || ''; m = href.match(/[?&]title=(File|Image):([^&]+)/); if (m) return m[1] + ':' + decodeURIComponent(m[2].replace(/\+/g, ' ')); return null; } function isFileLink(link) { if (!link || !link.href) return false; var title = link.getAttribute('title') || ''; if (/^(File|Image):/i.test(title)) return true; return /[?&]title=(File|Image):/i.test(link.search || link.href); } function fmtBytes(b) { if (!b) return '?'; if (b < 1024) return b + ' B'; if (b < 1048576) return (b / 1024).toFixed(1) + ' KB'; return (b / 1048576).toFixed(1) + ' MB'; } function esc(s) { return mw.html.escape(String(s)); } /* ---------- API ---------- */ function fetchFileData(fileTitle) { var api = new mw.Api(); return api.get({ action: 'query', titles: fileTitle, prop: 'categories|templates|imageinfo|info', cllimit: 50, tllimit: 50, clprop: 'sortkey', iiprop: 'url|size|user|comment|timestamp|extmetadata', iiurlwidth: CONFIG.thumbWidth, format: 'json' }); } /* ---------- render ---------- */ function renderPopup(data, x, y) { var pages = data.query && data.query.pages; if (!pages) { showPopup(x, y, '<div style="color:#c00;">无法获取数据</div>'); return; } var id = Object.keys(pages)[0]; var page = pages[id]; if (id === '-1' || !page) { showPopup(x, y, '<div style="color:#888;">文件页面不存在</div>'); return; } var parts = []; parts.push('<div style="font-weight:bold;font-size:14px;margin-bottom:6px;word-break:break-all;">' + esc(page.title) + '</div>'); // 缩略图 var ii = page.imageinfo && page.imageinfo[0]; if (ii) { if (ii.thumburl) { parts.push('<div style="text-align:center;margin-bottom:8px;"><a href="' + esc(ii.url) + '" target="_blank">' + '<img src="' + esc(ii.thumburl) + '" alt="" style="max-width:100%;border:1px solid #eaecf0;border-radius:3px;"></a></div>'); } var meta = []; if (ii.width && ii.height) meta.push(ii.width + '×' + ii.height + ' (' + fmtBytes(ii.size) + ')'); if (ii.user) meta.push('上传者: ' + esc(ii.user)); if (meta.length) parts.push('<div style="color:#555;margin-bottom:4px;font-size:12px;">' + meta.join(' | ') + '</div>'); // 许可信息 (extmetadata) if (ii.extmetadata) { var licField = ii.extmetadata.LicenseShortName || ii.extmetadata.License || ii.extmetadata.Copyright; if (licField) { var licVal = licField.value || licField; parts.push('<div style="color:#555;margin-bottom:4px;font-size:12px;">许可: ' + esc(String(licVal)) + '</div>'); } } } // 分类 if (page.categories && page.categories.length) { parts.push('<div style="margin-top:8px;font-weight:bold;font-size:13px;">分类</div>'); parts.push('<ul style="margin:2px 0 0 14px;padding:0;list-style:none;">'); var cats = page.categories.slice(0, 25); cats.forEach(function (c) { parts.push('<li style="padding:1px 0;font-size:12px;">• ' + esc(c.title.replace(/^Category:/, '')) + '</li>'); }); if (page.categories.length > 25) { parts.push('<li style="color:#888;font-size:12px;">… 还有 ' + (page.categories.length - 25) + ' 个</li>'); } parts.push('</ul>'); } else { parts.push('<div style="margin-top:6px;color:#888;font-size:12px;">无分类</div>'); } // 模板(含版权模板) if (page.templates && page.templates.length) { parts.push('<div style="margin-top:8px;font-weight:bold;font-size:13px;">模板 (含版权/许可)</div>'); parts.push('<div style="margin:2px 0 0;max-height:130px;overflow-y:auto;">'); var tpls = page.templates.slice(0, 40); tpls.forEach(function (t) { var name = t.title.replace(/^Template:/, ''); parts.push('<span style="display:inline-block;background:#f0f2f5;border:1px solid #d4d8dd;border-radius:3px;padding:1px 5px;margin:1px 2px;font:12px monospace;color:#333;">{{' + esc(name) + '}}</span>'); }); if (page.templates.length > 40) { parts.push('<span style="color:#888;font-size:11px;"> …+' + (page.templates.length - 40) + '</span>'); } parts.push('</div>'); } // 底部链接 var url = mw.config.get('wgScript') || '/wiki/'; var target = page.title.replace(/ /g, '_'); parts.push('<div style="margin-top:10px;border-top:1px solid #eaecf0;padding-top:6px;text-align:right;font-size:12px;">' + '<a href="' + url + esc(target) + '" target="_blank">打开文件页 →</a></div>'); showPopup(x, y, parts.join('')); } /* ---------- hover handlers ---------- */ function onLinkOver(e) { var link = e.target.closest ? e.target.closest('a[href]') : null; if (!link || !isFileLink(link)) return; if (link === state.link) return; clearTimeout(state.fetchTimer); clearTimeout(state.hideTimer); state.link = link; var title = getFileTitle(link); if (!title) return; state.fetchTimer = setTimeout(function () { showPopup(e.clientX, e.clientY, '<div style="color:#888;font-size:13px;">加载中… ' + esc(title) + '</div>'); fetchFileData(title).then(function (data) { if (state.link === link) renderPopup(data, e.clientX, e.clientY); }).catch(function (err) { if (state.link === link) showPopup(e.clientX, e.clientY, '<div style="color:#c00;">请求失败: ' + esc(String(err)) + '</div>'); }); }, CONFIG.delay); } function onLinkOut(e) { var link = e.target.closest ? e.target.closest('a[href]') : null; if (!link || !isFileLink(link)) return; if (link !== state.link) return; // 如果鼠标进入浮窗则不隐藏 clearTimeout(state.fetchTimer); state.hideTimer = setTimeout(function () { hidePopup(); }, 200); } /* ---------- install ---------- */ document.addEventListener('mouseover', onLinkOver, true); document.addEventListener('mouseout', onLinkOut, true); // 滚动时隐藏 window.addEventListener('scroll', function () { hidePopup(); }, true); });
该页面使用的模板:
模板:' + esc(t.title.replace(/^Template:/, '')) + '
(
查看源代码
)
返回
用户:Hehua/filepreview.js
。
导航
导航
首页
讨论版
最近更改
随机页面
深色模式
帮助
规章制度
编辑规范
格式指导
管理员公示板
人员任免方针
沙盒
链接
QQ群
Discord社区服务器
wiki工具
wiki工具
特殊页面
页面工具
页面工具
用户页面工具
日志
查看用户组
更多
链入页面
相关更改
页面信息
页面日志