/*
 * Copyright (c) 2007, David A. Lindquist (stringify.com)
 * Some Rights Reserved
 *
 * This code is licensed under the Creative Commons Attribution 2.5 License
 * (http://creativecommons.org/licenses/by/2.5/). Please maintain the above
 * license and copyright statements when using this code.
 *
 * $Id: columnav.js 481 2007-02-21 05:56:01Z david $
 */
YAHOO.namespace('extension');

YAHOO.extension.ColumNav = function(id, cfg) {
    this._init(id, cfg);
};

YAHOO.extension.ColumNav.prototype = {

    DOM: YAHOO.util.Dom,
    EVT: YAHOO.util.Event,
    CON: YAHOO.util.Connect,

    ERROR_MSG: 'data unavailable',

    // PUBLIC API

    reset: function() {
        this.carousel.clear();
        this._init(this.id, this.cfg);
    },

    // PRIVATE API

    _init: function(id, cfg) {
        this.id = id;
        this.cfg = cfg; // make this a YAHOO.util.Config object?

        this.source = cfg.source;
        this.linkAction = cfg.linkAction || this._defaultLinkAction;
        this.request = null;
        this.counter = 1;
        this.numScrolled = 0;
        this.breadCrumb = cfg.breadCrumb;
        
        this.moving = false;
        this.carousel = new YAHOO.extension.Carousel(id,
                            {
                                'animationCompleteHandler': this._animationCompleteHandler,
                                'loadPrevHandler':          this._loadPrevHandler,
                                'numVisible':               cfg.numVisible || 1,
                                'prevElement':              cfg.prevElement || cfg.prevId,
                                'scrollInc':                1
                            });
        this.carousel.cn = this;

        var notOpera = (navigator.userAgent.match(/opera/i) == null);
        var kl = new YAHOO.util.KeyListener(this.carousel.carouselElem,
                                            { ctrl: notOpera, keys: [37, 38, 39, 40] },
                                            { fn: this._handleKeypress,
                                              scope: this,
                                              correctScope: true });
        kl.enable();

        var source = this.source;
        if (source && source.nodeType == 1)
            this._addMenu(source);
        else if (typeof source == 'string')
            this._makeRequest(source);
        else
            this._handleFailure({});
    },

    _loadPrevHandler: function(type, args) {
    	this.cn._removeLastBreadCrumb();

        this.cn._abortRequest();
    },

    _makeRequest: function(url) {
        if (document.getElementById("loadingImage")){  
        	document.getElementById("loadingImage").style["display"]="inline"; 
        }   
        var callback = {
            'success':  this._handleSuccess,
            'failure':  this._handleFailure,
            'scope':    this,
            'timeout':  5000
        };
        this._abortRequest();
        this.request = this.CON.asyncRequest('GET', url, callback);
    },

    _abortRequest: function() {
        if (this.request && this.CON.isCallInProgress(this.request))
            this.CON.abort(this.request);
    },

    _handleSuccess: function(o) {
        this._addMenu(o.responseXML.documentElement);
    },

    _handleFailure: function(o) {
        var list = document.createElement('ul');
        var item = document.createElement('li');
        var span = document.createElement('span');
        span.className = 'columnav-error';
        span.appendChild(document.createTextNode(this.ERROR_MSG));
        item.appendChild(span);
        list.appendChild(item);
        this._addMenu(list);
    },

    _handleKeypress: function(type, args, o) {
        var key = args[0];
        var evt = args[1];
        var target = this.EVT.getTarget(evt);
        if (target.tagName != 'A') {
            var links = this._getNodes(this.carousel.carouselList.lastChild,
                                       this._links);
            links[0].focus();
            return;
        }
        switch (key) {
        case 37: // left
            var menu = target.parentNode.parentNode;
            if (this._shouldScrollPrev(menu))
                o.carousel.scrollPrev();
            else {
                var prevMenu = this._prevMenu(menu);
                if (prevMenu)
                    this._focus(prevMenu);
            }
            break;
        case 38: // up
            if (target.previousSibling)
                target.previousSibling.focus();
            break;
        case 39: // right
            this._next(evt);
            break;
        case 40: // down
            if (target.nextSibling)
                target.nextSibling.focus();
            break;
        }
        this.EVT.stopEvent(evt);
    },

    _addMenu: function(node) {
        var menu = this._createMenu(node);
        this.carousel.addItem(this.counter++, menu);
        if (this.onFinishRendering != null){
        	try{eval(this.onFinishRendering);
        	}catch(e){}
        }
        if (document.getElementById("loadingImage")){  
        	document.getElementById("loadingImage").style["display"]="none"; 
        }   
        if (this._shouldScrollNext()) {
            this.carousel.scrollNext();
            this.moving = true;
        } else {
            if (this.counter > 2)
                this._focus(menu);
        }
    },

    _shouldScrollNext: function() {
        var numVisible = this.carousel.cfg.getProperty('numVisible');
        return ((this.counter - 1) - this.numScrolled > numVisible);
    },

    _shouldScrollPrev: function(menu) {
        var menus = this._getNodes(this.carousel.carouselList,
                                   this._childElements);
        var i = 0;
        for ( ; i < menus.length; i++) {
            if (menu == menus[i]) break;
        }
        return (i == this.numScrolled);
    },

    _prevMenu: function(menu) {
        var prevLi = menu.previousSibling;
        if (prevLi)
            return prevLi.getElementsByTagName('div')[0];
        return null;
    },

    _next: function(e) {
		//Add the items to the bread crumb.
		//<span> | <a href="javascript:columNav.carousel.moveTo(0)">All 50 States</a></span>
        if (this.moving)
            return;
        var target = this.EVT.getTarget(e);
        if (target.tagName == 'SPAN')
            target = target.parentNode;
        this._removeMenus(target);
        var href = target.getAttribute('href');
        var rel = target.getAttribute('rel');
        var list = target.list;
        if (href !== null)
            this._highlight(target);
        if (list){
            this._addMenu(list);
        }else if (rel == 'ajax'){
        	this._populateBreadCrumbs(target);
            this._makeRequest(href);
        }else {
            if (this.linkAction(e))
                return true;
        }
        this.EVT.preventDefault(e);
    },
    
    _populateBreadCrumbs: function(element){
    	var clickedItem = element.childNodes[0].childNodes[0].nodeValue;
    	var breadCrumbElement = document.getElementById(this.breadCrumb);
    	var breadCrumb = breadCrumbElement.childNodes[breadCrumbElement.childNodes.length - 1].childNodes[0].nodeValue;
    	if ((" | " + clickedItem) == breadCrumb){
    		return;
    	}
        var spanItem = document.createElement('span');
        spanItem.appendChild(document.createTextNode(" | " + element.childNodes[0].childNodes[0].nodeValue));
		document.getElementById(this.breadCrumb).appendChild(spanItem);
    },
    
    _removeLastBreadCrumb: function(){
    	var breadCrumbElement = document.getElementById(this.breadCrumb);
    	breadCrumbElement.removeChild(breadCrumbElement.childNodes[breadCrumbElement.childNodes.length - 1]);
    },

    _removeMenus: function(target) {
        var li = target.parentNode.parentNode;
        var list = this.carousel.carouselList;
        while (li != list.lastChild) {
            list.removeChild(list.lastChild);
            this.counter--;
        }
    },

    _highlight: function(target) {
        var items = this._getNodes(target.parentNode, this._childElements);
        for (var i = 0; i < items.length; i++)
            this.DOM.removeClass(items[i], 'columnav-active');
        this.DOM.addClass(target, 'columnav-active');
    },

    _focus: function(menu) {
        var links = this._getNodes(menu, this._links);
        for (var i = 0; i < links.length; i++) {
            if (this.DOM.hasClass(links[i], 'columnav-active')) {
                links[i].focus();
                return;
            }
        }
        links[0].focus();
    },

    _animationCompleteHandler: function(type, args) {
        this.cn.moving = false;
        if (args[0] == 'next') {
            this.cn.numScrolled++;
            this.cn._focus(this.carouselList.lastChild);
        }
        if (args[0] == 'prev') {
            this.cn._removeLastMenu();
            this.cn.numScrolled--;
            if (this.cfg.getProperty('numVisible') == 1)
                this.cn._focus(this.carouselList.lastChild);
        }
    },

    _removeLastMenu: function() {
        var list = this.carousel.carouselList;
        list.removeChild(list.lastChild);
        this.counter--;
    },

    _createMenu: function(node) {
        var menu = document.createElement('div');
        menu.setAttribute("class", "carousel-list-contents");
		this.onFinishRendering = node.getAttribute("onFinishRendering");
        var items = this._getNodes(node, this._childElements);
        var itemCount = 0
        for (var i = 0; i < items.length; i++) {
        
            var ce = this._getNodes(items[i], this._childElements);
            var link = ce[0], list = ce[1];
            var a = null;
            if (link.nodeName == "A" || link.nodeName == "a"){
            	itemCount++;
	            var text = link.firstChild.data;
	            var href = link.getAttribute('href');
	            var rel = link.getAttribute('rel');
	            var id = link.getAttribute('id');
	            var target = link.getAttribute('target');
	            var cls = link.getAttribute('class') || link.className;
	            
        		//var surround = document.createElement('div');
	            
	            a = document.createElement('a');
	            var span = document.createElement('span');
	            span.appendChild(document.createTextNode(text));
	            a.appendChild(span);
	            
	            if (rel == "javascript"){
	            	a.setAttribute('href', href);
	            	if (href.indexOf("javascript:") !== -1){
	            		href = href.substr(11);
	            	}
	            	a.setAttribute('onclick', href+ ";return false;");
	            }else{
	            	a.setAttribute('href', href);
		            a.setAttribute('rel', rel);
	            }
	            
	            if (id !== null){
	           	 a.setAttribute('id', id);
	            }
	            if (target !== null){
	            	a.setAttribute('target', target);
	            }
	            
	            a.list = list;
	            
				this.DOM.addClass(a, "carousel-list-contents-link");
				this.DOM.addClass(span, "carousel-list-contents-link-span");
				
	            if (cls){
	                this.DOM.addClass(a, cls);
	            }
	            if (list || rel == 'ajax'){
	                this.DOM.addClass(a, 'columnav-has-menu');
	            }  
	            if (rel != "javascript"){         
	            	this.EVT.addListener(a, 'click', this._next, this, true);
	            }
            	menu.appendChild(a);
            }else{
				//create the root element.
				var importedElement = this._createElement(link);
	            this._importNativeElement(importedElement, link);
            	menu.appendChild(importedElement);
            }
        }
        
   //     if (itemCount > 45){
  //      	document.getElementById("navclipRegion").style.height=itemCount*19;
   //     }
        return menu;
    },

    _defaultLinkAction: function() { return true; },

    _getNodes: function(root, filter) {
        var node = root;
        var nodes = [];
        var next;
        var f = filter || function() { return true; }
        while (node != null) {
            if (node.hasChildNodes())
                node = node.firstChild;
            else if (node != root && null != (next = node.nextSibling))
                node = next;
            else {
                next = null;
                for ( ; node != root; node = node.parentNode) {
                    next = node.nextSibling;
                    if (next != null) break;
                }
                node = next;
            }
            if (node != null && f(node, root))
                nodes.push(node);
        }
        return nodes;
    },

    _childElements: function(node, root) {
        return (node.nodeType == 1 && node.parentNode == root);
    },

    _links: function(node) { return (node.tagName == 'A'); },
    
	_importNativeElement: function(importedParentElement, nativeElement){	
		//create move over all the attributes.
		var length = nativeElement.attributes.length;
		for (var index = 0; index < length; index++){
			var attribute = nativeElement.attributes[index];
			if (attribute.name.indexOf("xmlns") !== 0 && 
				attribute.name.indexOf("on") === 0){
	            this.EVT.addListener(importedParentElement, attribute.name.substr(2), function (){
		        	try{eval(attribute.value);
		        	}catch(e){}
	            }, this, true);
			}else if (attribute.name.indexOf("xmlns") !== 0){
				importedParentElement.setAttribute(attribute.name, 
										  attribute.value);
			}
		}
				
		//replicate all the child elements for this native element.
		length = nativeElement.childNodes.length;
		for (var index = 0; index < length; index++){
			var childNativeElement = nativeElement.childNodes[index];
		   	if (childNativeElement.nodeType == 1){
				var xapElement = this._createElement(childNativeElement);
				
				//add the new element to the parent.
				this._importNativeElement(xapElement, childNativeElement);	
				importedParentElement.appendChild(xapElement);
			}else if (childNativeElement.nodeType == 3 ||
					  childNativeElement.nodeType == 4){
				if (childNativeElement.nodeValue !== null && 
					childNativeElement.nodeValue !== ""){
					var childCopy = document.createTextNode(childNativeElement.nodeValue);
					importedParentElement.appendChild(childCopy);
				}
			}
		}
	},

	/**
	 * Function is responsible for taking the native element and create 
	 * imported element will the same information.
	 */
	_createElement: function(nativeElement){	
		var importedElement = null;
		if (nativeElement.namespaceURI === null || nativeElement.namespaceURI === ""){
			importedElement = document.createElement(nativeElement.nodeName);
		}else{
			var prefixIndex = nativeElement.nodeName.indexOf(":");
			var nodeName = nativeElement.nodeName;
			var prefix = null;
			if (prefixIndex != -1){
				prefix = nodeName.substring(0, prefixIndex);
				nodeName = nodeName.substring(prefixIndex + 1);
			}
			importedElement = document.createElement(nodeName, nativeElement.namespaceURI, prefix);
		}
		return importedElement;
	}
    
};

YAHOO.extension.ColumnNav = YAHOO.extension.ColumNav;
