מדיה ויקי:Gadget-Rechtschreibpruefung.js

מתוך ויקיסוגיה
קפיצה לניווט קפיצה לחיפוש
הגרסה להדפסה אינה נתמכת עוד וייתכן שיש בה שגיאות תיצוג. נא לעדכן את הסימניות בדפדפן שלך ולהשתמש בפעולת ההדפסה הרגילה של הדפדפן במקום זה.

הערה: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.

  • פיירפוקס / ספארי: להחזיק את המקש Shift בעת לחיצה על טעינה מחדש (Reload), או ללחוץ על צירוף המקשים Ctrl-F5 או Ctrl-R (במחשב מק: ⌘-R).
  • גוגל כרום: ללחוץ על צירוף המקשים Ctrl-Shift-R (במחשב מק: ⌘-Shift-R).
  • אינטרנט אקספלורר / אדג': להחזיק את המקש Ctrl בעת לחיצה על רענן (Refresh), או ללחוץ על צירוף המקשים Ctrl-F5.
  • אופרה: ללחוץ על Ctrl-F5.
/** 
 * 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' ] ).done(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.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 ) {
	    var hasError = false;
	    //extract article words for efficient 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;
			    hasError = true;
		    }
	    }
 
	    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 );
	    }
	    return hasError;
    }
 
    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;
                    }
					// OO UI doesnt like when we update the DOM during documentUpdate event
					setTimeout( function () {
		                //remove spell errors from current node
		                $( node.$element[0] ).find('.spellError').contents().unwrap();

		                if ( checkSpells( $( node.$element[0] ) ) ) {
							//reset selection
							selection = model.getSelection().getRange();
							originalSelection = view.getSelectionState( new ve.Range(selection.to, selection.to) );			
							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);
});