
/**
 * Encapsulate all Javascript used by the navbar - expose only the navbar class.
 * This is to avoid function and library conflicts when the navbar is embedded.
 * 
 * This technique is described in http://ajaxcookbook.org/javascript-api-namespaces/
 */
function defineNavbar() {

    /* PROTOTYPE EXTRACT */
	
    /*
 * The following has been hacked out of Prototype 1.5.0
 * to avoid library clashes with other frameworks (for example MooTools).
 * 
 * Some functions have been modified to avoid extending shared
 * classes (like Element). If prototype extends these classes
 * you run the risk of problems as other frameworks do the same.
 * 
 * This approach sucks for these reasons (among others):
 *   - if you want to upgrade Protoype you will need to go through these functions one by one and merge in changes
 *   - if you want to use Prototype functions not included here you will need to hack them in
 *   - we have modified Prototype so we can no longer be totally confident everything works
 *     - we should ideally cover all of these functions with JSUnit tests or at least ensure the functional tests cover every condition
 * 
 * Be on the lookout for a framework that doesn't extend shared classes.
 * Such a framework could be used a lot more easily and cleanly.
 */
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0, length = iterable.length; i < length; i++)
      results.push(iterable[i]);
    return results;
  }
}

function bind() {
	var args = $A(arguments), __method = args.shift(), object = args.shift();
	return function() {
		return __method.apply(object, args.concat($A(arguments)));
	}
}

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

/* this has been based on http://daniel.lorch.cc/docs/ajax_simple/ rather than Prototype */
function update(elementId, url) {
	var http = false;
	
	if(navigator.appName == "Microsoft Internet Explorer") {
	  http = new ActiveXObject("Microsoft.XMLHTTP");
	} else {
	  http = new XMLHttpRequest();
	}
	
	http.open("GET", url);
	http.onreadystatechange = function() {
	  if(http.readyState == 4) {
	    $(elementId).innerHTML = http.responseText;
	  }
	}
	http.send(null);
}

function getElementsByClassName(parentElement, className) {
	var children = ($(parentElement) || document.body).getElementsByTagName('*');
	var elements = [], child;
	for (var i = 0, length = children.length; i < length; i++) {
	  child = children[i];
	  if (hasClass(child, className))
	    elements.push(child);
	}
	return elements;
}

function hasClass(element, searchClass) {
	var classNames = classNamesOf(element);
	for(var i = 0; i < classNames.length; i++) {
		if (classNames[i] == searchClass) return true;
	}
	
	return false;
}

function dimensionsOf(element) {
	element = $(element);
    var display = styleOf(element, 'display');
    if (display != 'none' && display != null) // Safari bug
      return {width: element.offsetWidth, height: element.offsetHeight};

    // All *Width and *Height properties give 0 on elements with display none,
    // so enable the element temporarily
    var els = element.style;
    var originalVisibility = els.visibility;
    var originalPosition = els.position;
    var originalDisplay = els.display;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = 'block';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = originalDisplay;
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
}

function styleOf(element, style) {
	element = $(element);
    var value = element.style[style];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css[style] : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style];
      }
    }

    if((value == 'auto') && ['width','height'].include(style) && (styleOf(element, 'display') != 'none'))
      value = element['offset'+style.capitalize()] + 'px';

    return value == 'auto' ? null : value;
}

function removeClassFrom(element, nameToRemove) {
	if (!(element = $(element))) return;
	
	var newClassNames = '';
	var classNames = classNamesOf(element);
	for(var i = 0; i < classNames.length; i++) {
	    var className = classNames[i];
		if (className != nameToRemove) newClassNames = newClassNames + className + ' ';
	}
    element.className = newClassNames;
    return element;
}

function addClassTo(element, nameToAdd) {
	if (!(element = $(element))) return;
    removeClassFrom(element, nameToAdd);
	var origClassName = element.className;
	element.className = origClassName + ' ' + nameToAdd;
	return element;
}

function classNamesOf(element) {
	element = $(element);
	return element.className.split(/\s+/);
}

function isVisible(element) {
	return $(element).style.display != 'none';
}

function setStyleOf(element, style) {
	element = $(element);
    for (var name in style) {
      var value = style[name];
      element.style[name] = value;
    }
    return element;
}

function hasClass(element, className) {
	if (!(element = $(element))) return;
    var elementClassName = element.className;
    if (elementClassName.length == 0) return false;
    if (elementClassName == className ||
        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
      return true;
    return false;
}

function elementFrom(event) {
	return event.target || event.srcElement;
}

function $(element) {
	if (typeof element == 'string')
	element = document.getElementById(element);
	return element;
}

function observe(elementId, eventType, observer, useCapture) {
	var observed = $(elementId);
	if (observed.addEventListener) {
      observed.addEventListener(eventType, observer, useCapture);
    } else if (observed.attachEvent) {
      observed.attachEvent('on' + eventType, observer);
    }
}

	
	/* NAVBAR CLASS */
	
    var RIGHT_SECTION = "navbar_rightSection";
var LEFT_SECTION = "navbar_leftSection";
var NAVBAR_CONTAINER = "navBar_content";
/*
 * This width needs to be set to around 5px bigger
 * than the actual width of the right section.
 * Otherwise, the bigpond arrow tends to drop down
 * in IE 6.
 */
var MINIMUM_RIGHT_SECTION_WIDTH = 372

/* HELPER METHODS */

function setWidthOf(element, widthInPixels) {
    setStyleOf(element, {width: widthInPixels + "px"});
}

function widthOf(element) {
  return dimensionsOf(element).width;
}

function hide(element) {
	setStyleOf(element, {display: 'none'});
}

function isHidden(element) {
	return !isVisible(element);
}

function observeMouseOverFor(element, handler) {
	observe(element, "mouseover", handler, true);
}

function observeMouseOutFor(element, handler) {
	observe(element, "mouseout", handler, true);
}

function observeResizeWith(handler) {
	observe(window, 'resize', handler, false);
}

function show(element) {
	setStyleOf(element, {display: 'block'});
}

function setImageAfter(event, src) {
    elementFrom(event).src = src;
}

function observeNavBarBlock(blockId){
	observeMouseOverFor(blockId, mouseOverNavBarBlock);
	observeMouseOutFor(blockId, mouseOutNavBarBlock);
}

function mouseOverNavBarBlock(event){
    setNavBarBlockStyleAfter(event, {background : 'url(/navbar/i/n_tile_02.gif) repeat-x'})
}

function mouseOutNavBarBlock(event){
	setNavBarBlockStyleAfter(event, {background : 'none'})
}

function setNavBarBlockStyleAfter(event, style){
	var element = elementFrom(event);
    for (var parent = element; parent != null; parent = parent.parentNode)
	{
		if (hasClass(parent, "navbar_navBarBlock"))
		{
			element = parent;
			break;
		}
	}
	setStyleOf(element, style);
}

function getSiteCells() {
    return getElementsByClassName(LEFT_SECTION, "navbar_navBarBlock");
}
    
function getSiteCellWidths(){
    var siteCells = getSiteCells();
    var siteCellWidths = new Array();
    var toDisplayAllSiteCells = 1500;
    
    // calculating the width correctly requires that the cell be fully visible
    setWidthOf(LEFT_SECTION, toDisplayAllSiteCells);
    
    for (var i = 0; i < siteCells.length; ++i)
    {
        siteCellWidths[i] = widthOf(siteCells[i])
    }
    
    return siteCellWidths;
}
	
var NavBar = Class.create();
NavBar.prototype = {
	
	initialize: function() {
		this.loadedMoreSites = false;
		this.loadedBigpond = false;
	},
	
	observeElements: function() {
		observeMouseOverFor("navbar_moreSites", bind(this.mouseOverMoreSites, this));
		observeMouseOutFor("navbar_moreSites", bind(this.mouseOutMoreSites, this));
		
		observeMouseOverFor("navbar_bigpond", bind(this.mouseOverBigpond, this));
		observeMouseOutFor("navbar_bigpond", bind(this.mouseOutBigpond, this));
		
		observeMouseOverFor("navbar_bigpondMore", bind(this.mouseOverBigpondMore, this));
		observeMouseOutFor("navbar_bigpondMore", bind(this.mouseOutBigpondMore, this));
	
        var siteCells = getSiteCells();
        for(i = 0; i < siteCells.length; i++)
        {
            observeNavBarBlock(siteCells[i]);
        }

		hide("navbar_moreSitesContainer");
		hide("navbar_bigpondContainer");
        
        observeResizeWith(bind(this.resizeNavbar, this));
        	
	},
	
	mouseOverMoreSites: function(evt) {
        this.toggleHoverOn('navbar_moreSites', true)
	},
	
	mouseOutMoreSites: function(evt) {
		this.toggleHoverOn('navbar_moreSites', false)
	},
	
	mouseOverBigpond: function(evt) {
		setImageAfter(evt, "/navbar/i/n_bp_02.gif");
	},
	
	mouseOutBigpond: function(evt) {
		setImageAfter(evt, "/navbar/i/n_bp.gif");
	},
	
	mouseOverBigpondMore: function(evt) {
		if (isHidden('navbar_bigpondContainer'))
		{
			$('navbar_bigpondMore').src = "/navbar/i/n_bpa_02.gif";
			$('navbar_bigpond').src = "/navbar/i/n_bp_02.gif";
		}
	},
	
	mouseOutBigpondMore: function(evt) {
		if (isHidden('navbar_bigpondContainer'))
		{
			$('navbar_bigpondMore').src = "/navbar/i/n_bpa.gif";
			$('navbar_bigpond').src = "/navbar/i/n_bp.gif";
		}
	},

	loadBigpond: function() {
		if (isVisible('navbar_bigpondContainer'))
		{
			hide('navbar_bigpondContainer');
			$('navbar_bigpondMore').src = "/navbar/i/n_bpa.gif";
			$('navbar_bigpond').src = "/navbar/i/n_bp.gif";
		}
		else
		{
			if (this.loadedBigpond == false) {
				update("navbar_bigpondContainer",
					   "/navbar/bigpond.html");
				this.loadedBigpond = true;
			}
			if (isVisible('navbar_moreSitesContainer'))
			{
				this.loadMoreSites();
			}
			show('navbar_bigpondContainer');

			$('navbar_bigpondMore').src = "/navbar/i/n_bpa_03.gif";
			$('navbar_bigpond').src = "/navbar/i/n_bp.gif";
		}
	},
	
	loadMoreSites: function() {
		if (isVisible('navbar_moreSitesContainer'))
		{
            hide('navbar_moreSitesContainer');
			removeClassFrom('navbar_moreSites', 'navbar_moreSitesExpanded');
			addClassTo('navbar_moreSites', 'navbar_moreSites');
		}
		else
		{
			if (!this.loadedMoreSites) {
				update("navbar_moreSitesContainer",
					   "/navbar/moreSites.html");
				this.loadedMoreSites = true;
			}
			if (isVisible('navbar_bigpondContainer'))
			{
				this.loadBigpond();
			}
			show('navbar_moreSitesContainer');

			removeClassFrom('navbar_moreSites', 'navbar_moreSitesHover');
			removeClassFrom('navbar_moreSites', 'navbar_moreSites');
			addClassTo('navbar_moreSites', 'navbar_moreSitesExpanded');
		}
	},
	
	resizeNavbar: function() {
        /* 
         * first show the Navbar container
         * we hide it to begin with because it appears weird before resizing
         */
        show(NAVBAR_CONTAINER);
        
        var navbarWidth = widthOf(NAVBAR_CONTAINER);
        var availableLeftWidth = navbarWidth - MINIMUM_RIGHT_SECTION_WIDTH;
        
        var siteCellWidths = getSiteCellWidths();
        
        var leftWidth = 0;
		for (var i = 0; i < siteCellWidths.length; ++i)
		{
			if (availableLeftWidth >= siteCellWidths[i])
			{
                leftWidth += siteCellWidths[i];
                availableLeftWidth -= siteCellWidths[i];
			}
			else break;
		}
		setWidthOf(LEFT_SECTION, leftWidth);
        /*
         * Read the navbar width again - Safari thinks
         * the navbar size has shrunk by 15px since the first read.
         * This only happens on browser load - resize is fine.
         * Is it the resize for reading site cell widths causing
         * this problem?
         */
		setWidthOf(RIGHT_SECTION, widthOf(NAVBAR_CONTAINER) - leftWidth);
      
	},
    
    clearSearchField: function() {
        $("navbar_searchField").value = '';
    },
    
    fireEventsOnLoad: function() {
        observe(window, 'load', bind(this.resizeNavbar, this), false);
        observe(window, 'load', bind(this.observeElements, this), false);
    },
    
    toggleHoverOn: function(elementId, hovering) {
        var classToRemove = elementId + (hovering ? '' : 'Hover');
        var classToAdd = elementId + (hovering ? 'Hover' : '');
    
        if (isHidden(elementId + 'Container'))
    	{
    	    removeClassFrom(elementId, classToRemove);
    		addClassTo(elementId, classToAdd);
        }
    }
};

var navbar = new NavBar();
	
    return navbar;
}