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

מתוך חב"דפדיה, אנציקלופדיה חב"דית חופשית
גרסה מ־01:04, 9 במאי 2021 מאת חלוקת קונטרסים (שיחה | תרומות)
(הבדל) → הגרסה הקודמת | הגרסה האחרונה (הבדל) | הגרסה הבאה ← (הבדל)
קפיצה לניווט קפיצה לחיפוש

הערה: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (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'
        }).then( function( dictionaryPage ) {
            if (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 );
 
    /* VE is in Beta so the below API may not be valid
    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);
});