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);
});