Special Signs Helper - Forget The Key Codes!

By Motty Katan Last update Oct 15, 2007 — Installed 1,562 times. Daily Installs: 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0
//#special_signs_helpy.user.js
// ==UserScript==
// @name          Special Signs Helper - Forget The Key Codes!
// @author        Motty Katan
// @namespace     http://moppy.4free.co.il
// @description	  Special signs are hard to remember, when writing in a foreign lang or on a different comp. Select the letter(s) and use arrow keys/wheel to switch between available options(shift to change case) Motty Katan(c) 29-09-07 updated at 15-10-07  
// @include       http://*
// ==/UserScript==
// Build Changes:
// 29-09-2007 now supports more than one character(case and dictionary/class). added french numbers to dictionary.
// 30-09-2007 Support of mouse wheel. Better framework - now supports capital letters in a better neater way. Vocabulary: Added Harry Potter Houses, at=@, (c),(r),tm,1/4,1/2,3/4,oe,ae
// 31-09-2007 better getUnicodeChar function. 
// 04-10-2007 Shift key has other function when you select text, switching to ctrl(when shift isn't click). Load DB only if needed. Added: o,u,i
// 05-10-2007 Fixed the letter "o", added "i"
// 15-10-2007 Fixed non issue with scrool bars jumping.

//it seems that String.fromCharCode is not relaiable
var oText = document.createElement("DIV");
function getUnicodeChar(nCode){
  oText.innerHTML = "&#"+nCode+";";
  return oText.innerHTML; 
}

//array of all characters supported (key=in miniscule)
var aChars = new Array();
//classes contains object stored in the aChars
//to be able to loop per character using the arrows
//key=in miniscule
var aClass = new Array(new Array());

//add a character to characters and character class arrays
function addChar(sMiniscule, sClass, bSkipCapital){
  function getCapital(sString){
    return sString.charAt(0).toUpperCase()+sString.substring(1);
  }
  if (!aClass[sClass]){
    aClass[sClass] = new Array();
  }
  var nPosition  = aClass[sClass].length;
  var sMajuscule = sMiniscule.toUpperCase();
  var sCapital   = (bSkipCapital) ? "":getCapital(sMiniscule);
  aChars[sMiniscule] = {sMiniscule:sMiniscule, sMajuscule:sMajuscule, sCapital:sCapital, sClass:sClass, nPos:nPosition, bIsMiniscule:true};
  aClass[sClass][nPosition] = aChars[sMiniscule];
}

/*               SET DATA                   */
function setData(){
  
  /****** e class ********/
  addChar("e","e");
  //grave
  addChar(getUnicodeChar(232),"e");
  //acute
  addChar(getUnicodeChar(233),"e");
  //circomflex
  addChar(getUnicodeChar(234),"e");
  //diaeresis
  addChar(getUnicodeChar(235),"e");
  //macron
  addChar(getUnicodeChar(275),"e");
  //breve
  addChar(getUnicodeChar(277),"e");
  //dot
  addChar(getUnicodeChar(279),"e");
  //ogonek
  addChar(getUnicodeChar(281),"e");
  //caron
  addChar(getUnicodeChar(283),"e");

  /****** i class ********/
  addChar("i","i");
  //grave
  addChar(getUnicodeChar(236),"i");
  //acute
  addChar(getUnicodeChar(237),"i");
  //circomflex
  addChar(getUnicodeChar(238),"i");
  //diaeresis
  addChar(getUnicodeChar(239),"i");
  //tilde
  addChar(getUnicodeChar(297),"i");
  //macron
  addChar(getUnicodeChar(299),"i");
  //breve
  addChar(getUnicodeChar(301),"i");
  //ogonek
  addChar(getUnicodeChar(303),"i");
  //dotless
  addChar(getUnicodeChar(305),"i"); 
  
  /****** o class ********/
  addChar("o","o");
  //grave
  addChar(getUnicodeChar(242),"o");
  //acute
  addChar(getUnicodeChar(243),"o");
  //circomflex
  addChar(getUnicodeChar(244),"o");
  //tilde
  addChar(getUnicodeChar(245),"o");
  //diaeresis
  addChar(getUnicodeChar(246),"o");
  //small letter eth
  addChar(getUnicodeChar(240),"o");
  //macron
  addChar(getUnicodeChar(333),"o");
  //breve
  addChar(getUnicodeChar(335),"o");
  //double acute
  addChar(getUnicodeChar(337),"o");
  
  /****** a class ********/
  addChar("a","a");
  //grave
  addChar(getUnicodeChar(224),"a");
  //acute
  addChar(getUnicodeChar(225),"a");
  //circomflex
  addChar(getUnicodeChar(226),"a");
  //tilde
  addChar(getUnicodeChar(227),"a");
  //diaeresis
  addChar(getUnicodeChar(228),"a");
  //ring above
  addChar(getUnicodeChar(229),"a");
  //macron
  addChar(getUnicodeChar(257),"a");
  //breve
  addChar(getUnicodeChar(259),"a");
  //ogonek
  addChar(getUnicodeChar(261),"a");
  //ring and acute
  addChar(getUnicodeChar(507),"a");
  
  
  /****** u class ********/
  addChar("u","u");
  //grave
  addChar(getUnicodeChar(249),"u");
  //acute
  addChar(getUnicodeChar(250),"u");
  //circomflex
  addChar(getUnicodeChar(251),"u");
  //diaeresis
  addChar(getUnicodeChar(252),"u");
  //tilda
  addChar(getUnicodeChar(361),"u");
  //macron
  addChar(getUnicodeChar(363),"u");
  //breve
  addChar(getUnicodeChar(365),"u");
  //ring above
  addChar(getUnicodeChar(367),"u");
  //double acute
  addChar(getUnicodeChar(369),"u");  
  //ogonek
  addChar(getUnicodeChar(371),"u");  
  //horn
  addChar(getUnicodeChar(432),"u");
  //caron
  addChar(getUnicodeChar(468),"u");  
  //diaeresis and macron
  addChar(getUnicodeChar(470),"u");  
  //diaeresis and acute
  addChar(getUnicodeChar(472),"u");  
  //diaeresis and caron
  addChar(getUnicodeChar(474),"u");  
  //diaeresis and grave
  addChar(getUnicodeChar(476),"u");  
    
    
  
  
  
  /****** c class ********/
  addChar("c","c");
  //ccedil
  addChar(getUnicodeChar(231),"c");
  //acute
  addChar(getUnicodeChar(263),"c");
  //circomflex
  addChar(getUnicodeChar(265),"c");
  //weird dot above
  addChar(getUnicodeChar(267),"c");
  //caron
  addChar(getUnicodeChar(269),"c");

  /******* counting in french class ********/
  addChar("z"+getUnicodeChar(233)+"ro","counting_in_french");
  addChar("un","counting_in_french");
  addChar("deux","counting_in_french");
  addChar("trois","counting_in_french");
  addChar("quatre","counting_in_french");
  addChar("cinq","counting_in_french");
  addChar("six","counting_in_french");
  addChar("sept","counting_in_french");
  addChar("huit","counting_in_french");
  addChar("neuf","counting_in_french");
  addChar("dix","counting_in_french");
  
  /******* Harry Potter: Hogwarts Houses in english class ********/
  addChar("gryffindor","Hogwarts_Houses");
  addChar("slytherin","Hogwarts_Houses");
  addChar("ravenclaw","Hogwarts_Houses");
  addChar("hufflepuff","Hogwarts_Houses");      

  /******* at sign = @ ********/ 
  addChar("at","@_sign");
  addChar("@","@_sign");
  
  /******* (c) = � ********/ 
  addChar("(c)","copyright_sign");
  addChar(getUnicodeChar(169),"copyright_sign"); 

  /******* (r) = � ********/ 
  addChar("(r)","register_sign");
  addChar(getUnicodeChar(174),"register_sign"); 
  
  /******* TM = � ********/ 
  addChar("tm","trademark_sign");
  addChar(getUnicodeChar(153),"trademark_sign");

  /******* 1/4 ********/ 
  addChar("1/4","quarter");
  addChar(getUnicodeChar(188),"quarter");

  /******* 1/2 ********/ 
  addChar("1/2","half");
  addChar(getUnicodeChar(189),"half");

  /******* 3/4 ********/ 
  addChar("3/4","three_quarters");
  addChar(getUnicodeChar(190),"three_quarters");

  /******* oe ********/ 
  addChar("oe","oe");
  addChar(getUnicodeChar(339),"oe");
  
  /******* ae ********/ 
  addChar("ae","ae");
  addChar(getUnicodeChar(230),"ae");
  
  /******* ?=� ********/ 
  addChar("?","?");
  addChar(getUnicodeChar(191),"?");  

  /******* ... chinese/other ********/ 
  addChar("...","...");
  addChar(getUnicodeChar(133),"...");

  /******* Currency sign: Euro ********/ 
  addChar("eur","euro");
  addChar(getUnicodeChar(128),"euro");

  /******* Currency sign: Shekel ********/ 
  addChar("nis","shekel");
  
  addChar(getUnicodeChar(8362),"shekel");
  addChar("ils","shekel");  
    
    
  
  
    
     
}
/*       End  of  SET DATA                   */


function nextChar(sValue, nPosition, nPositionEnd, nDir){
  var sSelectedString = sValue.substring(nPosition,nPositionEnd);
  var oChar = aChars[sSelectedString.toLowerCase()];
  var nNewPos = nPos = oChar.nPos;
  if (aClass[oChar.sClass].length>0){
    //regular state
    nNewPos += nDir;
    //corner state - go to start/end   
    if ((aClass[oChar.sClass].length-1)< nNewPos || nNewPos==-1){
      nNewPos = Math.abs(nPos - (aClass[oChar.sClass].length-1));
    }
    var sChar;
    switch(sSelectedString){
      case oChar.sMiniscule:
        sChar = aClass[oChar.sClass][nNewPos].sMiniscule;
        break;
      case oChar.sMajuscule:
        sChar = aClass[oChar.sClass][nNewPos].sMajuscule;
        break;
      case oChar.sCapital:
        sChar = aClass[oChar.sClass][nNewPos].sCapital;
        break;      
    }
    //var sChar = (oChar.bIsMiniscule) ? aClass[oChar.sClass][nNewPos].sReplacer.toLowerCase():aClass[oChar.sClass][nNewPos].sReplacer;    
    sValue = swapChar(sValue, nPosition, sChar, nPositionEnd - nPosition);
  }
  return sValue;
}

var g_nLastReplacementLen=0;

function swapChar(sValue, nPos, sReplacer, nReplacedStringLen){
  g_nLastReplacementLen = sReplacer.length; 
  return sValue.substring(0,nPos)+sReplacer+sValue.substring(nPos+nReplacedStringLen,sValue.length);
}

function checkKeyPress(oEvent){
  var oElement = oEvent["target"];//oEvent["currentTarget"];
  var sValue = oElement.value;
	var nSelStart = oElement.selectionStart;
	var nSelEnd = oElement.selectionEnd;
  var sSelectedString = sValue.substring(nSelStart, nSelEnd);
  var bUpdated = false; 
  var sChar;	
	//the selection is only on one character and the key that was pressed was
	//up/down arrow or mouse wheel
	if (!oEvent.shiftKey && (nSelEnd-nSelStart>=1) && (oEvent.detail!==0 || oEvent.which==oEvent.DOM_VK_DOWN || oEvent.which==oEvent.DOM_VK_UP)){
	  //switch to lowercase/upercase using shift+arrow (mouse wheel+shift is a danger)
    if (oEvent.ctrlKey){
      if (aChars[sSelectedString.toLowerCase()]){        
        if (sSelectedString==aChars[sSelectedString.toLowerCase()].sMiniscule){
            sChar = aChars[sSelectedString.toLowerCase()].sMajuscule;
        }else{
            sChar = aChars[sSelectedString.toLowerCase()].sMiniscule;
        }     
        sValue = swapChar(sValue, nSelStart, sChar, sSelectedString.length);
      }else{
        //no object exist use ugly way to swap to lower/upper according to the
        //current char state
        if (sSelectedString == sSelectedString.toLowerCase()){
          sValue = swapChar(sValue,nSelStart, sSelectedString.toUpperCase(), sSelectedString.length);
        }else{
          sValue = swapChar(sValue,nSelStart, sSelectedString.toLowerCase(), sSelectedString.length);
        }
      }
      bUpdated = true;
    }else{
      
      //do we have any data about this character?
      if (aChars[sSelectedString.toLowerCase()]){
        //decide direction according to key pressed
        var nDir = (oEvent.which==oEvent.DOM_VK_DOWN || oEvent.detail>0) ? -1:1;
        //navigate inside character class      
        sValue = nextChar(sValue, nSelStart, nSelEnd, nDir);
        //oElement.disabled = 0;
        bUpdated = true;
      }
    }
    if (bUpdated){
      var sScrollTop          = oElement.scrollTop;
      var sScrollLeft         = oElement.scrollLeft;
      //setting the value might alter the scrollbar position fix it right after to what it was      
      oElement.value          = sValue;
      oElement.scrollTop      = sScrollTop;
      oElement.scrollLeft     = sScrollLeft; 
      oElement.selectionStart = nSelStart;
      oElement.selectionEnd   = nSelStart + g_nLastReplacementLen;
    }  	  
	  //finally prevent default
	  oEvent.preventDefault();
  }  
}
//No contenteditable in mozilla handle only textarea and input
var aResultLinks = document.evaluate( "//body//*[(self::textarea or self::input) and not(@UNSELECTABLE='true') and not(@READONLY='true')]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
var oElement;
if (aResultLinks.snapshotLength){
  //load data only if needed
  setData();
}
for (var n = 0; n < aResultLinks.snapshotLength; n++)
{
  oElement = aResultLinks.snapshotItem(n);
  //attaching 
  oElement.addEventListener("keydown", checkKeyPress, false);
  oElement.addEventListener('DOMMouseScroll', checkKeyPress, false);
}