Jump to content

User:Shirik/guidebook.js

From Wikipedia, the free encyclopedia
This is the current revision of this page, as edited by Shirik (talk | contribs) at 02:17, 9 June 2013 (corrected an issue where links were being interpreted incorrectly). The present address (URL) is a permanent link to this version.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// <source lang="javascript">
/**
 * Guidebook v0.1
 * By Matthew "Shirik" Del Buono
 * 
 * This script adjusts the "diff" window so that it shows relevant guidelines
 * associated with the edit. 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

// Some semaphore-like things that allow us to implement a barrier waiting for all other modules to load.
var guidebook_mootoolsLoaded = false;
var guidebook_patternsLoaded = false;
var guidebook_configLoaded = false;
var guidebook_onLoadOccurred = false;

function guidebook_checkLoaded()
{
   if (guidebook_mootoolsLoaded && guidebook_patternsLoaded && guidebook_configLoaded && guidebook_onLoadOccurred)
   {
      guidebook_onReady();
   }
}

function guidebook_loadDefaults()
{
   mw.log("Loading guidebook defaults");
   window.GUIDEBOOK_MAXDISPLAYEDPOLICIES = 3;
   guidebook_configLoaded = true;
}

// Import libs
jQuery.getScript('//meta.wikimedia.org/w/index.php?title=User:Pathoschild/Scripts/MooTools.js&action=raw&ctype=text/javascript&9',
   function() { guidebook_mootoolsLoaded = true; guidebook_checkLoaded(); });

// Load configuration
jQuery.getScript('//en.wikipedia.org/w/index.php?title=User:Shirik/guidebook_patterns.js&action=raw&ctype=text/javascript',
   function() { guidebook_patternsLoaded = true; guidebook_checkLoaded(); });

function guidebook_onReady()
{
   // If AJAX not supported, bail out
   if(!wfSupportsAjax())
      return;

   // If this is a diff, then let's actually do something
   var GET = guidebook_getUrlVars();
   if (mw.config.get('wgAction') == 'view' && 'diff' in GET && (GET['diff'] > 0 || GET['diff'] == 'prev' || GET['diff'] == 'next'))
   {
      // This is a diff page. Do stuff!
      var added = guidebook_getLines('diff-addedline');
      var removed = guidebook_getLines('diff-deletedline');
      var summary = guidebook_getSummary();
      var guidelines = guidebook_lookupGuidelines(added, removed, summary);
      if (guidelines.length > 0)
      {
         var editSummary = document.getElementById('mw-diff-ntitle3');
         if (editSummary == undefined)
         {
            mw.log("Guidebook error: could not find edit summary block");
         }
 
         var block = new Element("div");
         block.setProperty('style', 'font-style: italic');
         block.appendText("Policies/Guidelines: ");
         for (var i = 0; i < guidelines.length; ++i)
         {
            if (i > 0)  
            {
               block.appendText(", ");
            }
 
            if (guidelines[i].href != undefined)
            {
               var link = new Element("a", {href: "/wiki/" + guidelines[i].href});
               link.appendText(guidelines[i].text);
               block.adopt(link);           
            }
            else 
            {
               block.appendText("...");
            }
         }

         editSummary.adopt(block);
      }
   }  
}

function guidebook_getUrlVars() 
{
    var vars = {};
    var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value)
    {
        vars[key] = value;
    });
    return vars;
}

/// Gets the lines that were added in the current diff.
/// Parameters: lookupClass [string] - a class for the td element to identify (e.g., 'diff-deletedline'
/// Preconditions: Current page is a diff
/// Postconditions: Nothing has changed
/// Return value: An array of strings, each line that was added in the diff
function guidebook_getLines(lookupClass)
{
   var content = document.getElementById('mw-content-text');
   if (content == undefined)
   {
      // Error: Could not get content text
      return;
   }

   var result = new Array();

   var diffTable = content.getElement('table');       // should always be the first table
   var diffTableBody = diffTable.getElement('tbody'); // a table BETTER only have one body
   var rows = diffTableBody.getElements('tr');
   for (var i = 0; i < rows.length; ++i)              // iterate over each row
   {
       var cols = rows[i].getElements('td');
       for (var j = 0; j < cols.length; ++j)          // iterate over each cell
       {
           // Look for an line (has class 'diff-addedline', etc.)
           if (cols[j].get('class') == lookupClass)
           {
              // This is an added line. Record it.
              var div = cols[j].getElement('div');    // There should only be one div inside
              if (div == undefined)
              {
                  mw.log("Guidebook warning: Could not find line with class '" + lookupClass + "'");
              }
              else 
              {
                 result.push(div.textContent);
              }
           }
       }
   }
   return result;
}

/// Gets the edit summary for the new revision
/// Preconditions: Current page is a diff
/// Postconditions: Nothing has changed
/// Return value: A string representation of the edit summary text
function guidebook_getSummary()
{
   var content = document.getElementById('mw-diff-ntitle3');
   if (content == undefined) return; // likely no edit summary
 
   var text = (content.getElementsByClassName('comment').length > 0 ? content.getElementsByClassName('comment')[0] : undefined);
   if (text == undefined) return; // likely no edit summary
   
   return text.textContent.slice(1, -1); // Remove the ( and ) from the edit summary text
}

function guidebook_lookupGuidelines(addedLines, removedLines, summary)
{
   var result = new Array();
   for (var i = 0; i < GUIDEBOOK_PATTERNS.length; ++i)
   {
      // Compile the patterns first to improve performance since it might do a lot of tests
      var modifiers = (GUIDEBOOK_PATTERNS[i].modifiers == undefined ? '' : GUIDEBOOK_PATTERNS[i].modifiers);

      // Each pattern defaults to '' if it is undefined. That allows us to "always match" so that we ignore that pattern.
      // Slightly slower in performance, but it really shouldn't be that big of a deal. It will look at the first character and finish.
      // The alternative was some ugly-looking code, and this is just too much more elegant to pass up.
      addedRegex = new RegExp(GUIDEBOOK_PATTERNS[i].added_regex == undefined ? '' : GUIDEBOOK_PATTERNS[i].added_regex, modifiers);
      removedRegex = new RegExp(GUIDEBOOK_PATTERNS[i].removed_regex == undefined ? '' : GUIDEBOOK_PATTERNS[i].removed_regex, modifiers);
      summaryRegex = new RegExp(GUIDEBOOK_PATTERNS[i].summary_regex == undefined ? '' : GUIDEBOOK_PATTERNS[i].summary_regex, modifiers);

      // Local function to test it against each added line
      function testLines(regex, lineArray)
      {
         if (lineArray.length == 0 && regex.test('')) return true; // No lines still gets a test against the empty string. If pass, successful.

         for (var j = 0; j < lineArray.length; ++j)
         {
            if (regex.test(lineArray[j]))
            {
               return true;
            }
         }
         return false;
      }
      
      if (testLines(addedRegex, addedLines) && testLines(removedRegex, removedLines) && testLines(summaryRegex, [summary]))
      {
         // Add to list of policies/guidelines to return
         result.push({
            href: GUIDEBOOK_PATTERNS[i].href,
            text: GUIDEBOOK_PATTERNS[i].text
         });
 
         if (result.length > GUIDEBOOK_MAXDISPLAYEDPOLICIES)
         {
            mw.log("Guidebook: Too many matched policies. Capping at " + GUIDEBOOK_MAXDISPLAYEDPOLICIES);
            result.pop();               // remove last (we found 1 too many so we know if we should put an ellipsis)
            result.push({text: "..."}); // ellipsis to indicate "there was more"
            return result;
         }
      }
   }

   return result;
}

addOnloadHook(function() { 
   guidebook_onLoadOccurred = true;
   if (mw.user.isAnon() == false)
   {
      jQuery.getScript('//en.wikipedia.org/w/index.php?title=User:' + mw.user.getName() + '/guidebook_prefs.js&action=raw&ctype=text/javascript')
         .done(function() { guidebook_configLoaded = true; guidebook_checkLoaded(); })
         .fail(function() { mw.log("Error loading Guidebook user preferences"); guidebook_loadDefaults(); guidebook_checkLoaded(); }); // Offer a default set of options if page doesn't exist
   }
   else 
   {
      guidebook_configLoaded = true;
   }
});