LLWiki正在建设中,欢迎加入我们!
“MediaWiki:Gadget-inspect.js”的版本间差异
跳转到导航
跳转到搜索
小 |
|||
第1行: | 第1行: | ||
//<nowiki> |
|||
// 由ResourceLoader直接调用,不可使用ES6语法 |
// 由ResourceLoader直接调用,不可使用ES6语法 |
||
/** |
/** |
||
* @Function: 在页面内快速编辑和预览 |
|||
* @Author: [[User:Bhsd]] |
* @Author: [[User:Bhsd]] |
||
*/ |
*/ |
||
第7行: | 第9行: | ||
const id = mw.config.get( 'wgArticleId' ), |
const id = mw.config.get( 'wgArticleId' ), |
||
curRevid = mw.config.get( 'wgCurRevisionId' ), |
curRevid = mw.config.get( 'wgCurRevisionId' ), |
||
script = mw.config.get('wgScript'), |
|||
page = mw.config.get( 'wgPageName' ), |
page = mw.config.get( 'wgPageName' ), |
||
lang = mw.config.get( 'wgUserVariant' ), |
|||
gadgets = mw.gadgets || {}, |
gadgets = mw.gadgets || {}, |
||
inspect = gadgets.inspect || {}, |
inspect = gadgets.inspect || {}, |
||
rule = inspect.rule |
rule = inspect.rule; |
||
// 不是模板、不是重定向、页面已存在、阅读模式、Wikitext、最新版本 |
|||
if (id > 0 && (![10, 274].includes( mw.config.get( 'wgNamespaceNumber' ) ) || page.endsWith( '/doc' )) && |
|||
if ((mw.config.get( 'wgNamespaceNumber' ) != 10 || page.endsWith( '/doc' )) && !mw.config.get('wgIsRedirect') && |
|||
id > 0 && mw.config.get('wgAction') == 'view' && mw.config.get( 'wgPageContentModel' ) == 'wikitext' && |
|||
mw.config.get( 'wgRevisionId' ) == curRevid && rule === undefined ? true : rule) { |
|||
// 先提交Ajax请求,这里手动设置cache: true |
|||
const api = new mw.Api(), |
const api = new mw.Api(), |
||
getJSON = $.ajax({ dataType: 'json', cache: true, |
getJSON = $.ajax({ dataType: 'json', cache: true, |
||
url: |
url: '/zh?title=mediawiki:gadget-CodeMirror.json&action=raw&ctype=application/json' }); |
||
// 生成通用的API请求 |
|||
mw.request = mw.request || mw.standardQuery(api); |
mw.request = mw.request || mw.standardQuery(api); |
||
// 标注<p>标签 |
|||
const css = mw.util.addCSS( '#mw-content-text .mw-parser-output p { border:1px dashed; }' + |
const css = mw.util.addCSS( '#mw-content-text .mw-parser-output p { border:1px dashed; }' + |
||
// 解决<ul>等元素文字不换行的问题 |
|||
'#mw-content-text .mw-parser-output { display:flow-root; overflow:hidden; word-wrap:break-word; }'), |
|||
'#mw-content-text .mw-parser-output { display:flow-root; overflow:hidden; word-wrap:break-word; }' ), |
|||
cssHide = mw.util.addCSS( '#inspector-btns { margin-bottom:0.5em; }' + |
cssHide = mw.util.addCSS( '#inspector-btns { margin-bottom:0.5em; }' + |
||
// 由于resizable,left, top, height必须加!important,width不可加!important |
|||
'#inspector { position:fixed; right:24px; left:unset !important; width:calc(50% - 7rem - 0.5px); }' ); |
|||
'#inspector { position:fixed; bottom:0; right:24px; left:unset !important; top:unset !important;' + |
|||
'height:unset !important; width:calc(50% - 7rem - 0.5px); }' ); |
|||
css.disabled = true; |
css.disabled = true; |
||
var dialog, actionP, actionD, text, editor, wrapper, nextid |
var dialog, actionP, actionD, text, editor, $wrapper, nextid, |
||
lang = mw.config.get( 'wgUserVariant' ); |
|||
const $content = $('#mw-content-text, #mw-imagepage-content').last(), |
|||
const states = ['loaded', 'loading', 'ready'], // 以上states表示开启对应的小工具 |
|||
original = $content.children( '.mw-parser-output' ), |
|||
isBackup = states.includes( mw.loader.getState( 'ext.gadget.contentBackup' ) ), |
|||
placeholder = $('<div>', {class: "mw-parser-output"}).css('display', 'none'), |
|||
backupObj = isBackup ? Object.fromEntries( mw.storage.getObject( 'LLWiki-contentBackup' ) || [] ) : [], |
|||
urlDisplay = $('<a>', {href: "#"}).click(function(e) { e.preventDefault(); }), |
|||
backup = isBackup ? function() { |
|||
warning = $('<div>', {html: [wgULS("您确定要还原为未编辑的状态吗?", "您確認要復原為未編輯的狀態嗎?"), '<br>', |
|||
backupObj[id] = [mw.now(), editor.getValue()]; |
|||
mw.storage.setObject( Object.entries(backupObj) ); |
|||
} : function() {}, |
|||
loadBackup = function() { |
|||
const backupContent = (backupObj[id] || [])[1]; |
|||
if (!backupContent) { return; } |
|||
mw.confirm( wgULS('要加载备份吗?', '要加載備份嗎?'), 'progressive' ).then(function(confirm) { |
|||
if (confirm) { editor.setValue( backupContent ); } |
|||
}); |
|||
}, |
|||
$content = $('#mw-content-text, #mw-imagepage-content').last(), |
|||
$original = $content.children( '.mw-parser-output' ), |
|||
$placeholder = $('<div>', {class: "mw-parser-output"}).css('display', 'none'), // 用于替换时保留原始数据 |
|||
$url = $('<a>'), // 桌面版CSS不必需href |
|||
$warning = $('<div>', {html: [wgULS("确定要还原为未编辑的状态吗?", "確認要復原為未編輯的狀態嗎?"), '<br>', |
|||
wgULS("建议您做好编辑内容备份。", "建議您做好編輯內容備份。")]}), |
wgULS("建议您做好编辑内容备份。", "建議您做好編輯內容備份。")]}), |
||
outer = $('<div>', {id: 'inspector', class: 'mw-ajax-loader'}).insertBefore( original ) |
$outer = $('<div>', {id: 'inspector', class: 'mw-ajax-loader'}).insertBefore( $original ) |
||
.on('contextmenu', '.cm-mw-template-name |
.on('contextmenu', '.cm-mw-template-name', function(e) { |
||
e.preventDefault(); |
e.preventDefault(); |
||
const |
const template = '模板:' + $(this).text(); |
||
var |
var label; |
||
// 这里增加一次确认界面是因为部分浏览器不允许JS直接打开新标签页。 |
|||
if (ele.hasClass( 'cm-mw-template-name' )) { url = '模板:' + url; } |
|||
else if (ele.next( '.cm-mw-link' ).text() == '#') { |
|||
url += '#' + ele.next().next( '.cm-mw-link-tosection' ).text(); |
|||
} |
|||
urlDisplay.text( url ); |
|||
if (!mw.windowManager) { |
|||
mw.windowManager = new OO.ui.WindowManager(); |
|||
$('body').append( mw.windowManager.$element ); |
|||
} |
|||
if (!dialog) { |
if (!dialog) { |
||
dialog = new OO.ui.MessageDialog(); |
|||
actionP = new OO.ui.ActionWidget({label: '是', target: '_blank', flags: 'progressive'}); |
actionP = new OO.ui.ActionWidget({label: '是', target: '_blank', flags: 'progressive'}); |
||
actionD = new OO.ui.ActionWidget({label: '否', flags: 'destructive'}); |
actionD = new OO.ui.ActionWidget({label: '否', flags: 'destructive'}); |
||
label = [wgULS('要在新标签页打开', '要在新標籤頁打開'), $url, wgULS('吗?', '嗎?')]; |
|||
mw.windowManager.addWindows( [dialog] ); |
|||
dialog.message.$label.html( [wgULS('要在新标签页打开', '要在新標籤頁打開'), urlDisplay, wgULS('吗?', '嗎?')] ); |
|||
} |
} |
||
$url.text( template ); |
|||
actionP.setHref('/zh/' + template); |
|||
mw.windowManager.openWindow( dialog, {actions: [actionP, actionD]}).opening.then(function() { |
|||
dialog = mw.dialog(dialog, [actionP, actionD], label); |
|||
actionP.$button.off( 'click' ).click(function() { dialog.close(); }); |
|||
}); |
|||
}).resizable( {handles: 'w', minWidth: 350} ), |
}).resizable( {handles: 'w', minWidth: 350} ), |
||
btns = [new OO.ui.ButtonWidget({label: '提交', flags: ['primary', 'progressive']}).on('click', function() { |
btns = [new OO.ui.ButtonWidget({label: '提交', flags: ['primary', 'progressive']}).on('click', function() { |
||
backup(); |
|||
// 改变CSS样式表示提交中 |
|||
btns[0].setDisabled( true ); |
btns[0].setDisabled( true ); |
||
mw.safeEdit(api, curRevid, {pageid: id, text: editor.getValue(), |
mw.safeEdit(api, curRevid, {pageid: id, text: editor.getValue(), |
||
summary: wgULS('使用页面/文本对比查看器快速编辑', '使用頁面/文本對比察看器快速編輯')}).then(function() { |
summary: wgULS('使用页面/文本对比查看器快速编辑', '使用頁面/文本對比察看器快速編輯')}).then(function() { |
||
location.reload(); |
|||
}, function(reason) { if (reason != 'editConflict') { btns[0].setDisabled( false ); } |
}, function(reason) { if (reason != 'editConflict') { btns[0].setDisabled( false ); } }); // 编辑冲突需刷新页面 |
||
}); |
|||
}), new OO.ui.ButtonWidget({label: wgULS('预览', '預覽')}).on('click', function() { |
}), new OO.ui.ButtonWidget({label: wgULS('预览', '預覽')}).on('click', function() { |
||
backup(); |
|||
// 改变CSS样式表示预览中 |
|||
btns[1].setDisabled( true ); |
btns[1].setDisabled( true ); |
||
console.log('API request: 请求预览'); |
console.log('API request: 请求预览'); |
||
第72行: | 第86行: | ||
.then(function(html) { |
.then(function(html) { |
||
console.log('End API request: 已生成预览,用时 ' + (mw.now() - now) + ' ms'); |
console.log('End API request: 已生成预览,用时 ' + (mw.now() - now) + ' ms'); |
||
if ($.contains( $content[0], original[0] )) { original.after( placeholder ).detach(); } |
if ($.contains( $content[0], $original[0] )) { $original.after( $placeholder ).detach(); } |
||
$content.children( '.mw-parser-output' ).replaceWith( html ); |
$content.children( '.mw-parser-output' ).replaceWith( html ); |
||
if (mw.resizeLyrics) { mw.resizeLyrics(); } |
if (mw.resizeLyrics) { mw.resizeLyrics(); } |
||
mw.hook( 'wikipage.content' ).fire($content); |
mw.hook( 'wikipage.content' ).fire($content); |
||
}, function(reason) { mw.apiFailure(reason, wgULS('预览', '預覽')); } |
}, function(reason) { mw.apiFailure(reason, wgULS('预览', '預覽')); }) // mw.apiFailure不会抛出错误 |
||
btns[1].setDisabled( false ); |
.then(function() { btns[1].setDisabled( false ); }); |
||
}); |
|||
}), new OO.ui.ButtonWidget({label: wgULS('还原', '復原'), flags: 'destructive'}).on('click', function() { |
}), new OO.ui.ButtonWidget({label: wgULS('还原', '復原'), flags: 'destructive'}).on('click', function() { |
||
mw.confirm($warning, ['primary', 'destructive']).then(function(confirm) { |
|||
{label: "是", flags: ['primary', 'destructive'], action: 'accept'}]}).then(function(confirm) { |
|||
if (!confirm) { return; } |
if (!confirm) { return; } |
||
editor.setValue( text ); |
editor.setValue( text ); |
||
$content.children( '.mw-parser-output' ).replaceWith( original ); |
$content.children( '.mw-parser-output' ).replaceWith( $original ); |
||
if (mw.resizeLyrics) { mw.resizeLyrics(); } |
if (mw.resizeLyrics) { mw.resizeLyrics(); } |
||
}); |
}); |
||
第90行: | 第102行: | ||
css.disabled = !css.disabled; |
css.disabled = !css.disabled; |
||
cssHide.disabled = !cssHide.disabled; |
cssHide.disabled = !cssHide.disabled; |
||
wrapper.toggle(); |
$wrapper.toggle(); |
||
btns[3].setLabel( css.disabled ? wgULS('显示', '顯示') : wgULS('隐藏', '隱藏')); |
btns[3].setLabel( css.disabled ? wgULS('显示', '顯示') : wgULS('隐藏', '隱藏')); |
||
btns[2].setDisabled( css.disabled ); // 折叠时无法更新textarea |
|||
if (mw.resizeLyrics) { mw.resizeLyrics(); } |
if (mw.resizeLyrics) { mw.resizeLyrics(); } |
||
}), new OO.ui.ButtonWidget({flags: 'progressive', icon: 'next'})], |
}), new OO.ui.ButtonWidget({flags: 'progressive', icon: 'next'})], |
||
list = mw.storage.getObject( 'inspect-category' ) |
list = mw.storage.getObject( 'inspect-category' ), |
||
initList = function(pages) { |
|||
const currentIndex = pages[0] == id ? 1 : 0; |
|||
mw.storage.setObject( 'inspect-category', [pages, currentIndex] ); |
|||
btns[4].setHref( '/?redirect=no&curid=' + pages[ currentIndex ] ).setDisabled( false ); |
|||
}; |
|||
if (!list) { nextid = id + 1; } |
if (!list) { nextid = id + 1; } |
||
else { |
else { |
||
第103行: | 第121行: | ||
nextid = list[0][ list[1] ]; |
nextid = list[0][ list[1] ]; |
||
} |
} |
||
if (isBackup) { btns[2].$element.contextmenu( loadBackup ); } |
|||
btns[4].setHref( '/?redirect=no&curid=' + nextid ).setDisabled( !nextid ).$element.contextmenu(function(e) { |
btns[4].setHref( '/?redirect=no&curid=' + nextid ).setDisabled( !nextid ).$element.contextmenu(function(e) { |
||
e.preventDefault(); |
e.preventDefault(); |
||
OO.ui.prompt( wgULS('请输入分类名或名字空间编号:', '請輸入分類名或名字空間編號:') ).then(function(cat) { |
OO.ui.prompt( wgULS('请输入分类名或名字空间编号:', '請輸入分類名或名字空間編號:') ).then(function(cat) { |
||
if (cat === null) { return; } |
if (cat === null) { return; } |
||
// 输入空白则清除分类 |
|||
if (cat === '') { |
if (cat === '') { |
||
mw.storage.remove( 'inspect-category' ); |
mw.storage.remove( 'inspect-category' ); |
||
第112行: | 第132行: | ||
return; |
return; |
||
} |
} |
||
// 输入数字对应名字空间 |
|||
if (!isNaN(cat)) { |
if (!isNaN(cat)) { |
||
if (!Object.keys( mw.config.get( 'wgFormattedNamespaces' ) ).includes(cat)) { |
if (!Object.keys( mw.config.get( 'wgFormattedNamespaces' ) ).includes(cat)) { |
||
第117行: | 第138行: | ||
return; |
return; |
||
} |
} |
||
mw.timedQuery(api, {list: 'allpages', apnamespace: cat, apfilterredir: 'nonredirects', aplimit: 'max'}, |
|||
console.log('API request: 查询该名字空间的页面列表'); |
|||
wgULS('该名字空间的页面列表', '該名字空間的頁面列表')).then(function(ap) { |
|||
now = mw.now(); |
|||
api.get({action: 'query', list: 'allpages', apnamespace: cat, apfilterredir: 'nonredirects', |
|||
aplimit: 'max', formatversion: 2}).then(function(ap) { |
|||
console.log('End API request: 已获得该名字空间的页面列表,用时 ' + (mw.now() - now) + ' ms'); |
|||
const pages = ap.query.allpages.map(function(ele) { return ele.pageid; }) |
const pages = ap.query.allpages.map(function(ele) { return ele.pageid; }) |
||
.sort(function(a, b) { return a < b; }); |
.sort(function(a, b) { return a < b; }); // 由新到旧排列 |
||
if (pages.length === 0) { |
if (pages.length === 0) { |
||
mw.notify( wgULS('该名字空间沒有非重定向的页面!', '該名字空間沒有非重定向的頁面'), {type: 'warn'} ); |
mw.notify( wgULS('该名字空间沒有非重定向的页面!', '該名字空間沒有非重定向的頁面!'), {type: 'warn'} ); |
||
return; |
return; |
||
} |
} |
||
initList(pages); |
|||
}, function() {}); // mw.timedQuery已经通知过错误信息,这里只要一个空函数处理reject,下同 |
|||
mw.storage.setObject( 'inspect-category', [pages, currentIndex] ); |
|||
btns[4].setHref( '/?redirect=no&curid=' + pages[ currentIndex ] ).setDisabled( false ); |
|||
}, function(reason) { mw.apiFailure(reason, wgULS('该名字空间的页面列表', '該名字空間的頁面列表')); }); |
|||
return; |
return; |
||
} |
} |
||
if (!/^( |
if (!/^(category|分[类類]):/i.test( cat )) { cat = 'Category:' + cat; } |
||
// cmtitle参数不可自动转换,所以需要先获得转换后的正确标题 |
|||
console.log('API request: 查询标准的页面名'); |
|||
mw.timedQuery(api, {titles: cat, converttitles: 1}, wgULS('标准页面名称', '標準頁面名稱')) .then(function(r) { |
|||
var now = mw.now(); |
|||
const target = r.query.pages[0]; |
|||
api.get({action: 'query', titles: cat, converttitles: 1, formatversion: 2}).then(function(response) { |
|||
const target = response.query.pages[0]; |
|||
if (target.missing) { |
if (target.missing) { |
||
mw.notify(wgULS('错误的分类名!', '錯誤的分類名!'), {type: 'error'}); |
mw.notify(wgULS('错误的分类名!', '錯誤的分類名!'), {type: 'error'}); |
||
return; |
return; |
||
} |
} |
||
mw.timedQuery(api, {list: 'categorymembers', cmtitle: target.title, cmprop: 'ids', cmlimit: 'max', |
|||
console.log('End API request: 已获得标准的页面名,用时 ' + (mw.now() - now) + ' ms'); |
|||
cmsort: 'timestamp', cmdir: 'older'}, wgULS('分类下的页面列表', '分類下的頁面列表')).then(function(cm) { |
|||
now = mw.now(); |
|||
api.get({action: 'query', list: 'categorymembers', cmtitle: target.title, cmprop: 'ids', |
|||
cmlimit: 'max', cmsort: 'timestamp', cmdir: 'older', formatversion: 2}).then(function(cm) { |
|||
console.log('End API request: 已获得分类下的页面列表,用时 ' + (mw.now() - now) + ' ms'); |
|||
const pages = cm.query.categorymembers.map(function(ele) { return ele.pageid; }); |
const pages = cm.query.categorymembers.map(function(ele) { return ele.pageid; }); |
||
if (pages.length === 0) { |
if (pages.length === 0) { |
||
第154行: | 第165行: | ||
return; |
return; |
||
} |
} |
||
initList(pages); |
|||
}, function() {}); |
|||
}, function() {}); |
|||
btns[4].setHref( '/?redirect=no&curid=' + pages[ currentIndex ] ).setDisabled( false ); |
|||
}, function(reason) { mw.apiFailure(reason, wgULS('该分类下的页面列表', '該分類下的頁面列表')); }); |
|||
}, function(reason) { mw.apiFailure(reason, wgULS('标准页面名称', '標準頁面名稱')); }); |
|||
}); |
}); |
||
}); |
}); |
||
if (states.includes( mw.loader.getState( 'ext.gadget.PreviewWithVariant' ) )) { |
|||
const options = [{label: "大陆简体", data: "zh-cn"}, {label: "香港繁體", data: "zh-hk"}, |
|||
{label: "澳門繁體", data: "zh-mo"}, {label: "马来西亚简体", data: "zh-my"}, |
|||
{label: "新加坡简体", data: "zh-sg"}, {label: "臺灣繁體", data: "zh-tw"}], |
|||
select = new OO.ui.DropdownInputWidget({classes: ['inspector-variant'], options: options, value: lang}) |
|||
.on('change', function() { |
|||
lang = select.getValue(); |
|||
}); |
|||
select.$element.prependTo( btns[1].$element ); |
|||
btns[1].$element.contextmenu(function(e) { |
|||
e.preventDefault(); |
|||
select.$element.find( '.oo-ui-menuSelectWidget' ).removeClass( 'oo-ui-element-hidden' ); |
|||
}).children('a').blur(function() { |
|||
select.$element.find( '.oo-ui-menuSelectWidget' ).addClass( 'oo-ui-element-hidden' ); |
|||
}); |
|||
} |
|||
$.when(getJSON, mw.request).then(function(config, data) { |
$.when(getJSON, mw.request).then(function(config, data) { |
||
console.log('End API request: 已获得页面Wikitext'); |
console.log('End API request: 已获得页面Wikitext'); |
||
text = data[0].query.pages[0].revisions[0].content; |
text = data[0].query.pages[0].revisions[0].content; |
||
outer.removeClass( 'mw-ajax-loader' ); |
$outer.removeClass( 'mw-ajax-loader' ); |
||
editor = new CodeMirror(outer[0], {value: text, mode: 'text/mediawiki', mwConfig: config[0], |
editor = new CodeMirror($outer[0], {value: text, mode: 'text/mediawiki', mwConfig: config[0], |
||
lineWrapping: true, lineNumbers: true}); |
lineWrapping: true, lineNumbers: true}); |
||
wrapper = $( editor.getWrapperElement() ).toggle(); |
$wrapper = $( editor.getWrapperElement() ).toggle(); |
||
$('<div>', {id: 'inspector-btns', html: btns.map(function(ele) { return ele.$element; })}).appendTo( outer ); |
$('<div>', {id: 'inspector-btns', html: btns.map(function(ele) { return ele.$element; })}).appendTo( $outer ); |
||
// 处理页面上方的差异 |
|||
$('.diff').click(function(e) { |
$('.diff').click(function(e) { |
||
const row = $( e.target ).closest('tr'), |
const row = $( e.target ).closest('tr'), |
||
第174行: | 第200行: | ||
rowLineno = isLineno ? row : row.prevAll( ':has(.diff-lineno)' ).first(), |
rowLineno = isLineno ? row : row.prevAll( ':has(.diff-lineno)' ).first(), |
||
n = parseInt( rowLineno.children().last().text().match(/\d+/) ) + row.index() - rowLineno.index() |
n = parseInt( rowLineno.children().last().text().match(/\d+/) ) + row.index() - rowLineno.index() |
||
- 2 + isLineno, |
- 2 + isLineno, // 使用parseInt规避可能的程序错误,注意CodeMirror的行号从0开始 |
||
nLine = editor.lastLine(); |
nLine = editor.lastLine(); |
||
if (nLine >= n) { |
if (nLine >= n) { |
||
第184行: | 第210行: | ||
}, function(reason) { mw.apiFailure(reason, 'CodeMirror' + wgULS('设置或页面', '設置或頁面') + 'Wikitext'); }); |
}, function(reason) { mw.apiFailure(reason, 'CodeMirror' + wgULS('设置或页面', '設置或頁面') + 'Wikitext'); }); |
||
} |
} |
||
//</nowiki> |
|||
// [[category:维护工具]] [[category:桌面版小工具]] [[category:需要自确用户权限的小工具]] |
|||
// {{DEFAULTSORT:inspect.js}} |
2021年1月7日 (四) 08:28的版本
//<nowiki> // 由ResourceLoader直接调用,不可使用ES6语法 /** * @Function: 在页面内快速编辑和预览 * @Author: [[User:Bhsd]] */ "use strict"; /*global mw, $, OO, CodeMirror, wgULS*/ const id = mw.config.get( 'wgArticleId' ), curRevid = mw.config.get( 'wgCurRevisionId' ), page = mw.config.get( 'wgPageName' ), gadgets = mw.gadgets || {}, inspect = gadgets.inspect || {}, rule = inspect.rule; // 不是模板、不是重定向、页面已存在、阅读模式、Wikitext、最新版本 if ((mw.config.get( 'wgNamespaceNumber' ) != 10 || page.endsWith( '/doc' )) && !mw.config.get('wgIsRedirect') && id > 0 && mw.config.get('wgAction') == 'view' && mw.config.get( 'wgPageContentModel' ) == 'wikitext' && mw.config.get( 'wgRevisionId' ) == curRevid && rule === undefined ? true : rule) { // 先提交Ajax请求,这里手动设置cache: true const api = new mw.Api(), getJSON = $.ajax({ dataType: 'json', cache: true, url: '/zh?title=mediawiki:gadget-CodeMirror.json&action=raw&ctype=application/json' }); // 生成通用的API请求 mw.request = mw.request || mw.standardQuery(api); // 标注<p>标签 const css = mw.util.addCSS( '#mw-content-text .mw-parser-output p { border:1px dashed; }' + // 解决<ul>等元素文字不换行的问题 '#mw-content-text .mw-parser-output { display:flow-root; overflow:hidden; word-wrap:break-word; }' ), cssHide = mw.util.addCSS( '#inspector-btns { margin-bottom:0.5em; }' + // 由于resizable,left, top, height必须加!important,width不可加!important '#inspector { position:fixed; bottom:0; right:24px; left:unset !important; top:unset !important;' + 'height:unset !important; width:calc(50% - 7rem - 0.5px); }' ); css.disabled = true; var dialog, actionP, actionD, text, editor, $wrapper, nextid, lang = mw.config.get( 'wgUserVariant' ); const states = ['loaded', 'loading', 'ready'], // 以上states表示开启对应的小工具 isBackup = states.includes( mw.loader.getState( 'ext.gadget.contentBackup' ) ), backupObj = isBackup ? Object.fromEntries( mw.storage.getObject( 'LLWiki-contentBackup' ) || [] ) : [], backup = isBackup ? function() { backupObj[id] = [mw.now(), editor.getValue()]; mw.storage.setObject( Object.entries(backupObj) ); } : function() {}, loadBackup = function() { const backupContent = (backupObj[id] || [])[1]; if (!backupContent) { return; } mw.confirm( wgULS('要加载备份吗?', '要加載備份嗎?'), 'progressive' ).then(function(confirm) { if (confirm) { editor.setValue( backupContent ); } }); }, $content = $('#mw-content-text, #mw-imagepage-content').last(), $original = $content.children( '.mw-parser-output' ), $placeholder = $('<div>', {class: "mw-parser-output"}).css('display', 'none'), // 用于替换时保留原始数据 $url = $('<a>'), // 桌面版CSS不必需href $warning = $('<div>', {html: [wgULS("确定要还原为未编辑的状态吗?", "確認要復原為未編輯的狀態嗎?"), '<br>', wgULS("建议您做好编辑内容备份。", "建議您做好編輯內容備份。")]}), $outer = $('<div>', {id: 'inspector', class: 'mw-ajax-loader'}).insertBefore( $original ) .on('contextmenu', '.cm-mw-template-name', function(e) { e.preventDefault(); const template = '模板:' + $(this).text(); var label; // 这里增加一次确认界面是因为部分浏览器不允许JS直接打开新标签页。 if (!dialog) { actionP = new OO.ui.ActionWidget({label: '是', target: '_blank', flags: 'progressive'}); actionD = new OO.ui.ActionWidget({label: '否', flags: 'destructive'}); label = [wgULS('要在新标签页打开', '要在新標籤頁打開'), $url, wgULS('吗?', '嗎?')]; } $url.text( template ); actionP.setHref('/zh/' + template); dialog = mw.dialog(dialog, [actionP, actionD], label); }).resizable( {handles: 'w', minWidth: 350} ), btns = [new OO.ui.ButtonWidget({label: '提交', flags: ['primary', 'progressive']}).on('click', function() { backup(); // 改变CSS样式表示提交中 btns[0].setDisabled( true ); mw.safeEdit(api, curRevid, {pageid: id, text: editor.getValue(), summary: wgULS('使用页面/文本对比查看器快速编辑', '使用頁面/文本對比察看器快速編輯')}).then(function() { location.reload(); }, function(reason) { if (reason != 'editConflict') { btns[0].setDisabled( false ); } }); // 编辑冲突需刷新页面 }), new OO.ui.ButtonWidget({label: wgULS('预览', '預覽')}).on('click', function() { backup(); // 改变CSS样式表示预览中 btns[1].setDisabled( true ); console.log('API request: 请求预览'); const now = mw.now(); api.parse( editor.getValue(), {title: page, uselang: lang, disablelimitreport: 1, disableeditsection: 1} ) .then(function(html) { console.log('End API request: 已生成预览,用时 ' + (mw.now() - now) + ' ms'); if ($.contains( $content[0], $original[0] )) { $original.after( $placeholder ).detach(); } $content.children( '.mw-parser-output' ).replaceWith( html ); if (mw.resizeLyrics) { mw.resizeLyrics(); } mw.hook( 'wikipage.content' ).fire($content); }, function(reason) { mw.apiFailure(reason, wgULS('预览', '預覽')); }) // mw.apiFailure不会抛出错误 .then(function() { btns[1].setDisabled( false ); }); }), new OO.ui.ButtonWidget({label: wgULS('还原', '復原'), flags: 'destructive'}).on('click', function() { mw.confirm($warning, ['primary', 'destructive']).then(function(confirm) { if (!confirm) { return; } editor.setValue( text ); $content.children( '.mw-parser-output' ).replaceWith( $original ); if (mw.resizeLyrics) { mw.resizeLyrics(); } }); }), new OO.ui.ButtonWidget({label: wgULS('显示', '顯示')}).on('click', function() { css.disabled = !css.disabled; cssHide.disabled = !cssHide.disabled; $wrapper.toggle(); btns[3].setLabel( css.disabled ? wgULS('显示', '顯示') : wgULS('隐藏', '隱藏')); btns[2].setDisabled( css.disabled ); // 折叠时无法更新textarea if (mw.resizeLyrics) { mw.resizeLyrics(); } }), new OO.ui.ButtonWidget({flags: 'progressive', icon: 'next'})], list = mw.storage.getObject( 'inspect-category' ), initList = function(pages) { const currentIndex = pages[0] == id ? 1 : 0; mw.storage.setObject( 'inspect-category', [pages, currentIndex] ); btns[4].setHref( '/?redirect=no&curid=' + pages[ currentIndex ] ).setDisabled( false ); }; if (!list) { nextid = id + 1; } else { if (list[0][ list[1] ] == id) { list[1]++; mw.storage.setObject( 'inspect-category', list ); } nextid = list[0][ list[1] ]; } if (isBackup) { btns[2].$element.contextmenu( loadBackup ); } btns[4].setHref( '/?redirect=no&curid=' + nextid ).setDisabled( !nextid ).$element.contextmenu(function(e) { e.preventDefault(); OO.ui.prompt( wgULS('请输入分类名或名字空间编号:', '請輸入分類名或名字空間編號:') ).then(function(cat) { if (cat === null) { return; } // 输入空白则清除分类 if (cat === '') { mw.storage.remove( 'inspect-category' ); btns[4].setHref( '/?redirect=no&curid=' + (id + 1) ).setDisabled( false ); return; } // 输入数字对应名字空间 if (!isNaN(cat)) { if (!Object.keys( mw.config.get( 'wgFormattedNamespaces' ) ).includes(cat)) { mw.notify(wgULS('错误的名字空间编号!', '錯誤的名字空間編號!'), {type: 'error'}); return; } mw.timedQuery(api, {list: 'allpages', apnamespace: cat, apfilterredir: 'nonredirects', aplimit: 'max'}, wgULS('该名字空间的页面列表', '該名字空間的頁面列表')).then(function(ap) { const pages = ap.query.allpages.map(function(ele) { return ele.pageid; }) .sort(function(a, b) { return a < b; }); // 由新到旧排列 if (pages.length === 0) { mw.notify( wgULS('该名字空间沒有非重定向的页面!', '該名字空間沒有非重定向的頁面!'), {type: 'warn'} ); return; } initList(pages); }, function() {}); // mw.timedQuery已经通知过错误信息,这里只要一个空函数处理reject,下同 return; } if (!/^(category|分[类類]):/i.test( cat )) { cat = 'Category:' + cat; } // cmtitle参数不可自动转换,所以需要先获得转换后的正确标题 mw.timedQuery(api, {titles: cat, converttitles: 1}, wgULS('标准页面名称', '標準頁面名稱')) .then(function(r) { const target = r.query.pages[0]; if (target.missing) { mw.notify(wgULS('错误的分类名!', '錯誤的分類名!'), {type: 'error'}); return; } mw.timedQuery(api, {list: 'categorymembers', cmtitle: target.title, cmprop: 'ids', cmlimit: 'max', cmsort: 'timestamp', cmdir: 'older'}, wgULS('分类下的页面列表', '分類下的頁面列表')).then(function(cm) { const pages = cm.query.categorymembers.map(function(ele) { return ele.pageid; }); if (pages.length === 0) { mw.notify( wgULS('该分类下无页面!', '該分類下無頁面!'), {type: 'warn'} ); return; } initList(pages); }, function() {}); }, function() {}); }); }); if (states.includes( mw.loader.getState( 'ext.gadget.PreviewWithVariant' ) )) { const options = [{label: "大陆简体", data: "zh-cn"}, {label: "香港繁體", data: "zh-hk"}, {label: "澳門繁體", data: "zh-mo"}, {label: "马来西亚简体", data: "zh-my"}, {label: "新加坡简体", data: "zh-sg"}, {label: "臺灣繁體", data: "zh-tw"}], select = new OO.ui.DropdownInputWidget({classes: ['inspector-variant'], options: options, value: lang}) .on('change', function() { lang = select.getValue(); }); select.$element.prependTo( btns[1].$element ); btns[1].$element.contextmenu(function(e) { e.preventDefault(); select.$element.find( '.oo-ui-menuSelectWidget' ).removeClass( 'oo-ui-element-hidden' ); }).children('a').blur(function() { select.$element.find( '.oo-ui-menuSelectWidget' ).addClass( 'oo-ui-element-hidden' ); }); } $.when(getJSON, mw.request).then(function(config, data) { console.log('End API request: 已获得页面Wikitext'); text = data[0].query.pages[0].revisions[0].content; $outer.removeClass( 'mw-ajax-loader' ); editor = new CodeMirror($outer[0], {value: text, mode: 'text/mediawiki', mwConfig: config[0], lineWrapping: true, lineNumbers: true}); $wrapper = $( editor.getWrapperElement() ).toggle(); $('<div>', {id: 'inspector-btns', html: btns.map(function(ele) { return ele.$element; })}).appendTo( $outer ); // 处理页面上方的差异 $('.diff').click(function(e) { const row = $( e.target ).closest('tr'), isLineno = row.children( '.diff-lineno' ).length ? 1 : 0, rowLineno = isLineno ? row : row.prevAll( ':has(.diff-lineno)' ).first(), n = parseInt( rowLineno.children().last().text().match(/\d+/) ) + row.index() - rowLineno.index() - 2 + isLineno, // 使用parseInt规避可能的程序错误,注意CodeMirror的行号从0开始 nLine = editor.lastLine(); if (nLine >= n) { editor.scrollIntoView(nLine); editor.scrollIntoView(n); } else { mw.notify( wgULS('当前不存在该行!', '當前不存在該行!'), {type: 'warn'} ); } }); }, function(reason) { mw.apiFailure(reason, 'CodeMirror' + wgULS('设置或页面', '設置或頁面') + 'Wikitext'); }); } //</nowiki> // [[category:维护工具]] [[category:桌面版小工具]] [[category:需要自确用户权限的小工具]] // {{DEFAULTSORT:inspect.js}}