User:TechnoSquirrel69/Scripts/diffedit.js

/* Customized fork of this script for TechnoSquirrel69's use. See the revision history at m:User:Jon Harald Søby/diffedit.js for attribution /*! * diffedit.js – script that lets you edit pages directly from the diff view * * @author Jon Harald Søby * @version 1.2.0 (2023-10-13) * @licence CC-by-SA 4.0 * * For documentation, see User:Jon Harald Søby/diffedit */ function initDiffedit( $, mw, OO ) { 'use strict'; var messages, linetopCache = '', tabIndex = 1, pageId = mw.config.get( 'wgArticleId' ), oldRevisionId = mw.config.get( 'wgDiffOldId' ), newRevisionId = mw.config.get( 'wgDiffNewId' ), currentRevisionId = mw.config.get( 'wgCurRevisionId' ), contentModel = mw.config.get( 'wgPageContentModel' ), allowedContentModels = [ 'wikitext', 'text', 'sanitized-css', 'json', 'javascript', 'css', 'Scribunto' ], api = new mw.Api; /* The 'messages' function is from d:MediaWiki:Gadget-Merge.js, * see that page's history for credits */	messages = function { var translations = { en: { editTitle: 'Edit this diff', editTitleNewerRevs: 'This diff can\'t be edited, since there are newer revisions of the page.', noPermission: 'You do not have permission to edit this page', refresh: 'Show latest', refreshTitle: 'Click to load the diff against the newest revision of the page' },			ar: { editTitle: 'عدّل هذا الفرق', editTitleNewerRevs: 'لا يمكن تعديل هذا الفرق، نظرًا لوجود مراجعات أحدث للصفحة.', noPermission: 'ليس لديك صلاحية تعديل هذه الصفحة', refresh: 'عرض الأحدث', refreshTitle: 'انقر لتحميل الفرق مقابل أحدث مراجعة للصفحة' },			bn: { editTitle: 'এই পার্থক্যটি সম্পাদনা করুন', editTitleNewerRevs: 'পাতাটিতে নতুনতর সম্পাদনা থাকায়, এই পার্থক্যটি সম্পাদনা করা যাবে না।', noPermission: 'আপনার এই পাতাটি সম্পাদনা করার অনুমতি নেই', refresh: 'সর্বশেষটি দেখান', refreshTitle: 'পাতাটির নতুনতর সংশোধনের পার্থক্য লোড করতে ক্লিক করুন' },			ckb: { editTitle: 'ئەم جیاوازییە دەستکاری بکە', editTitleNewerRevs: 'ئەم جیاوازییە ناتوانرێت دەستکاری بکرێ، چونکە پەڕەکە پێداچوونەوەی نوێتری ھەیە.', noPermission: 'دەسەڵاتت نییە دەستکاریی ئەم پەڕەیە بکەیت.', refresh: 'کۆتا دانە پیشان بدە', refreshTitle: 'کرتە بکە تا جیاوازییەکە لەگەڵ نوێترین پێداچوونەوەی پەڕەکە بار بکەیت' },			de: { editTitle: 'Diesen Versionsunterschied bearbeiten', editTitleNewerRevs: 'Dieser Versionsunterschied kann nicht bearbeitet werden, da es neuere Versionen der Seite gibt.', noPermission: 'Du hast keine Berechtigung, diese Seite zu bearbeiten.', refresh: 'Letzte anzeigen', refreshTitle: 'Klicke, um den Versionsunterschied zur neuesten Version der Seite zu laden' },			es: { editTitle: 'Editar esta diferencia', editTitleNewerRevs: 'Esta diferencia no puede editarse ya que hay revisiones más recientes de la página.', noPermission: 'No tienes permiso para editar esta página', refresh: 'Mostrar la última revisión', refreshTitle: 'Pulsa para cargar la diferencia con la revisión más reciente de la página' },			fa: { editTitle: 'ویرایش این تفاوت', editTitleNewerRevs: 'این تفاوت را نمی‌توان ویرایش کرد؛ زیرا نسخه‌های جدیدتری از صفحه موجود هستند.', noPermission: 'شما اختیارات لازم برای ویرایش این صفحه را ندارید', refresh: 'نمایش جدیدترین', refreshTitle: 'برای بارگیری تفاوت با جدیدترین نسخهٔ صفحه کلیک کنید' },			fr: { editTitle: 'Modifier ce diff', editTitleNewerRevs: 'Ce diff ne peut pas être édité, car il existe des révisions plus récentes de la page.', noPermission: 'Vous n\'avez pas le droit de modifier cette page', refresh: 'Montrer le dernier', refreshTitle: 'Cliquez pour charger la comparaison avec la dernière révision de la page.' },			he: { editTitle: 'עריכת ההשוואה הזאת', editTitleNewerRevs: 'ההשוואה הזאת לא ניתנת לעריכה, מכיוון שקיימות גרסאות חדשות יותר של הדף.', noPermission: 'אין לך הרשאות לעריכת הדף הזה', refresh: 'להראות את המאוחר ביותר', refreshTitle: 'ללחוץ לקבלת ההשוואה עם הגרסה האחרונה של הדף' },			hr: { editTitle: 'Uredi prikazanu razliku inačica', editTitleNewerRevs: 'Prikazana razlika ne može se uređivati zato što postoje novije inačice stranice.', noPermission: 'Nije Vam dopušteno uređivati ovu stranicu', refresh: 'Posljednja inačica', refreshTitle: 'Učitaj razliku s najnovijom inačicom stranice' },			ja: { editTitle: 'この差分を編集', editTitleNewerRevs: 'ページに新しい版があるため、この差分は編集できません. ',				noPermission: 'このページを編集する権限がありません', refresh: '最新版を表示', refreshTitle: 'クリックして、ページの最新版との差分を読み込みます' },			ka: { editTitle: 'ამ განსხვავების რედაქტირება', editTitleNewerRevs: 'ამ განსხვავების რედაქტირება შეუძლებელია, რადგან არსებობს გვერდის უფრო ახალი ვერსია.', noPermission: 'თქვენ არ გაქვთ ამ გვერდის რედაქტირების უფლება', refresh: 'უკანასკნელის ხილვა', refreshTitle: 'დააწკაპეთ, რათა იხილოთ განსხვავება გვერდის უახლეს ვერსიასთან' },			ko: { editTitle: '이 diff 수정', editTitleNewerRevs: '페이지의 최신 버전이 있으므로 이 diff를 수정할 수 없습니다.', noPermission: '이 페이지를 편집할 수 있는 권한이 없습니다.', refresh: '최신 보기', refreshTitle: '페이지의 최신 개정판에 대한 diff를 로드하려면 클릭하십시오.' },			nb: { editTitle: 'Rediger denne diffen', editTitleNewerRevs: 'Denne diffen kan ikke redigeres, siden det finnes nyere revisjoner av siden.', noPermission: 'Du har ikke tillatelse til å redigere denne sida', refresh: 'Vis nyeste', refreshTitle: 'Klikk for å laste diffen mot den nyeste revisjonen av siden' },			nl: { editTitle: 'Dit verschil bewerken', editTitleNewerRevs: 'Dit verschil kan niet worden bewerkt, omdat er een nieuwere revisie van de pagina is.', noPermission: 'U mag deze pagina niet bewerken', refresh: 'Toon de laatste', refreshTitle: 'Klik om het verschil te laden met de nieuwste revisie van de pagina' },			nn: { editTitle: 'Endre denne diffen', editTitleNewerRevs: 'Du kan ikkje endre denne diffen, av di det finst nyare revisjonar av sida.', noPermission: 'Du har ikkje løyve til å endra denne sida', refresh: 'Syn nyaste', refreshTitle: 'Klikk for å lasta diffen mot den nyaste versjonen av sida' },			pl: { editTitle: 'Edytuj to porównanie', editTitleNewerRevs: 'Nie możesz edytować porównania, bo istnieją nowsze wersje tej strony.', noPermission: 'Nie masz uprawnień do edycji tej strony', refresh: 'Pokaż najnowszą', refreshTitle: 'Kliknij, aby załadować porównanie z najnowszą wersją tej strony' },			sv: { editTitle: 'Redigera denna diff', editTitleNewerRevs: 'Denna diff kan inte redigeras då det finns nyare sidversioner.', noPermission: 'Du har inte behörighet att redigera den här sidan', refresh: 'Visa senaste', refreshTitle: 'Klicka för att ladda in skillnaden mot den senaste sidversionen' },			th: { editTitle: 'แก้ไขความแตกต่างนี้', editTitleNewerRevs: 'แก้ไขความแตกต่างนี้ไม่ได้ เนื่องจากมีการแก้ไขหน้าเว็บที่ใหม่กว่า', noPermission: 'คุณไม่ได้รับอนุญาตให้แก้ไขหน้านี้', refresh: 'แสดงการแก้ไขล่าสุด', refreshTitle: 'คลิกเพื่อโหลดความแตกต่างกับรุ่นใหม่ล่าสุดของหน้า' },			tl: { editTitle: 'I-edit ang diff na ito', editTitleNewerRevs: 'Hindi ma-edit ang diff na ito, dahil may mga mas bagong rebisyon ng page.', noPermission: 'Wala kang permission mag-edit ang page.', refresh: 'Ipakita ang pinakbago', refreshTitle: 'I-click upang i-load ang diff laban sa pinakabagong rebisyon ng pahina' },			vi: { editTitle: 'Sửa đổi từ trang Khác này', editTitleNewerRevs: 'Không thể sửa đổi vì có các phiên bản mới hơn.', noPermission: 'Bạn không có quyền sửa trang này', refresh: 'Xem sửa đổi mới nhất', refreshTitle: 'Nhấp để xem khác biệt với phiên bản hiện tại của trang' },			zh: { editTitle: '編輯此差異', editTitleNewerRevs: '由於有更新的版本，這個差異無法被修改. ',				noPermission: '您沒有權限修改這個頁面. ',				refresh: '顯示最新版本', refreshTitle: '點擊以載入頁面與最新修改的差異' },		},			chain = mw.language.getFallbackLanguageChain, len = chain.length, ret = {}, i = len - 1; while ( i >= 0 ) { if ( translations.hasOwnProperty( chain[ i ] ) ) { $.extend( ret, translations[ chain[ i ] ] ); }	   	i = i - 1; }			return ret; };

function enumerateLines { var currentLine = 0, numberRegex = '0-9', localNumbers = mw.language.getDigitTransformTable; if ( ( localNumbers instanceof Array && localNumbers.length ) || Object.keys( localNumbers ).length ) { for ( const i of Array( 10 ).keys ) { numberRegex = numberRegex.concat( localNumbers[ i ] ); }		}		$( 'table.diff tbody tr td:last-of-type' ).each( function {			if ( $( this ).hasClass( 'diff-lineno' ) ) {				var lineNo = $( this ).text;				lineNo = mw.language.convertNumber( lineNo.replace( new RegExp( '[^' + numberRegex + ']', 'g' ), '' ), true );				currentLine = lineNo;			} else if ( $( this ).hasClass( 'diff-addedline' ) || $( this ).hasClass( 'diff-context' ) ) {				$( this ).addClass( 'diff-editable' ).attr( 'data-mw-diff-line', currentLine );				$( this ).attr( 'data-mw-diff-tabindex', tabIndex );				currentLine++;				tabIndex++;			}		}); }	function addEditButton( titleText ) { var editTitle = titleText ? titleText : messages.editTitle, editIcon = titleText ? 'editLock' : 'edit'; var editButton = new OO.ui.ButtonWidget( {				label: mw.message( 'edit' ).text,				icon: editIcon,				title: editTitle,				flags: [ 'primary', 'progressive' ],				disabled: !!titleText			}); linetopCache = $( '.diff-linetop' ).html; $( '.diffedit-editbutton' ).append( editButton.$element ); if ( !titleText ) { editButton.on( 'click', function {				toggleEditMode( 'enable' );				addEditLine;			}); }	}	function addRefreshButton { var refreshButton = new OO.ui.ButtonWidget( {				label: messages.refresh,				icon: 'reload',				title: messages.refreshTitle,				flags: [ 'progressive' ],				invisibleLabel: true,				framed: false			}); $( '.diffedit-editbutton' ).prepend( refreshButton.$element ); refreshButton.on( 'click', function {			window.location.href = mw.config.get( 'wgServer' ) + mw.util.getUrl( mw.config.get( 'wgPageName' ), { 'diff': 'cur', 'oldid': oldRevisionId } );		}); }	function addEditLine { var editSummary = new OO.ui.TextInputWidget( {				icon: 'textSummary',				accessKey: mw.message( 'accesskey-summary' ).text,				name: 'wpSummary',				tabIndex: tabIndex + 1,				placeholder: mw.message( 'revisionslider-label-comment' ).text,				title: mw.message( 'tooltip-summary' ).text,				classes: [ 'diffedit-editsummary' ]			}), publishButton = new OO.ui.ButtonWidget( {				label: mw.message( 'publishchanges' ).text,				title: mw.message( 'tooltip-publish' ).text,				accessKey: mw.message( 'accesskey-publish' ).text,				tabIndex: tabIndex + 2,				flags: [ 'primary', 'progressive' ],				classes: [ 'diffedit-publishbutton' ]			}), cancelButton = new OO.ui.ButtonWidget( {				label: mw.message( 'cancel' ).text,				icon: 'cancel',				invisibleLabel: true,				tabIndex: tabIndex + 3,				flags: [ 'destructive' ],				framed: false			}), editLine = new OO.ui.FieldLayout( new OO.ui.Widget( { content: [ new OO.ui.HorizontalLayout( {						items: [ editSummary, publishButton, cancelButton ]					})] }));		publishButton.on( 'click', function {			processEdit;		}); publishButton.$element.hover( function {			if ( $( 'input[name=wpSummary]' ).val.length === 0 ) {				$( 'input[name=wpSummary]' ).css( { 'outline': '5px solid gold', 'transition': 'outline 500ms cubic-bezier(.5,2,.5,-1)' } );			} else {				$( 'input[name=wpSummary] ').css( { 'outline': '5px solid transparent' } );			}		}); editSummary.$element.keyup( function( e ) {			if ( e.key === 'Enter' ) {				processEdit;			}		}); cancelButton.on( 'click', function {			toggleEditMode( 'disable' );		}); editLine.$field.css( 'float', 'none' ); $( '.diff-linetop' ).html( editLine.$element ); $( 'input[name=wpSummary]' ).on( 'keyup keydown change', function {			$( this ).css( { 'outline': '5px solid transparent', 'transition': 'outline 500ms ease-out' } );		}); }	function toggleEditMode( state ) { if ( state === 'enable' ) { $( 'table.diff' ).addClass( 'diff-editmode' ); $( '.diff-editable' ).each( function {				$( this ).attr( { 'contenteditable': 'true', 'tabindex': $( this ).attr( 'data-mw-diff-tabindex' ) } ).css( { 'word-wrap': 'break-word', 'white-space': 'pre-wrap' } );			}); $( '.diff-editable div' ).text; $( '.diff-editable' ).first.attr( 'accesskey', ',' ).focus; } else { $( 'table.diff' ).removeClass( 'diff-editmode' ); $( '.diff-linetop' ).html( linetopCache ); addEditButton; $( '.diff-editable' ).each( function {				$( this ).attr( 'contenteditable', 'false').removeAttr( 'tabindex' ).css( { 'word-wrap': 'break-word', 'white-space': 'pre-wrap' } );			}); }	}	function processEdit { $( '.diffedit-publishbutton' ).addClass( 'oo-ui-pendingElement-pending' ).removeClass( 'oo-ui-flaggedElement-primary' ); var currentContent = '', contentFromApi = api.get( {				action: 'query',				prop: 'revisions',				rvprop: 'content',				rvslots: 'main',				pageids: pageId,				rvstartid: currentRevisionId,				rvendid: currentRevisionId			} ); contentFromApi.done( function( data ) {			var mainSlot = data.query.pages[pageId].revisions[0].slots.main,				contentModel = mainSlot.contentmodel;			currentContent = mainSlot[ '*' ];			currentContent = currentContent.split( '\n' );			var newContent = currentContent;			$( '.diff-editable' ).each( function { var thisline = [], lineNo = $( this ).attr( 'data-mw-diff-line' ); if ( $( this ).contents.length === 0 ) { thisline.push( '' ); } else if ( $( this ).contents[0].nodeName === '#text' ) { thisline.push( $( this ).contents[0].textContent ); } else { $( this ).contents.each( function {						thisline.push( this.textContent );					}); }				newContent[lineNo-1] = thisline.join( '\n' ); });			api.postWithEditToken( { action: 'edit', pageid: pageId, baserevid: newRevisionId, nocreate: true, text: newContent.join( '\n' ), minor: false, summary: $( 'input[name=wpSummary]' ).val + ' ' + mw.message( 'parentheses', 'diffedit' ).text } ).done( function( data ) { window.location.href = mw.config.get( 'wgServer' ) + mw.util.getUrl( mw.config.get( 'wgPageName' ), { 'diff': 'cur' } ); }).fail( function( err ) { console.log(err); alert( 'Error: ' + err ); });		}).fail( function( err ) {			alert( 'Failed: ' + err );			return;		}); }	$( '.diff-lineno:first' ).next.addClass( 'diff-linetop' ).append( $( ' ' ).addClass( 'diffedit-editbutton' ) ); if ( !mw.config.get( 'wgIsProbablymiEditable' ) ) { addEditButton( messages.noPermission ); } else if ( $( '.mw-diff-slot-header' ).length ) { addEditButton( mw.message( 'editpage-invalidcontentmodel-text', 'mixed content' ).text ); } else if ( !allowedContentModels.includes( contentModel ) ) { addEditButton( mw.message( 'editpage-invalidcontentmodel-text', contentModel ).text ); } else if ( newRevisionId !== currentRevisionId ) { addEditButton( messages.editTitleNewerRevs ); addRefreshButton; } else { enumerateLines; addEditButton; $( '.diff-linetop, .diff-editable' ).keyup( function( e ) {			if ( e.key === 'Escape' ) {				toggleEditMode( 'disable' );			} else if ( e.ctrlKey && e.key === 'Enter' ) {				processEdit;			}		}); } }

( function {	if ( !( mw.config.get( 'wgDiffNewId' ) ) ) {		return;	}

mw.loader.using( [		'mediawiki.api',		'mediawiki.jqueryMsg',		'mediawiki.language',		'oojs-ui-core',		'oojs-ui.styles.icons-editing-core',		'oojs-ui.styles.icons-interactions',		'oojs-ui.styles.icons-layout'	] ).then( function {		mw.loader.load( 'https://meta.wikimedia.org/w/index.php?title=User:Jon_Harald_Søby/diffedit.css&action=raw&ctype=text/css', 'text/css' );		new mw.Api.loadMessagesIfMissing( [ 'edit', // Edit 'publishchanges', // Publish 'tooltip-publish', // Publish these changes 'cancel', // Cancel 'accesskey-publish', // s			'accesskey-summary', // b			'editpage-invalidcontentmodel-text', // Invalid content model $1 'revisionslider-label-comment', // Edit summary 'tooltip-summary', // Summary tooltip 'parentheses' // ($1) ] ).done( function( data ) { initDiffedit( jQuery, mediaWiki, OO ); } ).fail( function( err ) { mw.notify( err, { title: 'diffedit error', type: 'error' } ); } );	} ); } );