function FlyOutMenu(menuId) {
    var self = this;
    var wrapper = document.getElementById(menuId);
    this.removeClass(wrapper, 'css-fly-out-menu');
    this.menuRoot = this.firstChildByName(wrapper, 'ul');
    this.menuRoot.setAttribute('role','role:menu');
    this.currentItem = null;
    this.setEventListener(document, 'keydown', function(event) { self.keydown(event); }, true);
    this.setUpMenu(this.menuRoot, 1);
}

FlyOutMenu.prototype.setUpMenu = function(node, depth) {
    var self = this;
    var previousMenuItem = null;
    var menuItems = this.childNodesByName(node, 'li');
    node.firstMenuItem = menuItems[0];
    for(var i = 0; i < menuItems.length; i++){
        var menuItem = menuItems[i];
        menuItem.setAttribute('role','role:menuitem');
        this.setAaaAttribute('level', menuItem, depth);

        menuItem.previousMenuItem = previousMenuItem;
        if(previousMenuItem) {
            previousMenuItem.nextMenuItem = menuItem;
        }
        previousMenuItem = menuItem;

        this.setEventListener(menuItem, 'mouseover', function (event) {
                                  self.mouseover(event); }, true);
        this.setEventListener(menuItem, 'mouseout', function (event) {
                                  self.mouseout(event); }, true);
        this.setEventListener(menuItem, 'focus', function (event) {
                                  self.mouseover(event); }, true);
        this.setEventListener(menuItem, 'blur', function (event) {
                                  self.mouseout(event); }, true);
        var subMenu = this.firstChildByName(menuItem, 'ul');
        if(subMenu) {
            menuItem.subMenu = subMenu;
            this.setClass(menuItem, 'has-submenu');
            this.setClass(subMenu, 'vertical');
            subMenu.setAttribute('role','role:menu');
            this.setUpMenu(subMenu, depth+1);
        }
    }
}

FlyOutMenu.prototype.mouseover = function(event) {
    var target = this.getEventTarget(event, 'li');
    if(this.currentItem){
        this.blurElement(this.currentItem);
    }
    if(target.timeout) {
        clearTimeout(target.timeout);
    }
    this.setClass(target, 'focused');
    if(target.subMenu && !target.focused) {
        if(this.isClass(target.parentNode, 'horizontal')){
            this.setClass(target, 'open');
        } else {
            var pos = this.findRealPos(target);
            var width = window.innerWidth ? window.innerWidth : document.body.offsetWidth;
            if((pos[0] + (target.offsetWidth * 2)) > width){
                this.setClass(target, 'open-left');
            } else {
                this.setClass(target, 'open-right');
            }
        }
        target.subMenu.style.display = "block";
    }
    target.focused = true;
    var parent = target.parentNode.parentNode;
    if(parent.focusedChidren) {
        parent.focusedChidren += 1;
    } else {
        parent.focusedChidren = 1;
    }
    var siblings = this.childNodesByName(target.parentNode, 'li');
    for(var i = 0; i < siblings.length; i++){
        if(siblings[i] != target) {
            if(siblings[i].subMenu) {
                this.removeClass(siblings[i], 'open-left');
                this.removeClass(siblings[i], 'open-right')
                this.removeClass(siblings[i], 'open')
                siblings[i].subMenu.style.display = "none";
            }
            this.removeClass(siblings[i], 'focused');
            siblings[i].focused = false;
        }
    }
    if(parent.nodeName.toLowerCase() == 'li') {
        event.newTarget = parent;
        this.mouseover(event);
    }
    this.currentItem = target;
}

FlyOutMenu.prototype.mouseout = function(event) {
    var self = this;
    var target = this.getEventTarget(event, 'li');
    self.removeClass(target, 'focused');
    target.focused = false;

    var parent = target.parentNode.parentNode;
    if(target.timeout) {
        clearTimeout(target.timeout);
    }
	target.timeout = setTimeout(function() {
        if(target.subMenu && !target.focused && (!target.focusedChidren || target.focusedChidren < 1)) {
            target.focusedChidren = 0;
            self.removeClass(target, "open-left");
            self.removeClass(target, "open-right"); 
            self.removeClass(target, "open"); 
            target.subMenu.style.display = "none";
        }
    }, 500);
    if(parent.nodeName.toLowerCase() == 'li') {
        parent.focusedChidren -= 1;
        event.newTarget = parent
        this.mouseout(event);
    }
}

FlyOutMenu.prototype.findRealPos = function(obj) {
    var curleft = curtop = 0;
    if(obj.offsetParent) {
        curleft = obj.offsetLeft
        curtop = obj.offsetTop
        while (obj = obj.offsetParent) {
            curleft += obj.offsetLeft
            curtop += obj.offsetTop
        }
    }
    return [curleft, curtop];
}

FlyOutMenu.prototype.keydown = function(event) {
    var action = null;
    if(this.currentItem == null) {
        return
    } else if(this.isClass(this.currentItem.parentNode, 'horizontal')) {
        if(event.keyCode == 39) {   //LEFT
            action = "next";
        } else if(event.keyCode == 37) { //RIGHT
            action = "previous";
        } else if(event.keyCode == 38) { //UP
            action = "previous";
        } else if(event.keyCode == 40) { // DOWN
            action = "sub-menu";
        } else {
            return;
        }
    } else {
        if(event.keyCode == 39) {   //LEFT
            action = "sub-menu";
        } else if(event.keyCode == 37) { //RIGHT
            action = "previous";
        } else if(event.keyCode == 38) { //UP
            action = "previous";
        } else if(event.keyCode == 40) { // DOWN
            action = "next";
        } else {
            return;
        }
    }
    addMessage(action);
    if(action == "next") {
        if(this.currentItem.nextMenuItem) {
            this.focusElement(this.currentItem.nextMenuItem);
         }
    } else if(action == "sub-menu"){
        var subMenu = this.firstChildByName(this.currentItem, 'ul');
        if(subMenu){
             this.focusElement(subMenu.firstMenuItem);
            
        }
    } else if(action == "previous"){
        if(this.currentItem.previousMenuItem) {
            this.focusElement(this.currentItem.previousMenuItem);
        } else if(this.currentItem.parentNode.parentNode.nodeName.toLowerCase() == 'li') {
            this.focusElement(this.currentItem.parentNode.parentNode)
        }
    }
}

FlyOutMenu.prototype.focusElement = function(node) {
    var link = this.firstChildByName(node, 'a');
    if(link) {
        link.focus();
    } else {
        node.focus();
        var event = document.createEvent("HTMLEvents");
        event.initEvent("focus", true, true); 
        node.dispatchEvent(event);
    }
}

FlyOutMenu.prototype.blurElement = function(node) {
    var link = this.firstChildByName(node, 'a');
    if(link) {
        link.blur();
    } else {
        node.blur();
        var event = document.createEvent("HTMLEvents");
        event.initEvent("blur", true, true); 
        node.dispatchEvent(event);
    }
}

FlyOutMenu.prototype.removeClass = function(node, value) {
    if(node) {
        var currentValue = node.className;
        if(currentValue == null) {
            return;
        }
        node.className = currentValue.replace(new RegExp(value, 'gi'), '').replace(/ +/, ' ');
    }
};

FlyOutMenu.prototype.setClass = function(node, value){
    if(value != null){
        var currentValue = node.className;
        if(currentValue){
            node.className = currentValue + ' ' + value;
        } else {
            node.className = value;
        }
    }
}

FlyOutMenu.prototype.setEventListener = function(node, event, func, bool) {
    if(node.addEventListener){
        node.addEventListener(event, func, bool);
    } else {
        node.attachEvent('on'+event, func, bool);
    }
}

FlyOutMenu.prototype.childNodesByName = function(node, name) {
    var result = [];
    var childNodes = node.childNodes;
    var length = childNodes.length;
    for(var i = 0; i < length; i++) {
        if(childNodes[i].nodeName && childNodes[i].nodeName.toLowerCase() == name.toLowerCase()){
            result[result.length] = childNodes[i];
        }
    }
    return result;
}

FlyOutMenu.prototype.firstChildByName = function(node, name) {
    var childNodes = node.childNodes;
    var length = childNodes.length;
    for(var i = 0; i < length; i++) {
        if(childNodes[i].nodeName.toLowerCase() == name.toLowerCase()){
            return childNodes[i];
        }
    }
    return null;
}

FlyOutMenu.prototype.setAaaAttribute = function(attribute, node, value){
    if(node.setAttributeNS) {
        node.setAttributeNS("http://www.w3.org/2005/07/aaa", attribute, value);
    } else {
        node.setAttribute("aaa:" + attribute, value);
    }
    return true;
}

FlyOutMenu.prototype.isClass = function(node, value){
    var currentValue = node.className;
    if(currentValue == null){
        return false;
    }
    var classReg = new RegExp(value, 'gi');
    return currentValue.match(classReg);
}

FlyOutMenu.prototype.getEventTarget = function(event, name){
    if(event.newTarget) { // my own hack
        var target = event.newTarget; var type="new";
    } else if(event.target) {
        var target = event.target;var type="target";
    } else {
        var target = event.srcElement;var type="srcElement";
    }
    if(target != null && name != undefined){
        while(target && target.nodeName.toLowerCase() != name.toLowerCase()){
            target = target.parentNode;
        }
    }
    return target;
}
