User:SodiumJS/copying-check.js

/* eslint-disable no-jquery/no-global-selector */ /* eslint-disable no-undef */ ( function {	const similarityThreshold = 0.5;	// how similar a copy needs to be to a paste to be considered not a issue	const substantialEditIsGreaterThan = 100; // JS characters	/**	 * https://stackoverflow.com/a/36566052	 * @param {string} s1 first string	 * @param {string} s2 second string	 * @return {number}	 */	function similarity( s1, s2 ) {		let longer = s1;		let shorter = s2;		if ( s1.length < s2.length ) {			longer = s2;			shorter = s1;		}		const longerLength = longer.length;		if ( longerLength === 0 ) {			return 1.0;		}		return ( longerLength - editDistance( longer, shorter ) ) / parseFloat( longerLength );	}

function editDistance( s1, s2 ) { s1 = s1.toLowerCase; s2 = s2.toLowerCase;

const costs = []; for ( let i = 0; i <= s1.length; i++ ) { let lastValue = i;			for ( let j = 0; j <= s2.length; j++ ) { if ( i === 0 ) { costs[ j ] = j;				} else { if ( j > 0 ) { let newValue = costs[ j - 1 ]; if ( s1.charAt( i - 1 ) !== s2.charAt( j - 1 ) ) { newValue = Math.min( Math.min( newValue, lastValue ),								costs[ j ] ) + 1; }						costs[ j - 1 ] = lastValue; lastValue = newValue; }				}			}			if ( i > 0 ) { costs[ s2.length ] = lastValue; }		}		return costs[ s2.length ]; }

/**	 * https://stackoverflow.com/a/53601942 * @param {EventListenerOrEventListenerObject} fn Callback */

function domReady( fn ) { document.addEventListener( 'DOMContentLoaded', fn ); if ( document.readyState === 'interactive' || document.readyState === 'complete' ) { const loadEvent = new Event( 'DOMContentLoaded' ); fn( loadEvent ); }	}

class CopyrightDataModel { constructor { this.copyDataEvents = []; this.largeCopiedAdditionOfText = false; }

onClipboardCopy { this.copyDataEvents.push( window.getSelection.toString ); }

onClipboardCut { this.copyDataEvents.push( window.getSelection.toString ); }

onClipboardPaste( evt ) { const pasteText = evt.clipboardData.getData( 'text/plain' ); const lastCopiedText = this.copyDataEvents.pop; if ( pasteText.length < substantialEditIsGreaterThan ) { return; }

if ( !lastCopiedText ) { mw.log( 'copy-paste-detected' ); this.largeCopiedAdditionOfText = true; return; }

if ( similarity( pasteText, lastCopiedText ) < similarityThreshold ) { mw.log( 'copy-paste-detected' ); this.largeCopiedAdditionOfText = true; return; }		}

hasIssues { return this.largeCopiedAdditionOfText; }

// copied from the ProveIt gadget getEditor { if ( window.ve && ve.init && ve.init.target && ve.init.target.active ) { if ( ve.init.target.getSurface.getMode === 'source' ) { return '2017'; // 2017 wikitext editor }				return 'visualeditor'; // Visual editor }			const action = mw.config.get( 'wgAction' ); if ( action === 'edit' || action === 'submit' ) { if ( mw.user.options.get( 'usebetatoolbar' ) === 1 ) { if ( $( '.CodeMirror' ).length ) { return 'codemirror'; // CodeMirror }					return 'wikieditor'; // WikiEditor }				return 'core'; // Core editor }		}

onDumpData { mw.storage.session.set( 'copying-data-copy-events', JSON.stringify( this.copyDataEvents ) ); mw.storage.session.set( 'copying-data-issue', this.largeCopiedAdditionOfText ); }

recoverState { const action = mw.config.get( 'wgAction' ); if ( action === 'submit' ) { this.copyDataEvents = JSON.parse( mw.storage.session.get( 'copying-data-copy-events' ) ); this.largeCopiedAdditionOfText = Boolean( mw.storage.session.get( 'copying-data-issue' ) ); }		}

registerHandlers { switch ( this.getEditor ) { case 'core': case 'wikieditor': { const textbox = document.querySelector( '#wpTextbox1' ); textbox.addEventListener( 'cut', this.onClipboardCut.bind( this ) ); textbox.addEventListener( 'copy', this.onClipboardCopy.bind( this ) ); textbox.addEventListener( 'paste', this.onClipboardPaste.bind( this ) ); }					break; case 'codemirror': { const textbox = document.querySelector( '.Codemirror' ); textbox.addEventListener( 'cut', this.onClipboardCut.bind( this ) ); textbox.addEventListener( 'copy', this.onClipboardCopy.bind( this ) ); textbox.addEventListener( 'paste', this.onClipboardPaste.bind( this ) ); }					break; }			window.addEventListener( 'beforeunload', this.onDumpData.bind( this ) ); const submitButton = document.querySelector( '#wpSave' ); submitButton.addEventListener( 'click', this.addTag.bind( this ) ); }

addTag { if ( !this.hasIssues ) { return; }

switch ( this.getEditor ) { case 'core': case 'wikieditor': case 'codemirror': { // eslint-disable-next-line no-console console.log( 'Will add tags here!' ); }					break; }		}	}

domReady( function {		const action = mw.config.get( 'wgAction' );		if ( action === 'edit' || action === 'submit' ) {			const copyrightData = new CopyrightDataModel;			copyrightData.recoverState;			copyrightData.registerHandlers;		}	} );

} );