/**
 * $Id: spmenu-kbd.js 4322 2006-09-04 08:49:33Z shacka $
 * @fileoverview Keyboard navigation module for southprojects DHTML Menu Widget.
 *
 * <pre>
 * Copyright (c) 2006 by SouthProjects
 * http://www.southprojects.com
 * Santiago, Chile,
 * Version 1.2 Dec 2006.
 * All rights reserved.
 * </pre>
 */

/**
 * Provides keyboard navigation and shortcuts. Sould be assigned to
 * window.document keydown event.
 *
 * @private
 * @param {object} ev Event object
 */
southprojects.Menu.onDocumentKeyDown = function(ev) {
  ev || (ev = window.event);
  // Skip Shift, Ctrl and Alt buttons because they are used only as modifiers
  if (ev.keyCode == 16 || ev.keyCode == 17 || ev.keyCode == 18) {
    return true;
  }
  var item = southprojects.Menu.selectedItemsStack.pop();
  var self = null, subtree = null;
  if (item) {
    self = southprojects.Menu.all[item.__zp_tree];
    subtree = self._getTree(item.__zp_parent);
  }

  var buttonEsc = function(item) {
    if (!item) {
      return true; // Pass through
    }
    // Hide subtree
    if (item.__zp_subtree && item.__zp_state) {
      // Hide current item child subtree
      self.itemHide(item.__zp_item);
      southprojects.Menu.selectedItemsStack.push(item);
      subtree.__zp_activeitem = item.__zp_item;
    } else {
      // Unselect current item
      southprojects.Utils.removeClass(item, "spmenu-item-selected");
      southprojects.Utils.removeClass(item, "spmenu-item-selected-collapsed");
      if (subtree.__zp_activeitem == item.__zp_item) subtree.__zp_activeitem = '';
      // Hide current subtree
      if (subtree.__zp_item) {
        var parentItem = self.items[subtree.__zp_item];
        if (parentItem.__zp_state) {
          self.itemHide(parentItem.__zp_item);
        }
      }
    }
    // Hide menu
    if (self.config.triggerEvent) {
      for (var i = 0; i < southprojects.Menu.selectedItemsStack.length; i++) {
        if (southprojects.Menu.all[southprojects.Menu.selectedItemsStack[i].__zp_tree] == self) {
          return southprojects.Utils.stopEvent(ev);
        }
      }
      // No more selected items in this menu
      self.hideMenu();
    }
    return southprojects.Utils.stopEvent(ev);
  };

  var buttonEnter = function(item) {
    if (!item) {
      return true; // Pass through
    }
    southprojects.Menu.selectedItemsStack.push(item);
    item.onclick();
    return southprojects.Utils.stopEvent(ev);
  };

  var buttonLeftArrow = function(item) {
    if (!item) {
      return true; // Pass through
    }
    if (!self.config.vertical && /spmenu-level-1/.test(item.className)) {
      // Horizontal top menu item
      var prevItem = item.previousSibling;
      if (!prevItem) {
        prevItem = item.parentNode.lastChild;
      }
      if (prevItem) {
        if (item.__zp_state) {
          item.onmouseout();
          prevItem.onmouseover();
        } else {
          southprojects.Menu.unselectItem(item);
          southprojects.Menu.selectItem(prevItem);
          subtree.__zp_activeitem = prevItem.__zp_item;
        }
      } else {
        southprojects.Menu.selectedItemsStack.push(item);
      }
    } else {
      // Vertical menu item
      if (!/spmenu-level-1/.test(item.className)) {
        // Hide subtree
        return buttonEsc(item);
      } else {
        southprojects.Menu.selectedItemsStack.push(item);
      }
    }
    return southprojects.Utils.stopEvent(ev);
  };

  var buttonRightArrow = function(item) {
    if (!item) {
      return true; // Pass through
    }
    if (!self.config.vertical && /spmenu-level-1/.test(item.className)) {
      // Horizontal top menu item
      var nextItem = item.nextSibling;
      if (!nextItem) {
        nextItem = item.parentNode.firstChild;
      }
      if (nextItem) {
        if (item.__zp_state) {
          item.onmouseout();
          nextItem.onmouseover();
        } else {
          southprojects.Menu.unselectItem(item);
          southprojects.Menu.selectItem(nextItem);
          subtree.__zp_activeitem = nextItem.__zp_item;
        }
      } else {
        southprojects.Menu.selectedItemsStack.push(item);
      }
    } else {
      // Vertical menu item
      if (item.__zp_subtree) {
        // Show subtree
        southprojects.Menu.selectedItemsStack.push(item);
        self.itemShow(item.__zp_item);
        var subMenu = self._getTree(item.__zp_subtree);
        if (subMenu.__zp_items.length > 0) {
          if (subMenu.__zp_activeitem) {
            southprojects.Menu.unselectItem(self.items[subMenu.__zp_activeitem]);
          }
          subMenu.__zp_activeitem = subMenu.__zp_items[0];
          southprojects.Menu.selectItem(self.items[subMenu.__zp_activeitem]);
        }
      } else if (!self.config.vertical && !/spmenu-level-1/.test(item.className)) {
        // Go to next top menu item
        item.onmouseout();
        return buttonRightArrow(self.items[self._getTree(item.__zp_tree).__zp_activeitem]);
      } else {
        southprojects.Menu.selectedItemsStack.push(item);
      }
    }
    return southprojects.Utils.stopEvent(ev);
  };

  var buttonUpArrow = function(item) {
    if (!item) {
      return true; // Pass through
    }
    if (!self.config.vertical && /spmenu-level-1/.test(item.className)) {
      // Horizontal top menu item
      // Show subtree
      return buttonDownArrow(item);
    } else {
      // Vertical menu item
      var prevItem = item.previousSibling;
      while (prevItem && (!/spmenu-item/i.test(prevItem.className) || /spmenu-item-hr/i.test(prevItem.className))) {
        prevItem = prevItem.previousSibling;
      }
      if (!prevItem) {
        prevItem = item.parentNode.lastChild;
      }
      if (prevItem) {
        if (item.__zp_state) {
          item.onmouseout();
          prevItem.onmouseover();
        } else {
          southprojects.Menu.unselectItem(item);
          southprojects.Menu.selectItem(prevItem);
          subtree.__zp_activeitem = prevItem.__zp_item;
        }
      } else {
        southprojects.Menu.selectedItemsStack.push(item);
      }
    }
    return southprojects.Utils.stopEvent(ev);
  };

  var buttonDownArrow = function(item) {
    if (!item) {
      return true; // Pass through
    }
    if (!self.config.vertical && /spmenu-level-1/.test(item.className)) {
      // Horizontal top menu item
      southprojects.Menu.selectedItemsStack.push(item);
      if (item.__zp_subtree) {
        // Show subtree
        self.itemShow(item.__zp_item);
        var subMenu = self._getTree(item.__zp_subtree);
        if (subMenu.__zp_items.length > 0) {
          if (subMenu.__zp_activeitem) {
            southprojects.Menu.unselectItem(self.items[subMenu.__zp_activeitem]);
          }
          subMenu.__zp_activeitem = subMenu.__zp_items[0];
          southprojects.Menu.selectItem(self.items[subMenu.__zp_activeitem]);
        }
      }
    } else {
      // Vertical menu item
      var nextItem = item.nextSibling;
      while (nextItem && (!/spmenu-item/i.test(nextItem.className) || /spmenu-item-hr/i.test(nextItem.className))) {
        nextItem = nextItem.nextSibling;
      }
      if (!nextItem) {
        nextItem = item.parentNode.firstChild;
      }
      if (nextItem) {
        if (item.__zp_state) {
          item.onmouseout();
          nextItem.onmouseover();
        } else {
          southprojects.Menu.unselectItem(item);
          southprojects.Menu.selectItem(nextItem);
          subtree.__zp_activeitem = nextItem.__zp_item;
        }
      } else {
        southprojects.Menu.selectedItemsStack.push(item);
      }
    }
    return southprojects.Utils.stopEvent(ev);
  };

  var altShortcut = function() {
    if (subtree && subtree.__zp_keymap) {
      var keymapItem = subtree.__zp_keymap[ev.keyCode];
      if (keymapItem) {
        // Unselect current item
        if (item && !item.__zp_state) {
          southprojects.Utils.removeClass(item, "spmenu-item-selected");
          southprojects.Utils.removeClass(item, "spmenu-item-selected-collapsed");
          if (subtree.__zp_activeitem == item.__zp_item) subtree.__zp_activeitem = '';
        }
        // As onItemClick shows submenu with delay, we have to
        // show submenu immediately to be able to set active item
        self.itemShow(keymapItem.__zp_item);
        // Simulate click
        keymapItem.onclick();
        // Set active item in submenu
        if (keymapItem.__zp_subtree) {
          var subMenu = self._getTree(keymapItem.__zp_subtree);
          if (subMenu.__zp_items.length > 0) {
            if (subMenu.__zp_activeitem) {
              southprojects.Menu.unselectItem(self.items[subMenu.__zp_activeitem]);
            }
            subMenu.__zp_activeitem = subMenu.__zp_items[0];
            southprojects.Menu.selectItem(self.items[subMenu.__zp_activeitem]);
          }
        }
        return southprojects.Utils.stopEvent(ev);
      }
    }
    return true; // Pass through
  };

  var shortcut = function() {
    if (subtree) { // Alt modifier is not required if there is active item
      if (item.__zp_state) {
        southprojects.Menu.selectedItemsStack.push(item);
        subtree = self._getTree(item.__zp_subtree);
      }
      var ret = altShortcut();
      if (!ret) return ret;
    } else if (ev.altKey) { // Alt modifier is required
      for (var i in southprojects.Menu.all) {
        self = southprojects.Menu.all[i];
        subtree = self.top_parent;
        var ret = altShortcut();
        if (!ret) return ret;
      }
    }
    southprojects.Menu.selectedItemsStack.push(item);
    return true; // Pass through
  };

  switch (ev.keyCode) {
    case 27: // Esc
      return buttonEsc(item);
      break;
    case 13: // Enter
    case 32: // Space
      return buttonEnter(item);
      break;
    case 37: // Left arrow
      return buttonLeftArrow(item);
      break;
    case 39: // Right arrow
      return buttonRightArrow(item);
      break;
    case 38: // Up arrow
      return buttonUpArrow(item);
      break;
    case 40: // Down arrow
      return buttonDownArrow(item);
      break;
    default:
      return shortcut();
  }
};

// Setup keyboard navigation and shortcuts
southprojects.Utils.addEvent(window.document, 'keydown', southprojects.Menu.onDocumentKeyDown);

// Disable scrolling with arrows
southprojects.Utils.addEvent(window.document, 'keypress',
  function(ev) {
    ev || (ev = window.event);
    switch (ev.keyCode) {
      case 37: // Left arrow
      case 39: // Right arrow
      case 38: // Up arrow
      case 40: // Down arrow
        return southprojects.Utils.stopEvent(ev);
        break;
    }
  }
);
