/*
*/

// ==UserScript==
// @name          Spanish accent toggling
// @description   For entering Spanish text on an English keyboard: press F2 to toggle previous character to an accented equivalent
// @include       *
// @exclude       
// ==/UserScript==

// Copyright 2007 Philip Dorrell
// $Revision: 1.1 $

// disable if there is a script element loading a file of this name (to avoid double response to F2)
// set to false if you *don't* want it disabled for this reason
var disableIfLoadedExplicitly = true;

logging=false;

function log(line) {}

var F2_code = 113;

var toggleLookup;

var maxAlertCount = 20;
var alertCount = 1;

function maxAlert(message) {
  if(alertCount < maxAlertCount) {
    alert (alertCount + ": " + message);
  }
  else if (alertCount == maxAlertCount) {
    alert ("MAX ALERTS REACHED: " + message);
  }
  alertCount++;
}

function iAmInGreaseMonkey() {
  try {
    return !(window instanceof Window);
  }
  catch(err) {
    return false;
  }
}

function hasToggleSpanishAccentScriptElement() {
  var scripts = document.getElementsByTagName ("script");
  for (var i=0; i<scripts.length; i++) {
    var script = scripts[i];
    if (script.src && script.src.match (/[/]toggle-spanish-accents.user.js$/)) {
      return true;
    }
  }
  return false;
}

function makeToggleLookup (cycles) {
  var lookup = [];
  for (var i=0; i<cycles.length; i++) {
    cycle = cycles[i];
    for (var j=0; j<cycle.length; j++) {
      var oldCode = cycle[j];
      var newCode = cycle[j < cycle.length-1 ? j+1 : 0];
      lookup[oldCode] = newCode;
    }
  }
  //log ("lookup = " + lookup);
  return lookup;
}

function nodeInserted(event) {
  var node = event.target;
  // maxAlert ("nodeInserted " + node);
  if (node.nodeName == "textarea" || (node.nodeName == "input" && node.type == "text")) {
    setupControl (node);
  }
}

function setupDocument(document) {
  try {
    document.onkeydown = spanishToggleKeyDownHandler;
  }
  catch (err) {
    document.addEventListener ("keydown", spanishToggleKeyDownHandler, false);
    // DOMNodeInsertedIntoDocument doesn't see to work for GMail
    // document.addEventListener ("DOMNodeInsertedIntoDocument", nodeInserted, true); 
  }
}

var elementWithFocus = null;

function blurHandler(event) {
  elementWithFocus = null;
}

function focusHandler(event) {
  if (!event) event = window.event;
  if (event.target)
    elementWithFocus = event.target;
  else if (event.srcElement) {
    elementWithFocus = event.srcElement;
  }
}


function setupControl(control) {
  try {
    control.onfocus = focusHandler;
    control.onblur = blurHandler;
  }
  catch (err) {
    control.removeEventListener ("focus", focusHandler, false);
    control.removeEventListener ("blur", focusHandler, false);
    control.addEventListener ("focus", focusHandler, false);
    control.addEventListener ("blur", blurHandler, false);
  }
}

function spanishToggleKeyDownHandler(event) {
  if (!event) event = window.event;
  if (event.keyCode == F2_code && !event.altKey && !event.ctrlKey) {
    toggleSpanishAccent(this);
    return false;
  }
  if (event.keyCode == F2_code && event.altKey && !event.ctrlKey) {
    setupInputFields(document);
    alert ("Text input fields were reset for Spanish accent toggling");
    return false;
    }
  return true;
}  

function toggleAccentOnChar(charBefore) {
  var oldCode = charBefore.charCodeAt(0);
  var newCode = toggleLookup[oldCode];
  if (newCode) {
    return String.fromCharCode(newCode);
  }
  else {
    return charBefore;
  }
}

function toggleSpanishAccent(document) {
  var useDocumentSelection = false;
  if (elementWithFocus) {
    if (elementWithFocus.selectionStart && elementWithFocus.selectionEnd) {
      if (elementWithFocus.selectionStart == elementWithFocus.selectionEnd) {
        if (elementWithFocus.selectionStart > 0) {
          var pos = elementWithFocus.selectionStart-1;
          var charBefore = elementWithFocus.value[pos];
          var newCharBefore = toggleAccentOnChar(charBefore);
          if (newCharBefore != charBefore) {
            elementWithFocus.value = elementWithFocus.value.substring(0, pos) + newCharBefore + 
                    elementWithFocus.value.substring(pos+1);
            elementWithFocus.selectionStart = pos+1;
            elementWithFocus.selectionEnd = pos+1;
          }
        }
      }
    }
    else {
      useDocumentSelection = true;
    }
  }
  else if (window.getSelection()) {
    var selection =  window.getSelection();
    if (selection.isCollapsed) {
      var anchorNode = selection.anchorNode;
      var pos = selection.anchorOffset;
      var anchorText = anchorNode.nodeValue;
      if (pos > 0) {
        anchorNode.nodeValue = anchorText.substring(0, pos-1) + toggleAccentOnChar(anchorText[pos-1]) + 
                anchorText.substring(pos);
        var range = document.createRange();
        range.setStart(anchorNode, pos);
        range.setEnd(anchorNode, pos);
        selection.removeAllRanges();
        selection.addRange (range);
      }
      else {
        log ("at start of text"); // todo: make this work on last char of prev node ?
      }
    }
  }
  else {
    useDocumentSelection = true;
  }
  
  if (useDocumentSelection && document.selection) {
    var range = document.selection.createRange();
    if (range && range.text == "") {
      range.moveStart ("character", -1);
      if (range.text.length == 1) {
        var charBefore = range.text;
        var newCharBefore = toggleAccentOnChar(charBefore);
        range.text = newCharBefore;
        range.moveStart("character", 1)
      }
    }
  }
}

function setupSpanishTogglingOnAllTextFields() {
  var toggleCycles = [
                      [65,193],
                      [97,225,170],
                      [69,201],
                      [101,233],
                      [73,205],
                      [105,237],
                      [78,209],
                      [110,241],
                      [79,211],
                      [111,243,186],
                      [85,218,220],
                      [117,250,252],
                      [63,191],
                      [33,161],
                      [36,8364],
                      [34,171,187],
                      [39,8249,8250]
  ];
  toggleLookup = makeToggleLookup(toggleCycles);
  
  setupDocument (document);

  setupInputFields(document);
}

function setupInputFields (node) {
  var textAreas = node.getElementsByTagName("textarea");
  for (var i=0; i<textAreas.length; i++) {
    setupControl (textAreas[i]);
  }
  var fields = node.getElementsByTagName("input");
  for (var i=0; i<fields.length; i++) {
    if (fields[i].type == "text") {
      setupControl (fields[i]);
    }
  }
}


if (!(disableIfLoadedExplicitly && iAmInGreaseMonkey() && hasToggleSpanishAccentScriptElement())) {
  setupSpanishTogglingOnAllTextFields();
}
