/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mouse Gesture for Mozilla.
 *
 * The Initial Developer of the Original Code is Pavol Vaskovic.
 * Portions created by the Initial Developer are Copyright (C) 2001
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *  Andy Edmonds <aedmonds@mindspring.com>
 *  David Illsley <illsleydc@bigfoot.com>
 *  HJ van Rantwijk <bugs4HJ@netscape.net>
 *  Pavol Vaskovic <pali@pali.sk>
 *  Scott R. Turner <srt@aero.org>
 *  Tim Williamson <chsman@hotmail.com>
 * 	Martin.T.Kutschker <Martin.T.Kutschker@blackbox.net>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

// Global variables 
// mouse gesture condition (to be evaluated on event) for more details see mozgestReadPref()  
var condition = new String;
var grid; // grid size; minimal gesture has to be 'grid' pixels long
var delay; // delay before aborting gesture
var gestureInProgress = false;
// old coordinates from previous gesture stroke used in processCoordinates()
var old_x;
var old_y;
var gesture = new Array();
var localizedGesture = new Array(); var R,L,D,U;
var lastgesture;
// lastGestureTime is time of last gesture stroke, compared with delay and current time
var lastGestureTime;
// globalOnLink is an array that holds a list of all the links traversed during gesture
// globalOnImage is a string containing an image href or false
// globalSrcEvent stores event which started the gesture the active gesture.
// The links are used in the gesture functions in gesture.js.
var globalOnLink = false;
var globalOnImage = false;
var globalSrcEvent = false;
var inContent = false;
var bundle = null; // String bundle for localized strings 
var disableContextMenu = false;

// Preferences observer object; implements nsIObserver
function mgPrefObserver()
{
  try {
    var pbi = pref.QueryInterface(Components.interfaces.nsIPrefBranchInternal);
    pbi.addObserver(this.domain, this, false);
  } catch(ex) {
    dump("MozGest: Failed to observe prefs: " + ex + "\n");
  }
}
mgPrefObserver.prototype =
{
  domain: "mozgest.",
  observe: function(subject, topic, prefName)
  {
    // when MozGest pref was changed, we reinitialize 
    try{
      mozgestInit();
    } catch(err){}
  }
}  

// This code is performed on load:
// get the preferences service
var prefService = Components.classes["@mozilla.org/preferences-service;1"]
                            .getService(Components.interfaces.nsIPrefService);
var pref = prefService.getBranch(null); // preferences root node
var mgPref = prefService.getBranch("mozgest."); // preferences mozgest node
window.mozgestPrefObserver = new mgPrefObserver(); // and set an Pref observer
// END of on load performed code

function mozgestDisableContextMenu(aEvent) {
  if (disableContextMenu) {
    aEvent.preventDefault();
  }
  disableContextMenu = false;
}

function mozgestInit() { // load preferences into JS variables, or restore default prefs if none are found
  dump("MozGest: init\n");
  window.removeEventListener("load", mozgestInit, true);

  // read prefs or set Defaults on the first run
  try {
    mozgestReadPref();  
  }
  catch(e) {
    dump("MozGest: Setting Default Preferences\n");
    mgPref.setBoolPref("navigator", true);
    mgPref.setBoolPref("modifier.ctrl", false);
    mgPref.setBoolPref("modifier.alt", false);
    mgPref.setBoolPref("modifier.shift", false);
    mgPref.setIntPref("mousebutton", 0);
    mgPref.setIntPref("grid", 15);
    mgPref.setIntPref("delay", 500);
    mozgestReadPref();
  }
  
  // load localized string bundle & init dependand Gesture Table 
  bundle = document.getElementById("mozgestbundle");
  R = bundle.getString("abbreviation.right");
  L = bundle.getString("abbreviation.left");
  U = bundle.getString("abbreviation.up");
  D = bundle.getString("abbreviation.down");
  initGestureTable();

  // context menu disabler for right mouse button
  var contentAreaContextMenu = document.getElementById("contentAreaContextMenu");
  if (contentAreaContextMenu)
    contentAreaContextMenu.addEventListener("popupshowing", mozgestDisableContextMenu, false);
}

function processCoordinates(e){ // e is browser's event object
  var x_dir = parseInt((e.clientX-old_x)/grid);
  var y_dir = parseInt((e.clientY-old_y)/grid);

  //only add if movement enough to make a gesture
  if ((x_dir != 0) || (y_dir != 0))
  {
    var last_dir = gesture[gesture.length-1];
    if (x_dir > 0 && last_dir != "R"){
      gesture.push("R"); localizedGesture.push(R); }
    else if (x_dir < 0 && last_dir != "L"){
      gesture.push("L"); localizedGesture.push(L); }
    else if (y_dir > 0 && last_dir != "D"){
      gesture.push("D"); localizedGesture.push(D); }
    else if (y_dir < 0 && last_dir != "U"){
      gesture.push("U"); localizedGesture.push(U); }
    old_x = e.clientX;
    old_y = e.clientY;
    window.status = bundle.getString("g.gesture") + " " + localizedGesture;
    lastGestureTime = e.timeStamp; 
  }

  mozgestHandleLink(e);
}

function mozgestHandleLink(e){ // add new traversed link to globalOnLink array 
  var aHref = findLink(e.target); 
  if(aHref) {
    if(!globalOnLink) globalOnLink = new Array();
    if(!mozgestCheckLinkDups(aHref)){
      dump("Mozgest: adding new link to globalOnLink\n");
      globalOnLink.push(aHref);
   }
  }
}

// Recruited this function to find images, AE 11/15/01
// It loops up the DOM looking for a link and returns the A node
function findLink(domPosition) { // traverses DOM, searches for links
  //dump("walking dom" + domPosition.nodeName + " \n");
  if (domPosition == window._content.document) return false;
  try { // This is needed when user drags outside of content area,
        // because then we run out of tree up to null - 1st 'if' is never true. 
     if(domPosition.nodeName == "IMG" && globalOnImage == false){
      globalOnImage = domPosition;
    	  dump("Image found " + domPosition.src + "  \n");
     }
    if(domPosition.nodeName == "A") return domPosition.href;
    else return findLink(domPosition.parentNode);
  }
  catch(e) {return false;}
}

function mozgestCheckLinkDups(addy) { // avoid duplicates in globalOnLink array
  var isADup = false;
  for(var zz = 0; zz < globalOnLink.length; zz++) {
    if(globalOnLink[zz] == addy) isADup = true;
  }
  return isADup;
}

// startGesture is called from event listener in bubble phase,
// it relays on mozgestIsInContent which is called in capture phase
// to determine if gesture was started in the right place 
function startGesture(e){
 if (inContent){
  if (eval(condition)){ // is condition (mouse button, modifiers) for gesture met?
    globalSrcEvent = e;

    // DO NOT start gesture on scrollbars and input elements etc.
    var tag = e.target.nodeName.toLowerCase();
    // textarea is a bit tricky... #text is in 'div' which is in 'textarea'
    // when there's no error, we end with textarea or something harmless
    if (tag == "#text") { 
      try { tag = e.target.parentNode.parentNode.nodeName.toLowerCase(); }
      catch (err) {}
    }
    var xtag;
    try { xtag = e.originalTarget.localName.toLowerCase(); } catch (err) {}

    if (xtag != "slider" && xtag != "thumb" && xtag != "scrollbarbutton" &&
        tag != "input" && tag != "select" && tag != "textarea") {
      gestureInProgress = true;
      old_x = e.clientX;
      old_y = e.clientY;
      mozgestHandleLink(e);  // I could have started over link, let's check!
      gesture.length = 0; // clear gesture array
      localizedGesture.length = 0; // clear localized gesture array
      window.addEventListener("mousemove", processCoordinates, false);
    }
  }
  inContent = false;
 }
}
    
function endGesture(e){
  if (gestureInProgress) {
    window.removeEventListener("mousemove", processCoordinates, false);
    lastgesture = gesture.join("");
    if (lastgesture != ""){
      // abort gesture if user pauses in the end of gesture
      if ((e.timeStamp - lastGestureTime) < delay) {
        /* we should stop default actions here (popup menus, text selecting)
         but I think that current Mozilla implementation does those things
         internaly, so we can not stop them from DOM. */
        e.preventDefault();
        e.stopPropagation();
  	
        // Disable context menu but only if the right mouse button
  	    // is configured as the trigger
        if(mgPref.getIntPref("mousebutton") == 2) disableContextMenu = true;
        
        fireGesture(lastgesture);
      }
      else window.status = bundle.getString("g.aborted");
    }
    else dump("MozGest: Gesture was too small\n");
    gestureInProgress = false;
    globalOnLink = false;
    globalOnImage = false;
    globalSrcEvent = false;
  } 
}

function mozgestReadPref(){ // read settings from Preferences interface

  grid = mgPref.getIntPref("grid");
  delay = mgPref.getIntPref("delay");

  // condition is a boolean expression consisting of event object values
  condition = "e.button==" + mgPref.getIntPref("mousebutton");
  if (mgPref.getBoolPref("modifier.ctrl"))
    condition += " && e.ctrlKey";
  if (mgPref.getBoolPref("modifier.alt"))
    condition += " && e.altKey";  
  if (mgPref.getBoolPref("modifier.shift"))
    condition += " && e.shiftKey";

  // turn gestures recognition on/off
  if (mgPref.getBoolPref("navigator")) addWindowWatch();
  else removeWindowWatch();
}

function addWindowWatch() {
  window.addEventListener("mousedown", startGesture, false);
  window.addEventListener("mouseup", endGesture, false);
  dump("MozGest: ---- Adding Window Listener ---\n");
}

function removeWindowWatch() {
  window.removeEventListener("mousedown", startGesture, false);
  window.removeEventListener("mouseup", endGesture, false);
  dump("MozGest: ---- Removing Listener ---\n");
}

// mozgestIsInContent is called from event listener in capture phase
// when the event has occured in content area
function mozgestIsInContent(){
  inContent = true;
}