MediaWiki:Common.js: различия между версиями
Материал из melioration.space
Sonys (обсуждение | вклад) |
Sonys (обсуждение | вклад) |
||
Строка 1: | Строка 1: | ||
− | /* | + | /** |
− | mw. | + | * written by [[User:ערן]] basesd on [[de:MediaWiki:Gadget-Rechtschreibpruefung.js]] |
− | + | **/ | |
− | + | function spellChecker() { | |
− | + | 'use strict' | |
− | // | + | |
− | console.log(' | + | /*== CONFIGURATION ==*/ |
− | + | var wbSpellCheck = 'Q15098221'; // wikidata item of list of words | |
− | // | + | var ignorePages = []; |
− | + | // language variants: the script looks for the keys of the dictionary below and if it finds them in article code it loads the specific variant instead of default | |
− | + | var langVariants = { } | |
− | + | var errors = { | |
− | + | installError: '<div>Please create a dictionary for spelling mistakes and link it to <a href="//www.wikidata.org/wiki/{0}>{0}</a> in Wikidata</div>', | |
− | + | spellListNotFound: 'Page not found: <a href="{0}">{1}</a>' | |
− | + | } | |
− | + | /*== END OF CONFIGURATION ==*/ | |
− | + | ||
+ | var mispellsList = localStorage.mispellsList || $.cookie( 'mispellsList' ); | ||
+ | var dictionary = { | ||
+ | misspells: {}, | ||
+ | keys: [] | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | Setups misspelling gadget - get the site-specific name of page with dictionary. returns $.Deferred | ||
+ | */ | ||
+ | function setup() { | ||
+ | var misspellInstall = new $.Deferred(); | ||
+ | if ( mispellsList ) { | ||
+ | misspellInstall.resolve(); | ||
+ | } else { | ||
+ | mw.loader.using(['wikibase.api.RepoApi', 'wikibase.client.getMwApiForRepo'], function(){ | ||
+ | var repoApi = new wikibase.api.RepoApi(wikibase.client.getMwApiForRepo()); | ||
+ | repoApi.getEntities( wbSpellCheck, 'sitelinks').done( function( data ) { | ||
+ | var currSite = mw.config.get( 'wgDBname' ); | ||
+ | if ( data.entities && data.entities.hasOwnProperty( wbSpellCheck ) && data.entities[wbSpellCheck].sitelinks && data.entities[wbSpellCheck].sitelinks.hasOwnProperty( currSite ) ) { | ||
+ | mispellsList = data.entities[wbSpellCheck].sitelinks[currSite].title; | ||
+ | try { | ||
+ | localStorage.mispellsList = mispellsList; | ||
+ | } catch(e) { | ||
+ | $.cookie( 'mispellsList', mispellsList ); | ||
+ | } | ||
+ | misspellInstall.resolve(); | ||
+ | } else { | ||
+ | mw.notify( $( errors.installError.replace('{0}', wbSpellCheck) ) ); | ||
+ | misspellInstall.reject(); | ||
+ | } | ||
+ | } ); | ||
+ | } ); | ||
+ | } | ||
+ | return misspellInstall; | ||
+ | } | ||
+ | |||
+ | function runSpellCheck() { | ||
+ | if ( mw.config.get( 'wgPageName' ).replace('_', ' ') == mispellsList || $.inArray( mw.config.get( 'wgPageName' ).replace(/_/g, ' '), ignorePages) != -1 ) return; | ||
+ | if ( dictionary.keys.length>0 ) { | ||
+ | checkSpells( $( '.ve-ce-surface, #mw-content-text:visible' ) ); | ||
+ | return; | ||
+ | } | ||
+ | var contextHTML = $( '.ve-ce-surface, #mw-content-text:visible' ).html(); | ||
+ | // load language variant dictionary instead of default dictionary | ||
+ | for (var variant in langVariants) { | ||
+ | if (contextHTML.indexOf(variant) !== -1) { | ||
+ | mispellsList += '/' + langVariants[variant]; | ||
+ | break; | ||
+ | } | ||
+ | } | ||
+ | $.ajax({ | ||
+ | url: mw.config.get('wgServer') + mw.util.wikiScript('index') + '?title=' + mw.util.wikiUrlencode( mispellsList ) + '&action=raw&ctype=text/x-wiki', | ||
+ | dataType: 'html' | ||
+ | }).done( function( dictionaryPage ) { | ||
+ | //remove intro and headers | ||
+ | dictionaryPage = dictionaryPage.substr( dictionaryPage.indexOf('==') ).replace( /==.*==/g,'' ) | ||
+ | parseDictionary( dictionaryPage ); | ||
+ | checkSpells( $( '.ve-ce-surface, #mw-content-text:visible' ) ); | ||
+ | } ); | ||
+ | } | ||
+ | |||
+ | function uniqueArr( listWords ) { | ||
+ | var dictWords = {}; | ||
+ | var res = []; | ||
+ | for ( var i=0; i<listWords.length; i++ ) { | ||
+ | dictWords[listWords[i]] = 1; | ||
+ | } | ||
+ | for ( var k in dictWords ) { | ||
+ | res.push( k ); | ||
+ | } | ||
+ | return res; | ||
+ | } | ||
+ | |||
+ | /* | ||
+ | Extract unique words from context. Removes words in citations. | ||
+ | */ | ||
+ | function extractPageWords( context ) { | ||
+ | var pageWords = {};//unique words in article | ||
+ | var wordList = []; | ||
+ | // remove citations and all "sic" + one word before | ||
+ | var splittedWords = context.text().replace(/\s[^\s]*[ \-]sic[ \-\!]/,' ').replace(/„.*?“/g,'').split(/\s/); //remove citations | ||
+ | for (var i=0;i<splittedWords.length;i++){ | ||
+ | if ( splittedWords[i].length && !( /^[0-9]+$/.test( splittedWords[i] ) ) ) { | ||
+ | var trimed = splittedWords[i].replace( /[\[\],\.\(\)]/g, '' ).toLowerCase(); | ||
+ | pageWords[trimed] = 1; | ||
+ | } | ||
+ | } | ||
+ | for ( var word in pageWords ) { | ||
+ | wordList.push(word); | ||
+ | } | ||
+ | return wordList; | ||
+ | } | ||
+ | |||
+ | function parseDictionary( dict ){ | ||
+ | //to dictioanry! | ||
+ | var correcto = dict.split('\n'); | ||
+ | var keyWords = []; | ||
+ | |||
+ | for ( var i=0; i<correcto.length; i++ ){ | ||
+ | var entry = correcto[i]; | ||
+ | if ( entry.length === 0 || (entry=entry.trim()).length === 0 ){ | ||
+ | continue;//skip empty lines | ||
+ | } | ||
+ | var fixTriple = entry.split( '|' ); | ||
+ | if ( fixTriple.length !==3 ){ | ||
+ | console.log('Bad entry:' +entry); | ||
+ | continue; | ||
+ | } | ||
+ | |||
+ | //skip on words appear in title | ||
+ | if (!(new RegExp( '(^|\\s)' + fixTriple[0] + '(?:$|\\s)','i').test(mw.config.get('wgTitle')))){ | ||
+ | dictionary.misspells[fixTriple[0].toLowerCase()] = { | ||
+ | hint: fixTriple[2], | ||
+ | cs: fixTriple[1] == 'cs', //case sensetive | ||
+ | word: fixTriple[0] | ||
+ | } | ||
+ | keyWords.push( fixTriple[0].toLowerCase() ); | ||
+ | } | ||
+ | } | ||
+ | dictionary.keys = uniqueArr( keyWords ); | ||
+ | } | ||
+ | |||
+ | function checkSpells( context ) { | ||
+ | //extract article words for efficent search | ||
+ | var artWords = extractPageWords( context ); | ||
+ | |||
+ | var words = dictionary.keys.concat( artWords ); | ||
+ | words.sort(); | ||
+ | |||
+ | var relevantWords = {}; | ||
+ | for (var i = 1; i<words.length; i++){ | ||
+ | if ( words[i] == words[i-1] ) { | ||
+ | relevantWords[words[i]] = 1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | for (var k in relevantWords) { | ||
+ | // since \b isn't supported in unicode we use heuristic instead which should catch 99% of the cases :) | ||
+ | markWordStart( context, new RegExp( '(^|[\\s\\(\\[-])(' + dictionary.misspells[k].word + ')(?=$|[-\\?\\!\\s\\.:,;\\)\\]])', dictionary.misspells[k].cs? '' : 'i' ), dictionary.misspells[k].hint ); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function markWordStart(context, text, hint) | ||
+ | { | ||
+ | var markedElement = context.get(0); | ||
+ | if ( markedElement ){ | ||
+ | markWord( markedElement, text, hint); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function markWord(node, text, hint) | ||
+ | { | ||
+ | var pos, len, newnodes = 0; | ||
+ | var newnode, middlenode, endnode; | ||
+ | var textmatch; | ||
+ | // textnode - search for word | ||
+ | if (node.nodeType == 3) | ||
+ | { | ||
+ | pos = node.data.search(text); | ||
+ | if (pos >= 0 && text.test(node.data.replace(/[„].*?[“]/g,''))) | ||
+ | { | ||
+ | textmatch = node.data.match( text ); | ||
+ | pos += textmatch[1].length; // skip prefix | ||
+ | // create new span-element | ||
+ | newnode = document.createElement("span"); | ||
+ | newnode.style.backgroundColor = "#FF9191"; | ||
+ | newnode.title = hint; | ||
+ | newnode.className = 'spellError'; | ||
+ | // get length of the matching part | ||
+ | len = textmatch[2].length; | ||
+ | // splits content in three parts: begin, middle and end | ||
+ | middlenode = node.splitText(pos); | ||
+ | endnode = middlenode.splitText(len); | ||
+ | |||
+ | // appends a copy of the middle to the new span-node | ||
+ | newnode.appendChild(middlenode.cloneNode(true)); | ||
+ | // replace middlenode with the new span-node | ||
+ | middlenode.parentNode.replaceChild(newnode, middlenode); | ||
+ | newnodes = 1; | ||
+ | } | ||
+ | } | ||
+ | else if ((node.nodeType == 1) // element node | ||
+ | && (node.hasChildNodes()) // with child nodes | ||
+ | && !(/blockquote|^q$|cite|script|style|form/i.test(node.tagName)) | ||
+ | && !(/mw-userlink|mw-usertoollinks|mw-changeslist-(date|separator)/.test(node.className))) // no script, style and form and citations or mw classes | ||
+ | { | ||
+ | var this_child; | ||
+ | for (this_child = 0; this_child < node.childNodes.length; this_child++) | ||
+ | { | ||
+ | this_child = this_child + markWord(node.childNodes[this_child], text, hint); | ||
+ | } | ||
+ | } | ||
+ | return newnodes; | ||
+ | } | ||
+ | |||
+ | setup().then( runSpellCheck ); | ||
+ | |||
+ | mw.hook( 've.activationComplete' ).add( function() { | ||
+ | setup().then( function(){ | ||
+ | // inital find misspells (for all document) | ||
+ | runSpellCheck(); | ||
+ | var view =ve.init.target.getSurface().getView(); | ||
+ | var doc = view.getDocument(); | ||
+ | var model = ve.init.target.getSurface().getModel(); | ||
+ | //while editing - only on current node | ||
+ | model.on( 'documentUpdate', function () { | ||
+ | try | ||
+ | { | ||
+ | var selection = model.getSelection().getRange(), | ||
+ | node = selection && doc.getBranchNodeFromOffset( selection.start ), | ||
+ | originalSelection; | ||
+ | if ( !( node instanceof ve.ce.ContentBranchNode ) ) { | ||
+ | return; | ||
+ | } | ||
+ | //remove spell errors from current node | ||
+ | $( node.$element[0] ).find('.spellError').contents().unwrap(); | ||
+ | //view.surfaceObserver.disable(); | ||
+ | checkSpells( $( node.$element[0] ) ); | ||
+ | //reset selection | ||
+ | selection = model.getSelection().getRange(); | ||
+ | originalSelection = view.getSelectionState( new ve.Range(selection.to, selection.to) ); | ||
+ | |||
+ | setTimeout( function () { | ||
+ | view.showSelectionState( originalSelection ); | ||
+ | } ); | ||
+ | } catch(err){ | ||
+ | console.log('Error in Gadget-Rechtschreibpruefung.js:documentUpdate'); | ||
+ | console.log(err); | ||
+ | } | ||
+ | } ); | ||
+ | } ); | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | $(function(){ | ||
+ | //run only on active tabs | ||
+ | if ( typeof document.hidden === "undefined" || !document.hidden) spellChecker(); | ||
+ | else $(document).one('visibilitychange', spellChecker); | ||
}); | }); | ||
− |
Версия 18:39, 25 декабря 2020
/** * written by [[User:ערן]] basesd on [[de:MediaWiki:Gadget-Rechtschreibpruefung.js]] **/ function spellChecker() { 'use strict' /*== CONFIGURATION ==*/ var wbSpellCheck = 'Q15098221'; // wikidata item of list of words var ignorePages = []; // language variants: the script looks for the keys of the dictionary below and if it finds them in article code it loads the specific variant instead of default var langVariants = { } var errors = { installError: '<div>Please create a dictionary for spelling mistakes and link it to <a href="//www.wikidata.org/wiki/{0}>{0}</a> in Wikidata</div>', spellListNotFound: 'Page not found: <a href="{0}">{1}</a>' } /*== END OF CONFIGURATION ==*/ var mispellsList = localStorage.mispellsList || $.cookie( 'mispellsList' ); var dictionary = { misspells: {}, keys: [] } /* Setups misspelling gadget - get the site-specific name of page with dictionary. returns $.Deferred */ function setup() { var misspellInstall = new $.Deferred(); if ( mispellsList ) { misspellInstall.resolve(); } else { mw.loader.using(['wikibase.api.RepoApi', 'wikibase.client.getMwApiForRepo'], function(){ var repoApi = new wikibase.api.RepoApi(wikibase.client.getMwApiForRepo()); repoApi.getEntities( wbSpellCheck, 'sitelinks').done( function( data ) { var currSite = mw.config.get( 'wgDBname' ); if ( data.entities && data.entities.hasOwnProperty( wbSpellCheck ) && data.entities[wbSpellCheck].sitelinks && data.entities[wbSpellCheck].sitelinks.hasOwnProperty( currSite ) ) { mispellsList = data.entities[wbSpellCheck].sitelinks[currSite].title; try { localStorage.mispellsList = mispellsList; } catch(e) { $.cookie( 'mispellsList', mispellsList ); } misspellInstall.resolve(); } else { mw.notify( $( errors.installError.replace('{0}', wbSpellCheck) ) ); misspellInstall.reject(); } } ); } ); } return misspellInstall; } function runSpellCheck() { if ( mw.config.get( 'wgPageName' ).replace('_', ' ') == mispellsList || $.inArray( mw.config.get( 'wgPageName' ).replace(/_/g, ' '), ignorePages) != -1 ) return; if ( dictionary.keys.length>0 ) { checkSpells( $( '.ve-ce-surface, #mw-content-text:visible' ) ); return; } var contextHTML = $( '.ve-ce-surface, #mw-content-text:visible' ).html(); // load language variant dictionary instead of default dictionary for (var variant in langVariants) { if (contextHTML.indexOf(variant) !== -1) { mispellsList += '/' + langVariants[variant]; break; } } $.ajax({ url: mw.config.get('wgServer') + mw.util.wikiScript('index') + '?title=' + mw.util.wikiUrlencode( mispellsList ) + '&action=raw&ctype=text/x-wiki', dataType: 'html' }).done( function( dictionaryPage ) { //remove intro and headers dictionaryPage = dictionaryPage.substr( dictionaryPage.indexOf('==') ).replace( /==.*==/g,'' ) parseDictionary( dictionaryPage ); checkSpells( $( '.ve-ce-surface, #mw-content-text:visible' ) ); } ); } function uniqueArr( listWords ) { var dictWords = {}; var res = []; for ( var i=0; i<listWords.length; i++ ) { dictWords[listWords[i]] = 1; } for ( var k in dictWords ) { res.push( k ); } return res; } /* Extract unique words from context. Removes words in citations. */ function extractPageWords( context ) { var pageWords = {};//unique words in article var wordList = []; // remove citations and all "sic" + one word before var splittedWords = context.text().replace(/\s[^\s]*[ \-]sic[ \-\!]/,' ').replace(/„.*?“/g,'').split(/\s/); //remove citations for (var i=0;i<splittedWords.length;i++){ if ( splittedWords[i].length && !( /^[0-9]+$/.test( splittedWords[i] ) ) ) { var trimed = splittedWords[i].replace( /[\[\],\.\(\)]/g, '' ).toLowerCase(); pageWords[trimed] = 1; } } for ( var word in pageWords ) { wordList.push(word); } return wordList; } function parseDictionary( dict ){ //to dictioanry! var correcto = dict.split('\n'); var keyWords = []; for ( var i=0; i<correcto.length; i++ ){ var entry = correcto[i]; if ( entry.length === 0 || (entry=entry.trim()).length === 0 ){ continue;//skip empty lines } var fixTriple = entry.split( '|' ); if ( fixTriple.length !==3 ){ console.log('Bad entry:' +entry); continue; } //skip on words appear in title if (!(new RegExp( '(^|\\s)' + fixTriple[0] + '(?:$|\\s)','i').test(mw.config.get('wgTitle')))){ dictionary.misspells[fixTriple[0].toLowerCase()] = { hint: fixTriple[2], cs: fixTriple[1] == 'cs', //case sensetive word: fixTriple[0] } keyWords.push( fixTriple[0].toLowerCase() ); } } dictionary.keys = uniqueArr( keyWords ); } function checkSpells( context ) { //extract article words for efficent search var artWords = extractPageWords( context ); var words = dictionary.keys.concat( artWords ); words.sort(); var relevantWords = {}; for (var i = 1; i<words.length; i++){ if ( words[i] == words[i-1] ) { relevantWords[words[i]] = 1; } } for (var k in relevantWords) { // since \b isn't supported in unicode we use heuristic instead which should catch 99% of the cases :) markWordStart( context, new RegExp( '(^|[\\s\\(\\[-])(' + dictionary.misspells[k].word + ')(?=$|[-\\?\\!\\s\\.:,;\\)\\]])', dictionary.misspells[k].cs? '' : 'i' ), dictionary.misspells[k].hint ); } } function markWordStart(context, text, hint) { var markedElement = context.get(0); if ( markedElement ){ markWord( markedElement, text, hint); } } function markWord(node, text, hint) { var pos, len, newnodes = 0; var newnode, middlenode, endnode; var textmatch; // textnode - search for word if (node.nodeType == 3) { pos = node.data.search(text); if (pos >= 0 && text.test(node.data.replace(/[„].*?[“]/g,''))) { textmatch = node.data.match( text ); pos += textmatch[1].length; // skip prefix // create new span-element newnode = document.createElement("span"); newnode.style.backgroundColor = "#FF9191"; newnode.title = hint; newnode.className = 'spellError'; // get length of the matching part len = textmatch[2].length; // splits content in three parts: begin, middle and end middlenode = node.splitText(pos); endnode = middlenode.splitText(len); // appends a copy of the middle to the new span-node newnode.appendChild(middlenode.cloneNode(true)); // replace middlenode with the new span-node middlenode.parentNode.replaceChild(newnode, middlenode); newnodes = 1; } } else if ((node.nodeType == 1) // element node && (node.hasChildNodes()) // with child nodes && !(/blockquote|^q$|cite|script|style|form/i.test(node.tagName)) && !(/mw-userlink|mw-usertoollinks|mw-changeslist-(date|separator)/.test(node.className))) // no script, style and form and citations or mw classes { var this_child; for (this_child = 0; this_child < node.childNodes.length; this_child++) { this_child = this_child + markWord(node.childNodes[this_child], text, hint); } } return newnodes; } setup().then( runSpellCheck ); mw.hook( 've.activationComplete' ).add( function() { setup().then( function(){ // inital find misspells (for all document) runSpellCheck(); var view =ve.init.target.getSurface().getView(); var doc = view.getDocument(); var model = ve.init.target.getSurface().getModel(); //while editing - only on current node model.on( 'documentUpdate', function () { try { var selection = model.getSelection().getRange(), node = selection && doc.getBranchNodeFromOffset( selection.start ), originalSelection; if ( !( node instanceof ve.ce.ContentBranchNode ) ) { return; } //remove spell errors from current node $( node.$element[0] ).find('.spellError').contents().unwrap(); //view.surfaceObserver.disable(); checkSpells( $( node.$element[0] ) ); //reset selection selection = model.getSelection().getRange(); originalSelection = view.getSelectionState( new ve.Range(selection.to, selection.to) ); setTimeout( function () { view.showSelectionState( originalSelection ); } ); } catch(err){ console.log('Error in Gadget-Rechtschreibpruefung.js:documentUpdate'); console.log(err); } } ); } ); }); } $(function(){ //run only on active tabs if ( typeof document.hidden === "undefined" || !document.hidden) spellChecker(); else $(document).one('visibilitychange', spellChecker); });