查看“MediaWiki:Gadget-MiniEdit.js”的源代码
←
MediaWiki:Gadget-MiniEdit.js
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
此页面为本wiki上的软件提供界面文本,并受到保护以防止滥用。 如欲修改所有wiki的翻译,请访问
translatewiki.net
上的MediaWiki本地化项目。
您无权编辑此JavaScript页面,因为编辑此页面可能会影响所有访问者。
您可以查看和复制此页面的源代码。
/** * MiniEdit enables quick and simple edits without leaving the page * Documentation: https://www.mediawiki.org/wiki/MiniEdit * License: GNU General Public License 3 or later (http://www.gnu.org/licenses/gpl-3.0.html) * Author: Felipe Schenone (User:Sophivorus) */ var MiniEdit = { /**d * Will hold the wikitext of the current page */ pageWikitext: '', /** * Initialization script */ init: function () { // EDIT: pre-register messages to eliminate access to wikimedia sites mw.messages.set( { "miniedit-form-publish": "发布", "miniedit-form-cancel": "取消", "miniedit-form-summary": "编辑摘要", "miniedit-form-minor": "小编辑", "miniedit-form-saving": "正在保存...", "miniedit-summary-edit": "编辑段落", "miniedit-summary-delete": "删除段落" }); // Only init when viewing var action = mw.config.get( 'wgAction' ); if ( action !== 'view' ) { return; } // Only init in useful namespaces var namespaces = [ 0, 2, 4, 12, 14 ]; var namespace = mw.config.get( 'wgNamespaceNumber' ); var talk = namespace % 2 === 1; // Talk pages always have odd namespaces if ( !namespaces.includes( namespace ) && !talk ) { return; } // Only init in wikitext pages var model = mw.config.get( 'wgPageContentModel' ); if ( model !== 'wikitext' ) { return; } // Select only paragraphs that are direct children // to reduce the chances of matching paragraphs that come from templates var selector = '#mw-content-text > .mw-parser-output > p'; if ( mw.config.get( 'skin' ) === 'minerva' ) { selector = '#mw-content-text > .mw-parser-output > section > p'; } $( selector ).each( MiniEdit.addEditButton ); }, /** * Add an edit button */ addEditButton: function () { var $paragraph = $( this ); // Skip empty paragraphs if ( !$paragraph.text().trim() ) { return; } // Make the edit button var path = '<path fill="currentColor" d="M16.77 8l1.94-2a1 1 0 0 0 0-1.41l-3.34-3.3a1 1 0 0 0-1.41 0L12 3.23zm-5.81-3.71L1 14.25V19h4.75l9.96-9.96-4.75-4.75z"></path>'; var icon = '<svg width="14" height="14" viewBox="0 0 20 20">' + path + '</svg>'; var $button = $( '<span class="miniedit-button noprint">' + icon + '</span>' ); $button.css( { 'color': '#a2a9b1', 'cursor': 'pointer' } ); $button.on( 'click', MiniEdit.onEditButtonClick ); // Only show the button when the user hovers over the paragraph // On mobile devices there's no hover event, so we just skip this part and show the button always if ( window.innerWidth > 800 ) { $button.hide(); $paragraph.on( 'mouseenter', function () { $button.show(); } ); $paragraph.on( 'mouseleave', function () { $button.hide(); } ); $button.on( 'mouseenter', function () { $( this ).css( 'color', '#202122' ); } ); $button.on( 'mouseleave', function () { $( this ).css( 'color', '#a2a9b1' ); } ); } // Add to the DOM $paragraph.append( ' ', $button ); }, /** * Handle a click on an edit button */ onEditButtonClick: function () { var $button = $( this ).closest( '.miniedit-button' ); var $paragraph = $button.parent(); // Save the original paragraph in case we need to restore it later // For some reason the hover events on the button are not getting cloned, so we remake the button var $original = $paragraph.clone( true ); $original.find( '.miniedit-button' ).remove(); MiniEdit.addEditButton.call( $original ); // If pageWikitext is set, it means that the dependencies were already loaded by a previous click if ( MiniEdit.pageWikitext ) { MiniEdit.addEditForm( $paragraph, $original ); return; } // If we reach this point, we need to load the dependencies // First, we replace the button for a loading spinner // to prevent further clicks and to signal the user that something's happening var $spinner = MiniEdit.getSpinner(); $button.replaceWith( $spinner ); // Then we load dependencies $.when( MiniEdit.getPageWikitext(), MiniEdit.getMessages( 'en' ) ).done( function () { // Note the special treatment of getMessages( pageLanguage ) // because it may fail if a translation doesn't exist yet // and because its success callback needs to run AFTER getMessages( 'en' ) var pageLanguage = mw.config.get( 'wgPageContentLanguage' ); MiniEdit.getMessages( pageLanguage ).always( function () { MiniEdit.addEditForm( $paragraph, $original ); } ); } ); }, /** * Add an edit form */ addEditForm: function ( $paragraph, $original ) { // If no relevant wikitext for the element is found, fallback to regular edit var wikitext = MiniEdit.getParagraphWikitext( $paragraph ); if ( !wikitext ) { var $section = MiniEdit.getSection( $paragraph ); var sectionNumber = $section ? 1 + $section.prevAll( ':header' ).length : 0; var editUrl = mw.util.getUrl( null, { action: 'edit', section: sectionNumber } ); window.location.href = editUrl; return; } // Make the form var wikitextInput = new OO.ui.MultilineTextInputWidget( { name: 'wikitext', value: wikitext, autofocus: true, autosize: true } ); var wikitextLayout = new OO.ui.HorizontalLayout( { items: [ wikitextInput ] } ); var summaryInput = new OO.ui.TextInputWidget( { name: 'summary', placeholder: mw.msg( 'miniedit-form-summary' ) } ); var summaryLayout = new OO.ui.HorizontalLayout( { items: [ summaryInput ] } ); // Anons can't mark edits as minor if ( !mw.user.isAnon() ) { var minorCheckbox = new OO.ui.CheckboxInputWidget( { name: 'minor' } ); var minorLayout = new OO.ui.FieldLayout( minorCheckbox, { label: mw.msg( 'miniedit-form-minor' ), align: 'inline' } ); summaryLayout.addItems( [ minorLayout ] ); summaryLayout.$element.find( '.oo-ui-fieldLayout' ).css( 'vertical-align', 'text-bottom' ); // Minor alignment fix } // Add the buttons var publishButton = new OO.ui.ButtonInputWidget( { label: mw.msg( 'miniedit-form-publish' ), flags: [ 'primary', 'progressive' ] } ); var cancelButton = new OO.ui.ButtonInputWidget( { label: mw.msg( 'miniedit-form-cancel' ), flags: 'destructive', framed: false } ); var formLayout = new OO.ui.FormLayout( { items: [ wikitextLayout, summaryLayout, publishButton, cancelButton ] } ); // CSS tweaks formLayout.$element.css( 'overflow', 'hidden' ); wikitextInput.$element.css( { 'font-family': 'monospace', 'max-width': '100%' } ); // Add to the DOM var $form = formLayout.$element; $paragraph.html( $form ); // Handle a submission publishButton.on( 'click', MiniEdit.onSubmit, [ $paragraph, $original, $form, wikitext, publishButton, cancelButton ] ); // Handle a cancel cancelButton.on( 'click', function () { $paragraph.replaceWith( $original ); } ); }, /** * Handle a submission */ onSubmit: function ( $paragraph, $original, $form, oldWikitext, publishButton, cancelButton ) { var newWikitext = $form.find( 'textarea[name="wikitext"]' ).val(); // If no changes were made, just restore the original element if ( oldWikitext === newWikitext ) { $paragraph.replaceWith( $original ); return; } // Disable the buttons to prevent further clicks and to signal the user that something's happening publishButton.setDisabled( true ); cancelButton.setDisabled( true ); // Get the rest of the form data var summary = $form.find( 'input[name="summary"]' ).val(); var minor = $form.find( 'input[name="minor"]' ).prop( 'checked' ); // Fix excessive line breaks newWikitext = newWikitext.trim(); newWikitext = newWikitext.replace( /\n\n\n+/g, '\n\n' ); // If the paragraph was deleted, remove also any trailing newlines if ( !newWikitext ) { oldWikitext = oldWikitext.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ); // Escape special characters oldWikitext = new RegExp( oldWikitext + '\n+' ); } MiniEdit.pageWikitext = MiniEdit.pageWikitext.replace( oldWikitext, newWikitext ); var params = { 'action': 'edit', 'title': mw.config.get( 'wgPageName' ), 'text': MiniEdit.pageWikitext, 'minor': minor, 'summary': MiniEdit.makeSummary( summary, $form, newWikitext ), 'tags': mw.config.get( 'miniedit-tag' ) }; new mw.Api().postWithEditToken( params ).done( function () { MiniEdit.onSuccess( $paragraph, newWikitext ); } ); }, /** * Callback on successful edits */ onSuccess: function ( $paragraph, newWikitext ) { if ( !newWikitext ) { $paragraph.remove(); return; } var params = { 'action': 'parse', 'title': mw.config.get( 'wgPageName' ), 'text': newWikitext, 'formatversion': 2, 'prop': 'text', 'disablelimitreport': true, }; new mw.Api().get( params ).done( function ( data ) { var text = data.parse.text; var $html = $( text ); var $paragraphs = $html.find( 'p' ); $paragraph.replaceWith( $paragraphs ); $paragraphs.each( MiniEdit.addEditButton ); } ); }, /** * Get the wikitext of the current page */ getPageWikitext: function () { var params = { 'page': mw.config.get( 'wgPageName' ), 'action': 'parse', 'prop': 'wikitext', 'formatversion': 2, }; return new mw.Api().get( params ).done( function ( data ) { MiniEdit.pageWikitext = data.parse.wikitext; } ); }, /** * ~~Get messages from the Wikimedia repository~~ * Changing this to a no-op since we pre-register messages */ getMessages: function ( language ) { return jQuery.Deferred().resolve(); }, /** * Helper method to get the relevant wikitext that corresponds to a given paragraph * * This is actually the heart of the tool * It's a heuristic method to try to find the relevant wikitext * that corresponds to the paragraph being edited * Since wikitext and HTML are different markups * the only place where they are the same is in plain text fragments * so we find the longest plain text fragment in the HTML * then we search for that same fragment in the wikitext * and return the entire line of wikitext containing that fragment * or null if anything goes wrong * * @param {jQuery object} jQuery object representing the DOM element being edited * @return {string|null} Wikitext of the paragraph being edited, or null if it can't be found */ getParagraphWikitext: function ( $paragraph ) { // The longest text node has the most chances of being unique var text = MiniEdit.getLongestText( $paragraph ); // Some paragraphs may not have text nodes at all if ( !text ) { return; } // Match all lines that contain the text text = text.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ); // Escape special characters var regexp = new RegExp( '.*' + text + '.*', 'g' ); var matches = MiniEdit.pageWikitext.match( regexp ); // This may happen if the paragraph comes from a template if ( !matches ) { return; } // This may happen if the longest text is very short and repeats somewhere else if ( matches.length > 1 ) { return; } // We got our relevant wikitext line return matches[0]; }, /** * Helper method to get the text of the longest text node */ getLongestText: function ( $paragraph ) { var text = ''; var $textNodes = $paragraph.contents().filter( function () { return this.nodeType === Node.TEXT_NODE; } ); $textNodes.each( function () { var nodeText = $( this ).text().trim(); if ( nodeText.length > text.length ) { text = nodeText; } } ); return text; }, /** * Helper method to build a helpful edit summary */ makeSummary: function ( summary, $paragraph, wikitext ) { if ( !summary ) { var action = wikitext ? 'edit' : 'delete'; summary = mw.msg( 'miniedit-summary-' + action ); } var $section = MiniEdit.getSection( $paragraph ); if ( $section ) { var section = $section.find( '.mw-headline' ).attr( 'id' ).replaceAll( '_', ' ' ); summary = '/* ' + section + ' */ ' + summary; } var page = mw.config.get( 'miniedit-page', 'mw:MiniEdit' ); summary += ' [[' + page + '| #MiniEdit]]'; // For https://hashtags.wmcloud.org return summary; }, /** * Helper method to find the closest section * by traversing back and up the DOM tree * * @param {jQuery object} Starting element * @return {jQuery object} Closest section */ getSection: function ( $element ) { if ( $element.attr( 'id' ) === 'mw-content-text' ) { return; } if ( $element.is( ':header' ) ) { return $element; } var $previous = $element.prevAll( ':header' ).first(); if ( $previous.length ) { return $previous; } var $parent = $element.parent(); return MiniEdit.getSection( $parent ); }, /** * Helper method to get a spinner (loading) icon */ getSpinner: function () { var spinner = '<svg class="miniedit-spinner" width="14" height="14" viewBox="0 0 100 100">'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.000" transform="rotate(-90 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.125" transform="rotate(-45 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.250" transform="rotate(0 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.375" transform="rotate(45 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.500" transform="rotate(90 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.625" transform="rotate(135 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.750" transform="rotate(180 50 50)" />'; spinner += '<rect fill="#555555" height="10" rx="5" ry="5" width="28" x="67" y="45" opacity="0.875" transform="rotate(225 50 50)" />'; spinner += '</svg>'; var $spinner = $( spinner ); var degrees = 0; setInterval( function () { degrees += 45; $spinner.css( 'transform', 'rotate(' + degrees + 'deg)' ); }, 100 ); return $spinner; }, /** * Helper method to decode base64 strings * See https://stackoverflow.com/questions/30106476 * * @param {string} Encoded string * @return {string} Decoded string */ decodeBase64: function ( string ) { return decodeURIComponent( window.atob( string ).split( '' ).map( function ( character ) { return '%' + ( '00' + character.charCodeAt( 0 ).toString( 16 ) ).slice( -2 ); } ).join( '' ) ); } }; mw.loader.using( [ 'mediawiki.api', 'mediawiki.user', 'mediawiki.util', 'oojs-ui-core', 'oojs-ui-widgets' ], MiniEdit.init );
返回
MediaWiki:Gadget-MiniEdit.js
。
导航菜单
个人工具
未登录
讨论
贡献
创建账号
登录
命名空间
消息
讨论
English
查看
阅读
查看源代码
查看历史
更多
导航
首页
最近更改
随机页面
沙盒
在线地图
官方网站
希顶相关网站
希顶维基
虫站
希顶语翻译器
工具
链入页面
相关更改
特殊页面
页面信息