跳转到内容
打开/关闭搜索
搜索
打开/关闭菜单
13
条目
4
用户
232
编辑
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
首页
子服一览
总规章
常见问题
历史资料
全部条目
分类
最近更改
打开/关闭外观设置菜单
无法加载偏好设置。请检查您的网络连接并重试。
重试
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。
user-interface-preferences
个人工具
讨论
贡献
创建账号
登录
查看“︁MediaWiki:Common.js”︁的源代码
MediaWiki界面页面
查看
阅读
查看源代码
查看历史
associated-pages
系统消息
讨论
更多操作
更多
工具
链入页面
相关更改
页面信息
←
MediaWiki:Common.js
因为以下原因,您没有权限编辑该页面:
此页面为本wiki上的软件提供界面文本,并受到保护以防止滥用。如欲修改所有wiki的翻译,请访问
translatewiki.net
上的MediaWiki本地化项目。
您无权编辑此JavaScript页面,因为编辑此页面可能会影响所有访问者。
您可以查看和复制此页面的源代码。
/* 横版登录 / 注册弹窗。 表单在本地直接渲染(不再抓取 Special 页面的 HTML),提交走 Action API clientlogin / createaccount,token 在页面加载时就后台预取, 因此弹窗打开即可用,没有等待。 独立页面 Special:UserLogin / Special:CreateAccount 保留,无 JS 时仍可用。 */ ( function () { 'use strict'; var LOGO = 'https://m.ccw.site/gandi_application/user_assets/2a6bb37880317d2bb5525ab560618e04.png'; var MODES = { login: { page: 'Special:UserLogin', tab: '登录', title: '登录到 Kuke Wiki', sub: '登录后即可编辑条目、参与讨论并同步个人偏好设置。', submit: '登录', busy: '正在登录…' }, signup: { page: 'Special:CreateAccount', tab: '注册', title: '创建账号', sub: '加入 KukeMC 官方百科的编辑与维护,只需一分钟。', submit: '创建账号', busy: '正在创建…' } }; var FIELDS = { login: [ { name: 'username', label: '用户名', type: 'text', autocomplete: 'username', required: true }, { name: 'password', label: '密码', type: 'password', autocomplete: 'current-password', required: true }, { name: 'rememberMe', label: '在此浏览器上保持登录', type: 'checkbox' } ], signup: [ { name: 'username', label: '用户名', type: 'text', autocomplete: 'username', required: true }, { name: 'password', label: '密码', type: 'password', autocomplete: 'new-password', required: true }, { name: 'retype', label: '确认密码', type: 'password', autocomplete: 'new-password', required: true }, { name: 'email', label: '电子邮件', type: 'email', autocomplete: 'email', hint: '选填,用于找回密码' }, { name: 'captchaWord', label: '验证码', type: 'captcha', required: true } ] }; var POINTS = [ { icon: 'pencil', text: '编辑与创建条目,完善服务器资料' }, { icon: 'chat', text: '参与讨论页,与其他编辑者协作' }, { icon: 'star', text: '保存个人偏好、监视列表与编辑历史' }, { icon: 'clock', text: '追踪最近更改,第一时间了解更新' } ]; var ICONS = { pencil: 'M13.5 2.5a2.12 2.12 0 0 1 3 3L7 15l-4 1 1-4Z', chat: 'M3 5.5A1.5 1.5 0 0 1 4.5 4h11A1.5 1.5 0 0 1 17 5.5v7a1.5 1.5 0 0 1-1.5 1.5H8l-4 3v-3h-.5A1.5 1.5 0 0 1 3 12.5Z', star: 'm10 2.6 2.3 4.66 5.15.75-3.72 3.63.88 5.12L10 14.34l-4.6 2.42.87-5.12L2.55 8l5.15-.75Z', clock: 'M10 2.5a7.5 7.5 0 1 1 0 15 7.5 7.5 0 0 1 0-15Zm0 3.6v4.2l3 1.8', close: 'M5 5l10 10M15 5L5 15', eye: 'M1.8 10S4.9 4.7 10 4.7 18.2 10 18.2 10 15.1 15.3 10 15.3 1.8 10 1.8 10Zm8.2 2.2a2.2 2.2 0 1 0 0-4.4 2.2 2.2 0 0 0 0 4.4Z', eyeOff: 'M3 3l14 14M8.2 8.3A2.2 2.2 0 0 0 10 12.2c.6 0 1.1-.2 1.6-.6M6.1 6.2C3.6 7.6 1.8 10 1.8 10s3.1 5.3 8.2 5.3c1.4 0 2.6-.4 3.7-1M15.5 13C17.3 11.8 18.2 10 18.2 10S15.1 4.7 10 4.7c-.7 0-1.4.1-2 .3', alert: 'M10 6.6v4.2m0 2.6h.01M8.6 3.2 2.2 14.4A1.6 1.6 0 0 0 3.6 16.8h12.8a1.6 1.6 0 0 0 1.4-2.4L11.4 3.2a1.6 1.6 0 0 0-2.8 0Z' }; var root = null; var main = null; var body = null; var titleEl = null; var subEl = null; var alertEl = null; var lastFocus = null; var current = null; var api = null; var apiReady = null; var tokens = {}; var captcha = null; var submitting = false; function svg( d, cls, size ) { var s = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' ); s.setAttribute( 'viewBox', '0 0 20 20' ); s.setAttribute( 'width', size || '18' ); s.setAttribute( 'height', size || '18' ); s.setAttribute( 'aria-hidden', 'true' ); s.setAttribute( 'fill', 'none' ); s.setAttribute( 'stroke', 'currentColor' ); s.setAttribute( 'stroke-width', '1.6' ); s.setAttribute( 'stroke-linecap', 'round' ); s.setAttribute( 'stroke-linejoin', 'round' ); if ( cls ) { s.setAttribute( 'class', cls ); } var p = document.createElementNS( 'http://www.w3.org/2000/svg', 'path' ); p.setAttribute( 'd', d ); s.appendChild( p ); return s; } function el( tag, cls, text ) { var n = document.createElement( tag ); if ( cls ) { n.className = cls; } if ( text ) { n.textContent = text; } return n; } function returnTarget() { var page = mw.config.get( 'wgPageName' ); if ( /^Special:(UserLogin|CreateAccount|UserLogout)$/i.test( page ) ) { return mw.config.get( 'wgMainPageTitle' ) || '首页'; } return page; } function returnUrl() { return location.origin + mw.util.getUrl( returnTarget() ); } /* ---------- API ---------- */ function ready() { if ( !apiReady ) { apiReady = mw.loader.using( [ 'mediawiki.api', 'mediawiki.util' ] ).then( function () { api = new mw.Api(); return api; } ); } return apiReady; } function token( type ) { if ( tokens[ type ] ) { return tokens[ type ]; } tokens[ type ] = ready().then( function () { return api.get( { action: 'query', meta: 'tokens', type: type, formatversion: 2 } ); } ).then( function ( d ) { return d.query.tokens[ type + 'token' ]; } ); return tokens[ type ]; } /* QuestyCaptcha 的题面与 id,每次进入注册态重新取(旧 id 会失效) */ function loadCaptcha() { return ready().then( function () { return api.get( { action: 'query', meta: 'authmanagerinfo', amirequestsfor: 'create', formatversion: 2 } ); } ).then( function ( d ) { var reqs = ( d.query && d.query.authmanagerinfo && d.query.authmanagerinfo.requests ) || []; var hit = null; reqs.forEach( function ( r ) { if ( r.id && r.id.indexOf( 'Captcha' ) !== -1 && r.fields && r.fields.captchaId ) { hit = r; } } ); if ( !hit ) { return null; } return { id: hit.fields.captchaId.value, question: hit.fields.captchaInfo ? hit.fields.captchaInfo.value : '', label: ( hit.fields.captchaWord && hit.fields.captchaWord.label ) || '验证码' }; } ).catch( function () { return null; } ); } /* ---------- 结构 ---------- */ function build() { root = el( 'div', 'kuke-auth' ); root.hidden = true; var backdrop = el( 'div', 'kuke-auth__backdrop' ); backdrop.addEventListener( 'click', close ); root.appendChild( backdrop ); var dialog = el( 'div', 'kuke-auth__dialog' ); dialog.setAttribute( 'role', 'dialog' ); dialog.setAttribute( 'aria-modal', 'true' ); dialog.setAttribute( 'aria-labelledby', 'kuke-auth-title' ); var closeBtn = el( 'button', 'kuke-auth__close' ); closeBtn.type = 'button'; closeBtn.setAttribute( 'aria-label', '关闭' ); closeBtn.appendChild( svg( ICONS.close ) ); closeBtn.addEventListener( 'click', close ); dialog.appendChild( closeBtn ); // 左:品牌栏 var aside = el( 'aside', 'kuke-auth__aside' ); var logo = el( 'img', 'kuke-auth__logo' ); logo.src = LOGO; logo.alt = ''; logo.width = 44; logo.height = 44; aside.appendChild( logo ); aside.appendChild( el( 'p', 'kuke-auth__brand', 'Kuke Wiki' ) ); aside.appendChild( el( 'p', 'kuke-auth__tagline', 'KukeMC 群组服官方百科' ) ); var ul = el( 'ul', 'kuke-auth__points' ); POINTS.forEach( function ( item ) { var li = el( 'li', 'kuke-auth__point' ); li.appendChild( svg( ICONS[ item.icon ], 'kuke-auth__point-icon' ) ); li.appendChild( el( 'span', null, item.text ) ); ul.appendChild( li ); } ); aside.appendChild( ul ); aside.appendChild( el( 'p', 'kuke-auth__note', '本站内容由社区共同维护,欢迎参与。' ) ); dialog.appendChild( aside ); // 右:表单栏 main = el( 'div', 'kuke-auth__main' ); var head = el( 'div', 'kuke-auth__head' ); var tabs = el( 'div', 'kuke-auth__tabs' ); tabs.setAttribute( 'role', 'tablist' ); Object.keys( MODES ).forEach( function ( key ) { var b = el( 'button', 'kuke-auth__tab', MODES[ key ].tab ); b.type = 'button'; b.dataset.mode = key; b.setAttribute( 'role', 'tab' ); b.addEventListener( 'click', function () { render( key ); } ); tabs.appendChild( b ); } ); head.appendChild( tabs ); titleEl = el( 'h2', 'kuke-auth__title' ); titleEl.id = 'kuke-auth-title'; head.appendChild( titleEl ); subEl = el( 'p', 'kuke-auth__sub' ); head.appendChild( subEl ); main.appendChild( head ); body = el( 'div', 'kuke-auth__body' ); main.appendChild( body ); dialog.appendChild( main ); root.appendChild( dialog ); document.body.appendChild( root ); } /* ---------- 表单 ---------- */ function passwordToggle( input ) { var btn = el( 'button', 'kuke-auth__peek' ); btn.type = 'button'; btn.setAttribute( 'aria-label', '显示密码' ); btn.appendChild( svg( ICONS.eye, null, '17' ) ); btn.addEventListener( 'click', function () { var show = input.type === 'password'; input.type = show ? 'text' : 'password'; btn.setAttribute( 'aria-label', show ? '隐藏密码' : '显示密码' ); btn.textContent = ''; btn.appendChild( svg( show ? ICONS.eyeOff : ICONS.eye, null, '17' ) ); input.focus(); } ); return btn; } function field( spec, form ) { if ( spec.type === 'checkbox' ) { var wrap = el( 'label', 'kuke-auth__check' ); var box = el( 'input' ); box.type = 'checkbox'; box.name = spec.name; wrap.appendChild( box ); wrap.appendChild( el( 'span', null, spec.label ) ); return wrap; } var id = 'kuke-auth-' + spec.name; var row = el( 'div', 'kuke-auth__field' ); var label = el( 'label', 'kuke-auth__label', spec.label ); label.htmlFor = id; if ( !spec.required ) { label.appendChild( el( 'span', 'kuke-auth__optional', '选填' ) ); } row.appendChild( label ); var control = el( 'div', 'kuke-auth__control' ); var input = el( 'input', 'kuke-auth__input' ); input.id = id; input.name = spec.name; input.type = spec.type === 'captcha' ? 'text' : spec.type; if ( spec.autocomplete ) { input.autocomplete = spec.autocomplete; } if ( spec.required ) { input.required = true; } if ( spec.type === 'captcha' ) { input.autocomplete = 'off'; input.inputMode = 'text'; row.classList.add( 'kuke-auth__field--captcha' ); } control.appendChild( input ); if ( spec.type === 'password' ) { control.classList.add( 'kuke-auth__control--peek' ); control.appendChild( passwordToggle( input ) ); } row.appendChild( control ); if ( spec.type === 'captcha' ) { var q = el( 'p', 'kuke-auth__captcha' ); q.appendChild( el( 'span', 'kuke-auth__captcha-q', '正在获取…' ) ); row.appendChild( q ); form.captchaQuestion = q.firstChild; loadCaptcha().then( function ( data ) { if ( !form.isConnected ) { return; } captcha = data; q.firstChild.textContent = data ? data.question : '(暂不可用,请稍后重试)'; } ); } if ( spec.hint ) { row.appendChild( el( 'p', 'kuke-auth__hint', spec.hint ) ); } return row; } function showAlert( text ) { if ( !alertEl ) { return; } alertEl.textContent = ''; if ( !text ) { alertEl.hidden = true; return; } alertEl.appendChild( svg( ICONS.alert, 'kuke-auth__alert-icon' ) ); alertEl.appendChild( el( 'span', null, text ) ); alertEl.hidden = false; } function setSubmitting( btn, mode, on ) { submitting = on; btn.disabled = on; btn.classList.toggle( 'kuke-auth__submit--busy', on ); btn.textContent = ''; if ( on ) { btn.appendChild( el( 'span', 'kuke-auth__spinner' ) ); btn.appendChild( el( 'span', null, MODES[ mode ].busy ) ); } else { btn.appendChild( el( 'span', null, MODES[ mode ].submit ) ); } } function values( form ) { var out = {}; Array.prototype.forEach.call( form.querySelectorAll( 'input' ), function ( i ) { out[ i.name ] = i.type === 'checkbox' ? ( i.checked ? '1' : '' ) : i.value; } ); return out; } function submit( mode, form, btn ) { if ( submitting ) { return; } var v = values( form ); if ( mode === 'signup' && v.password !== v.retype ) { showAlert( '两次输入的密码不一致。' ); return; } showAlert( '' ); setSubmitting( btn, mode, true ); var type = mode === 'login' ? 'login' : 'createaccount'; ready().then( function () { return token( type ); } ).then( function ( tk ) { var params; if ( mode === 'login' ) { params = { action: 'clientlogin', loginreturnurl: returnUrl(), logintoken: tk, username: v.username, password: v.password, formatversion: 2, errorformat: 'html', errorlang: 'zh' }; if ( v.rememberMe ) { params.rememberMe = '1'; } } else { params = { action: 'createaccount', createreturnurl: returnUrl(), createtoken: tk, username: v.username, password: v.password, retype: v.retype, formatversion: 2, errorformat: 'html', errorlang: 'zh' }; if ( v.email ) { params.email = v.email; } if ( captcha ) { params.captchaId = captcha.id; params.captchaWord = v.captchaWord; } } return api.post( params ); } ).then( function ( d ) { var res = d.clientlogin || d.createaccount || {}; if ( res.status === 'PASS' ) { location.href = returnUrl(); return; } // 需要额外步骤(如二次验证)时交给完整页面处理 if ( res.status === 'UI' || res.status === 'REDIRECT' ) { location.href = mw.util.getUrl( MODES[ mode ].page, { returnto: returnTarget() } ); return; } setSubmitting( btn, mode, false ); showAlert( res.message || '提交失败,请稍后重试。' ); // token 与验证码一次性,失败后重取 delete tokens[ type ]; if ( mode === 'signup' ) { refreshCaptcha( form ); } } ).catch( function ( code, data ) { setSubmitting( btn, mode, false ); var msg = ( data && data.error && data.error.info ) || '网络异常,请稍后重试。'; showAlert( msg ); delete tokens[ type ]; } ); } function refreshCaptcha( form ) { var q = form.captchaQuestion; if ( !q ) { return; } q.textContent = '正在获取…'; loadCaptcha().then( function ( data ) { captcha = data; if ( q.isConnected ) { q.textContent = data ? data.question : '(暂不可用,请稍后重试)'; } } ); } function render( mode ) { if ( current === mode ) { return; } current = mode; captcha = null; titleEl.textContent = MODES[ mode ].title; subEl.textContent = MODES[ mode ].sub; Array.prototype.forEach.call( root.querySelectorAll( '.kuke-auth__tab' ), function ( b ) { var on = b.dataset.mode === mode; b.classList.toggle( 'kuke-auth__tab--active', on ); b.setAttribute( 'aria-selected', on ? 'true' : 'false' ); } ); body.textContent = ''; alertEl = el( 'div', 'kuke-auth__alert' ); alertEl.setAttribute( 'role', 'alert' ); alertEl.hidden = true; body.appendChild( alertEl ); var form = el( 'form', 'kuke-auth__form' ); form.setAttribute( 'novalidate', 'novalidate' ); FIELDS[ mode ].forEach( function ( spec ) { form.appendChild( field( spec, form ) ); } ); var btn = el( 'button', 'kuke-auth__submit' ); btn.type = 'submit'; btn.appendChild( el( 'span', null, MODES[ mode ].submit ) ); form.appendChild( btn ); form.addEventListener( 'submit', function ( e ) { e.preventDefault(); submit( mode, form, btn ); } ); body.appendChild( form ); var foot = el( 'p', 'kuke-auth__foot' ); if ( mode === 'login' ) { var reset = el( 'a', null, '忘记密码?' ); reset.href = mw.util.getUrl( 'Special:PasswordReset' ); foot.appendChild( reset ); foot.appendChild( el( 'span', 'kuke-auth__dot', '·' ) ); var toSignup = el( 'a', null, '还没有账号?立即注册' ); toSignup.href = '#'; toSignup.addEventListener( 'click', function ( e ) { e.preventDefault(); render( 'signup' ); } ); foot.appendChild( toSignup ); } else { foot.appendChild( el( 'span', null, '已有账号?' ) ); var toLogin = el( 'a', null, '直接登录' ); toLogin.href = '#'; toLogin.addEventListener( 'click', function ( e ) { e.preventDefault(); render( 'login' ); } ); foot.appendChild( toLogin ); } body.appendChild( foot ); main.scrollTop = 0; // 预热对应的 token token( mode === 'login' ? 'login' : 'createaccount' ); var first = form.querySelector( 'input' ); if ( first ) { first.focus(); } } /* ---------- 开关 ---------- */ function onKey( e ) { if ( e.key === 'Escape' ) { close(); return; } if ( e.key !== 'Tab' || root.hidden ) { return; } var items = root.querySelectorAll( 'a[href], button:not([disabled]), input:not([type=hidden]), select, textarea' ); if ( !items.length ) { return; } var first = items[ 0 ]; var last = items[ items.length - 1 ]; if ( e.shiftKey && document.activeElement === first ) { e.preventDefault(); last.focus(); } else if ( !e.shiftKey && document.activeElement === last ) { e.preventDefault(); first.focus(); } } function open( mode ) { if ( !root ) { build(); } lastFocus = document.activeElement; root.hidden = false; document.body.classList.add( 'kuke-auth-open' ); document.addEventListener( 'keydown', onKey ); render( mode ); } function close() { if ( !root || root.hidden ) { return; } root.hidden = true; current = null; document.body.classList.remove( 'kuke-auth-open' ); document.removeEventListener( 'keydown', onKey ); if ( lastFocus && lastFocus.focus ) { lastFocus.focus(); } } function modeOf( href ) { var target; try { target = new URL( href, location.href ); } catch ( e ) { return null; } if ( target.origin !== location.origin ) { return null; } var path = decodeURIComponent( target.pathname + '?' + target.search ); if ( /Special:UserLogin|特殊:用户登录|Special:登录/i.test( path ) ) { return 'login'; } if ( /Special:CreateAccount|特殊:创建账户|Special:创建账号/i.test( path ) ) { return 'signup'; } return null; } function onClick( e ) { if ( e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey ) { return; } var a = e.target.closest ? e.target.closest( 'a[href]' ) : null; if ( !a || a.target === '_blank' ) { return; } var mode = modeOf( a.getAttribute( 'href' ) ); if ( !mode ) { return; } e.preventDefault(); open( mode ); } function init() { if ( mw.config.get( 'wgUserName' ) ) { return; } document.addEventListener( 'click', onClick ); // 后台预取登录 token,弹窗打开时表单已可直接提交 token( 'login' ); var direct = /[?&]kukeauth=(login|signup)/.exec( location.search ); if ( direct ) { open( direct[ 1 ] ); } } if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', init ); } else { init(); } }() ); /* 站内跳转加载动画。 只在「点链接 / 提交表单」触发的整页跳转时出现,首次进站不显示。 分级出现,秒开的页面不会闪: 80ms 顶部细进度条 150ms 全屏磨砂遮罩 动画画在离开前的旧页面上。浏览器收到新页面响应后会把旧页面冻结成静态画面, 所以遮罩一旦出现就把标记写进 sessionStorage,新页面在 <head> 里读到标记后 立刻用同一层遮罩接力(见 LocalSettings.php 的 BeforePageDisplay 钩子), 由本文件在页面可交互时摘掉,整段等待被完整盖住。 */ ( function () { 'use strict'; var BAR_DELAY = 80; var VEIL_DELAY = 150; var GIVE_UP = 20000; var RELAY_KEY = 'kukeLoad'; var LOGO = 'https://m.ccw.site/gandi_application/user_assets/2a6bb37880317d2bb5525ab560618e04.png'; var bar, veil, barTimer, veilTimer, creepTimer, giveUpTimer; var progress = 0; var running = false; function build() { if ( bar ) { return; } bar = document.createElement( 'div' ); bar.className = 'kuke-load-bar'; veil = document.createElement( 'div' ); veil.className = 'kuke-load'; veil.setAttribute( 'role', 'status' ); veil.setAttribute( 'aria-live', 'polite' ); veil.innerHTML = '<div class="kuke-load__panel">' + '<div class="kuke-load__mark">' + '<div class="kuke-load__ring"></div>' + '<img class="kuke-load__logo" src="' + LOGO + '" alt="">' + '</div>' + '<p class="kuke-load__text">正在加载' + '<span class="kuke-load__dots"><i></i><i></i><i></i></span>' + '</p>' + '</div>'; document.body.appendChild( bar ); document.body.appendChild( veil ); } function setProgress( v ) { progress = v; bar.style.width = v + '%'; } function creep() { // 无法预知真实进度,用递减步长向 92% 逼近,永不自行走满。 creepTimer = setTimeout( function () { if ( !running ) { return; } setProgress( progress + ( 92 - progress ) * 0.12 ); creep(); }, 260 ); } function start() { if ( running ) { return; } running = true; barTimer = setTimeout( function () { setProgress( 0 ); bar.classList.add( 'is-on' ); // 让 width:0 先落一帧,否则过渡不会触发。 requestAnimationFrame( function () { setProgress( 28 ); creep(); } ); }, BAR_DELAY ); veilTimer = setTimeout( function () { veil.classList.add( 'is-on' ); // 留给下一页的接力标记:浏览器随时会把本页冻结成静态画面, // 新页面读到它就在 <head> 阶段立刻铺上同一层遮罩,衔接无缝。 try { sessionStorage.setItem( RELAY_KEY, '1' ); } catch ( err ) {} }, VEIL_DELAY ); giveUpTimer = setTimeout( stop, GIVE_UP ); } function stop() { running = false; clearTimeout( barTimer ); clearTimeout( veilTimer ); clearTimeout( creepTimer ); clearTimeout( giveUpTimer ); // 跳转没走成(被取消、或按了返回),标记必须撤掉, // 否则下次进站会凭空接力出一层遮罩。 try { sessionStorage.removeItem( RELAY_KEY ); } catch ( err ) {} if ( !bar ) { return; } veil.classList.remove( 'is-on' ); if ( bar.classList.contains( 'is-on' ) ) { setProgress( 100 ); setTimeout( function () { if ( running ) { return; } bar.classList.remove( 'is-on' ); setTimeout( function () { if ( !running ) { setProgress( 0 ); } }, 220 ); }, 180 ); } else { setProgress( 0 ); } } // 判断一次跳转是否值得加动画:必须是本站、会真正换页的整页导航。 function shouldAnimate( url ) { if ( !url || url.origin !== location.origin ) { return false; } // 目标就是当前页 —— 页内锚点(含 href="#"、跳到内容、回到顶部),不换页。 if ( url.pathname === location.pathname && url.search === location.search ) { return false; } return true; } function onClick( e ) { if ( e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey ) { return; } var a = e.target.closest ? e.target.closest( 'a[href]' ) : null; if ( !a || a.hasAttribute( 'download' ) ) { return; } if ( a.target && a.target !== '' && a.target !== '_self' ) { return; } // 站内导航才有意义;协议不是 http(s) 的(mailto: / javascript: 等)跳过。 if ( a.protocol !== 'http:' && a.protocol !== 'https:' ) { return; } // 由脚本接管的交互控件(弹窗、抽屉、折叠等)不是跳转。 if ( a.getAttribute( 'role' ) === 'button' || a.closest( '.kuke-auth' ) ) { return; } var url; try { url = new URL( a.href ); } catch ( err ) { return; } if ( shouldAnimate( url ) ) { start(); } } function onSubmit( e ) { if ( e.defaultPrevented ) { return; } var form = e.target; if ( !form || form.target === '_blank' || form.closest( '.kuke-auth' ) ) { return; } var url; try { url = new URL( form.action || location.href, location.href ); } catch ( err ) { return; } if ( url.origin === location.origin ) { start(); } } // 收掉上一页交接过来的那层遮罩。等两帧再淡出,确保新页面已经真的画出内容。 function dropRelay() { var html = document.documentElement; if ( !html.classList.contains( 'kuke-relay' ) ) { return; } requestAnimationFrame( function () { requestAnimationFrame( function () { html.classList.add( 'kuke-relay-off' ); setTimeout( function () { html.classList.remove( 'kuke-relay', 'kuke-relay-off' ); }, 240 ); } ); } ); } function init() { dropRelay(); // 提前建好节点:跳转发生时只切 class,不做 DOM 构建, // 也让遮罩的图片在空闲时就下载完,真要出现时不会白一下。 build(); // 注册在 bubble 阶段、且晚于本文件其它监听器, // 于是弹窗等调用过 preventDefault() 的交互能被 defaultPrevented 过滤掉。 document.addEventListener( 'click', onClick ); document.addEventListener( 'submit', onSubmit ); // 返回上一页(含 bfcache 复原)时必须收掉,否则旧遮罩会留在画面上。 window.addEventListener( 'pageshow', stop ); // 跳转被用户按 Esc 取消时也收掉。 document.addEventListener( 'keydown', function ( e ) { if ( e.key === 'Escape' && running ) { stop(); } } ); } if ( document.readyState === 'loading' ) { document.addEventListener( 'DOMContentLoaded', init ); } else { init(); } }() );
返回
MediaWiki:Common.js
。
查看“︁MediaWiki:Common.js”︁的源代码
MediaWiki界面页面