|
|
| 第1行: |
第1行: |
| /* 横版登录 / 注册弹窗。 | | /* 站点 JS。 |
| 表单在本地直接渲染(不再抓取 Special 页面的 HTML),提交走 Action API | | 登录 / 注册弹窗已迁移到 Kuke 扩展(extensions/Kuke/resources/auth.js), |
| clientlogin / createaccount,token 在页面加载时就后台预取,
| | 因为 Special:UserLogin 等受限页面不加载 MediaWiki:Common.js, |
| 因此弹窗打开即可用,没有等待。
| | 会话失效被服务端跳转过去时这里的脚本不会执行。 */ |
| 独立页面 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();
| |
| }
| |
| }() );
| |