
 /* This is a generated file.  Do not edit.  Edit the individual js files and regenerate the home page. */
	/*  Prototype JavaScript framework, version 1.4.0
 *  (c) 2005 Sam Stephenson <sam@conio.net>
 *
 *  Prototype is freely distributable under the terms of an MIT-style license.
 *  For details, see the Prototype web site: http://prototype.conio.net/
 *
/*--------------------------------------------------------------------------*/

var Prototype = {
  Version: '1.4.0',
  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',

  emptyFunction: function() {},
  K: function(x) {return x}
}

var Class = {
  create: function() {
    return function() {
      this.initialize.apply(this, arguments);
    }
  }
}

var Abstract = new Object();

Object.extend = function(destination, source) {
  for (property in source) {
    destination[property] = source[property];
  }
  return destination;
}

Object.inspect = function(object) {
  try {
    if (object == undefined) return 'undefined';
    if (object == null) return 'null';
    return object.inspect ? object.inspect() : (object.toString ? object.toString() : typeof object);
  } catch (e) {
    if (e instanceof RangeError) return '...';
    throw e;
  }
}

Function.prototype.bind = function() {
  var __method = this, args = $A(arguments), object = args.shift();
  return function() {
    return __method.apply(object, args.concat($A(arguments)));
  }
}

Function.prototype.bindAsEventListener = function(object) {
  var __method = this;
  return function(event) {
    return __method.call(object, event || window.event);
  }
}

Object.extend(Number.prototype, {
  toColorPart: function() {
    var digits = this.toString(16);
    if (this < 16) return '0' + digits;
    return digits;
  },

  succ: function() {
    return this + 1;
  },

  times: function(iterator) {
    $R(0, this, true).each(iterator);
    return this;
  }
});

var Try = {
  these: function() {
    var returnValue;

    for (var i = 0; i < arguments.length; i++) {
      var lambda = arguments[i];
      try {
        returnValue = lambda();
        break;
      } catch (e) {}
    }

    return returnValue;
  }
}

/*--------------------------------------------------------------------------*/

var PeriodicalExecuter = Class.create();
PeriodicalExecuter.prototype = {
  initialize: function(callback, frequency) {
    this.callback = callback;
    this.frequency = frequency;
    this.currentlyExecuting = false;

    this.registerCallback();
  },

  registerCallback: function() {
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  onTimerEvent: function() {
    if (!this.currentlyExecuting) {
      try {
        this.currentlyExecuting = true;
        this.callback();
      } finally {
        this.currentlyExecuting = false;
      }
    }
  }
}

/*--------------------------------------------------------------------------*/

function $() {
  var elements = new Array();

  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1)
      return element;

    elements.push(element);
  }

  return elements;
}
Object.extend(String.prototype, {
  stripTags: function() {
    return this.replace(/<\/?[^>]+>/gi, '');
  },

  stripScripts: function() {
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
  },

  extractScripts: function() {
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
    return (this.match(matchAll) || []).map(function(scriptTag) {
      return (scriptTag.match(matchOne) || ['', ''])[1];
    });
  },

  evalScripts: function() {
    return this.extractScripts().map(eval);
  },

  escapeHTML: function() {
    var div = document.createElement('div');
    var text = document.createTextNode(this);
    div.appendChild(text);
    return div.innerHTML;
  },

  unescapeHTML: function() {
    var div = document.createElement('div');
    div.innerHTML = this.stripTags();
    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
  },

  toQueryParams: function() {
    var pairs = this.match(/^\??(.*)$/)[1].split('&');
    return pairs.inject({}, function(params, pairString) {
      var pair = pairString.split('=');
      params[pair[0]] = pair[1];
      return params;
    });
  },

  toArray: function() {
    return this.split('');
  },

  camelize: function() {
    var oStringList = this.split('-');
    if (oStringList.length == 1) return oStringList[0];

    var camelizedString = this.indexOf('-') == 0
      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
      : oStringList[0];

    for (var i = 1, len = oStringList.length; i < len; i++) {
      var s = oStringList[i];
      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
    }

    return camelizedString;
  },

  inspect: function() {
    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
  },
	_each: function(iterator) {
	    for (var i = 0; i < this.length; i++){
			iterator(this.substring(i, i+1));
		}
	},
	chunkSplit: function(length, limit){
		var output = [];
		var str = this;
		var c = 0;
		while(str.length && (!limit || ++c < limit)){
			output.push(str.substring(0, length));
			str = str.substring(length);
		}
		if(limit){
			output.push(str);
		}
		return output;
	}
	
});

String.prototype.parseQuery = String.prototype.toQueryParams;

var $break    = new Object();
var $continue = new Object();

var Enumerable = {
  each: function(iterator) {
    var index = 0;
    try {
      this._each(function(value) {
        try {
          iterator(value, index++);
        } catch (e) {
          if (e != $continue) throw e;
        }
      });
    } catch (e) {
      if (e != $break) throw e;
    }
  },

  all: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      result = result && !!(iterator || Prototype.K)(value, index);
      if (!result) throw $break;
    });
    return result;
  },

  any: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      if (result = !!(iterator || Prototype.K)(value, index))
        throw $break;
    });
    return result;
  },

  collect: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },

  detect: function (iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw $break;
      }
    });
    return result;
  },

  findAll: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },

  grep: function(pattern, iterator) {
    var results = [];
    this.each(function(value, index) {
      var stringValue = value.toString();
      if (stringValue.match(pattern))
        results.push((iterator || Prototype.K)(value, index));
    })
    return results;
  },

  include: function(object) {
    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw $break;
      }
    });
    return found;
  },

  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },

  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.collect(function(value) {
      return value[method].apply(value, args);
    });
  },

  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value >= (result || value))
        result = value;
    });
    return result;
  },

  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value <= (result || value))
        result = value;
    });
    return result;
  },

  partition: function(iterator) {
    var trues = [], falses = [];
    this.each(function(value, index) {
      ((iterator || Prototype.K)(value, index) ?
        trues : falses).push(value);
    });
    return [trues, falses];
  },

  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },

  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },

  sortBy: function(iterator) {
    return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },

  toArray: function() {
    return this.collect(Prototype.K);
  },

  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (typeof args.last() == 'function')
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      iterator(value = collections.pluck(index));
      return value;
    });
  },

  inspect: function() {
    return '#<Enumerable:' + this.toArray().inspect() + '>';
  },
  
	unique: function(){
		return this.inject([], function(results, item){
			if(results.indexOf(item) < 0)
				results.push(item);
			return results;
		});
	}
}

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});
var $A = Array.from = function(iterable) {
  if (!iterable) return [];
  if (iterable.toArray) {
    return iterable.toArray();
  } else {
    var results = [];
    for (var i = 0; i < iterable.length; i++)
      results.push(iterable[i]);
    return results;
  }
}

Object.extend(Array.prototype, Enumerable);
Object.extend(String.prototype, Enumerable);

Array.prototype._reverse = Array.prototype.reverse;

Object.extend(Array.prototype, {
  _each: function(iterator) {
    for (var i = 0; i < this.length; i++)
      iterator(this[i]);
  },

  clear: function() {
    this.length = 0;
    return this;
  },

  first: function() {
    return this[0];
  },

  last: function() {
    return this[this.length - 1];
  },

  compact: function() {
    return this.select(function(value) {
      return value != undefined || value != null;
    });
  },

  flatten: function() {
    return this.inject([], function(array, value) {
      return array.concat(value.constructor == Array ?
        value.flatten() : [value]);
    });
  },

  without: function() {
    var values = $A(arguments);
    return this.select(function(value) {
      return !values.include(value);
    });
  },

  indexOf: function(object) {
    for (var i = 0; i < this.length; i++)
      if (this[i] == object) return i;
    return -1;
  },

  reverse: function(inline) {
    return (inline !== false ? this : this.toArray())._reverse();
  },

  shift: function() {
    var result = this[0];
    for (var i = 0; i < this.length - 1; i++)
      this[i] = this[i + 1];
    this.length--;
    return result;
  },

  inspect: function() {
    return '[' + this.map(Object.inspect).join(', ') + ']';
  }
});
var Hash = {
  _each: function(iterator) {
    for (key in this) {
      var value = this[key];
      if (typeof value == 'function') continue;

      var pair = [key, value];
      pair.key = key;
      pair.value = value;
      iterator(pair);
    }
  },

  keys: function() {
    return this.pluck('key');
  },

  values: function() {
    return this.pluck('value');
  },

  merge: function(hash) {
    return $H(hash).inject($H(this), function(mergedHash, pair) {
      mergedHash[pair.key] = pair.value;
      return mergedHash;
    });
  },

  toQueryString: function() {
    return this.map(function(pair) {
      return pair.map(encodeURIComponent).join('=');
    }).join('&');
  },

  inspect: function() {
    return '#<Hash:{' + this.map(function(pair) {
      return pair.map(Object.inspect).join(': ');
    }).join(', ') + '}>';
  }
}

function $H(object) {
  var hash = Object.extend({}, object || {});
  Object.extend(hash, Enumerable);
  Object.extend(hash, Hash);
  return hash;
}
ObjectRange = Class.create();
Object.extend(ObjectRange.prototype, Enumerable);
Object.extend(ObjectRange.prototype, {
  initialize: function(start, end, exclusive) {
    this.start = start;
    this.end = end;
    this.exclusive = exclusive;
  },

  _each: function(iterator) {
    var value = this.start;
    do {
      iterator(value);
      value = value.succ();
    } while (this.include(value));
  },

  include: function(value) {
    if (value < this.start)
      return false;
    if (this.exclusive)
      return value < this.end;
    return value <= this.end;
  }
});

var $R = function(start, end, exclusive) {
  return new ObjectRange(start, end, exclusive);
}




/**
 * Expanded by Aaron J Pedersen to find class when more than one class has been defined
 * Example class="class1 class2" you can find either class1 or class2
 */
document.getElementsByClassName = function(className, parentElement, nodeType) {
	var search_elements;
	
	if(nodeType)
		search_elements = $NL(($(parentElement) || document.body).getElementsByTagName(nodeType));
	else if(document.all)
		search_elements = $NL(($(parentElement) || document.body).all);
	else
		search_elements = $NL(($(parentElement) || document.body).getElementsByTagName("*"));

	className = className.toUpperCase();
 
  return search_elements.inject([], function(elements, child) {
  	var classElements = $A(child.className.toUpperCase().split(' '));
  	classElements.each(function(classElement) {
		if (classElement == className) {
	    		elements.push(child);
		}
  	});
  	return elements;
  });
}

/*--------------------------------------------------------------------------*/

if (!window.Element) {
  var Element = new Object();
}

Object.extend(Element, {
  visible: function(element) {
    return $(element).style.display != 'none';
  },

  toggle: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      Element[Element.visible(element) ? 'hide' : 'show'](element);
    }
  },

  hide: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      element.style.display = 'none';
    }
  },

  show: function() {
    for (var i = 0; i < arguments.length; i++) {
      var element = $(arguments[i]);
      element.style.display = '';
    }
  },

  remove: function(element) {
    element = $(element);
    element.parentNode.removeChild(element);
  },

  update: function(element, html) {
    $(element).innerHTML = html.stripScripts();
    setTimeout(function() {html.evalScripts()}, 10);
  },

  getHeight: function(element) {
    element = $(element);
    return element.offsetHeight;
  },

  classNames: function(element) {
    return new Element.ClassNames(element);
  },

  hasClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).include(className);
  },

  addClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).add(className);
  },

  removeClassName: function(element, className) {
    if (!(element = $(element))) return;
    return Element.classNames(element).remove(className);
  },

  // removes whitespace-only text node children
  cleanWhitespace: function(element) {
    element = $(element);
    for (var i = 0; i < element.childNodes.length; i++) {
      var node = element.childNodes[i];
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
        Element.remove(node);
    }
  },

  empty: function(element) {
    return $(element).innerHTML.match(/^\s*$/);
  },

  scrollTo: function(element) {
    element = $(element);
    var x = element.x ? element.x : element.offsetLeft,
        y = element.y ? element.y : element.offsetTop;
    window.scrollTo(x, y);
  },

  getStyle: function(element, style) {
    element = $(element);
    var value = element.style[style.camelize()];
    if (!value) {
      if (document.defaultView && document.defaultView.getComputedStyle) {
        var css = document.defaultView.getComputedStyle(element, null);
        value = css ? css.getPropertyValue(style) : null;
      } else if (element.currentStyle) {
        value = element.currentStyle[style.camelize()];
      }
    }

    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';

    return value == 'auto' ? null : value;
  },

  setStyle: function(element, style) {
    element = $(element);
    for (name in style)
      element.style[name.camelize()] = style[name];
  },

  getDimensions: function(element) {
    element = $(element);
    if (Element.getStyle(element, 'display') != 'none')
      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;
    els.visibility = 'hidden';
    els.position = 'absolute';
    els.display = '';
    var originalWidth = element.clientWidth;
    var originalHeight = element.clientHeight;
    els.display = 'none';
    els.position = originalPosition;
    els.visibility = originalVisibility;
    return {width: originalWidth, height: originalHeight};
  },

  makePositioned: function(element) {
    element = $(element);
    var pos = Element.getStyle(element, 'position');
    if (pos == 'static' || !pos) {
      element._madePositioned = true;
      element.style.position = 'relative';
      // Opera returns the offset relative to the positioning context, when an
      // element is position relative but top and left have not been defined
      if (window.opera) {
        element.style.top = 0;
        element.style.left = 0;
      }
    }
  },

  undoPositioned: function(element) {
    element = $(element);
    if (element._madePositioned) {
      element._madePositioned = undefined;
      element.style.position =
        element.style.top =
        element.style.left =
        element.style.bottom =
        element.style.right = '';
    }
  },

  makeClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element._overflow = element.style.overflow;
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
      element.style.overflow = 'hidden';
  },

  undoClipping: function(element) {
    element = $(element);
    if (element._overflow) return;
    element.style.overflow = element._overflow;
    element._overflow = undefined;
  }
});

var Toggle = new Object();
Toggle.display = Element.toggle;

/*--------------------------------------------------------------------------*/

Abstract.Insertion = function(adjacency) {
  this.adjacency = adjacency;
}

Abstract.Insertion.prototype = {
  initialize: function(element, content) {
    this.element = $(element);
    this.content = content.stripScripts();

    if (this.adjacency && this.element.insertAdjacentHTML) {
      try {
        this.element.insertAdjacentHTML(this.adjacency, this.content);
      } catch (e) {
        if (this.element.tagName.toLowerCase() == 'tbody') {
          this.insertContent(this.contentFromAnonymousTable());
        } else {
          throw e;
        }
      }
    } else {
      this.range = this.element.ownerDocument.createRange();
      if (this.initializeRange) this.initializeRange();
      this.insertContent([this.range.createContextualFragment(this.content)]);
    }

    setTimeout(function() {content.evalScripts()}, 10);
  },

  contentFromAnonymousTable: function() {
    var div = document.createElement('div');
    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
    return $A(div.childNodes[0].childNodes[0].childNodes);
  }
}

var Insertion = new Object();

Insertion.Before = Class.create();
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  initializeRange: function() {
    this.range.setStartBefore(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment, this.element);
    }).bind(this));
  }
});

Insertion.Top = Class.create();
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(true);
  },

  insertContent: function(fragments) {
    fragments.reverse(false).each((function(fragment) {
      this.element.insertBefore(fragment, this.element.firstChild);
    }).bind(this));
  }
});

Insertion.Bottom = Class.create();
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  initializeRange: function() {
    this.range.selectNodeContents(this.element);
    this.range.collapse(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.appendChild(fragment);
    }).bind(this));
  }
});

Insertion.After = Class.create();
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  initializeRange: function() {
    this.range.setStartAfter(this.element);
  },

  insertContent: function(fragments) {
    fragments.each((function(fragment) {
      this.element.parentNode.insertBefore(fragment,
        this.element.nextSibling);
    }).bind(this));
  }
});

/*--------------------------------------------------------------------------*/

Element.ClassNames = Class.create();
Element.ClassNames.prototype = {
  initialize: function(element) {
    this.element = $(element);
  },

  _each: function(iterator) {
    this.element.className.split(/\s+/).select(function(name) {
      return name.length > 0;
    })._each(iterator);
  },

  set: function(className) {
    this.element.className = className;
  },

  add: function(classNameToAdd) {
    if (this.include(classNameToAdd)) return;
    this.set(this.toArray().concat(classNameToAdd).join(' '));
  },

  remove: function(classNameToRemove) {
    if (!this.include(classNameToRemove)) return;
    this.set(this.select(function(className) {
      return className != classNameToRemove;
    }).join(' '));
  },

  toString: function() {
    return this.toArray().join(' ');
  }
}

Object.extend(Element.ClassNames.prototype, Enumerable);
var Field = {
  clear: function() {
    for (var i = 0; i < arguments.length; i++)
      $(arguments[i]).value = '';
  },

  focus: function(element) {
    $(element).focus();
  },

  present: function() {
    for (var i = 0; i < arguments.length; i++)
      if ($(arguments[i]).value == '') return false;
    return true;
  },

  select: function(element) {
    $(element).select();
  },

  activate: function(element) {
    element = $(element);
    element.focus();
    if (element.select)
      element.select();
  }
}

/*--------------------------------------------------------------------------*/

var Form = {
  serialize: function(form) {
    var elements = Form.getElements($(form));
    var queryComponents = new Array();

    for (var i = 0; i < elements.length; i++) {
      var queryComponent = Form.Element.serialize(elements[i]);
      if (queryComponent)
        queryComponents.push(queryComponent);
    }

    return queryComponents.join('&');
  },

  getElements: function(form) {
    form = $(form);
    var elements = new Array();

    for (tagName in Form.Element.Serializers) {
      var tagElements = form.getElementsByTagName(tagName);
      for (var j = 0; j < tagElements.length; j++)
        elements.push(tagElements[j]);
    }
    return elements;
  },

  getInputs: function(form, typeName, name) {
    form = $(form);
    var inputs = form.getElementsByTagName('input');

    if (!typeName && !name)
      return inputs;

    var matchingInputs = new Array();
    for (var i = 0; i < inputs.length; i++) {
      var input = inputs[i];
      if ((typeName && input.type != typeName) ||
          (name && input.name != name))
        continue;
      matchingInputs.push(input);
    }

    return matchingInputs;
  },

  disable: function(form) {
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      element.blur();
      element.disabled = 'true';
    }
  },

  enable: function(form) {
    var elements = Form.getElements(form);
    for (var i = 0; i < elements.length; i++) {
      var element = elements[i];
      element.disabled = '';
    }
  },

  findFirstElement: function(form) {
    return Form.getElements(form).find(function(element) {
      return element.type != 'hidden' && !element.disabled &&
        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
    });
  },

  focusFirstElement: function(form) {
    Field.activate(Form.findFirstElement(form));
  },

  reset: function(form) {
    $(form).reset();
  }
}

Form.Element = {
  serialize: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter) {
      var key = encodeURIComponent(parameter[0]);
      if (key.length == 0) return;

      if (parameter[1].constructor != Array)
        parameter[1] = [parameter[1]];

      return parameter[1].map(function(value) {
        return key + '=' + encodeURIComponent(value);
      }).join('&');
    }
  },

  getValue: function(element) {
    element = $(element);
    var method = element.tagName.toLowerCase();
    var parameter = Form.Element.Serializers[method](element);

    if (parameter)
      return parameter[1];
  }
}

Form.Element.Serializers = {
  input: function(element) {
    switch (element.type.toLowerCase()) {
      case 'submit':
      case 'hidden':
      case 'password':
      case 'text':
        return Form.Element.Serializers.textarea(element);
      case 'checkbox':
      case 'radio':
        return Form.Element.Serializers.inputSelector(element);
    }
    return false;
  },

  inputSelector: function(element) {
    if (element.checked)
      return [element.name, element.value];
  },

  textarea: function(element) {
    return [element.name, element.value];
  },

  select: function(element) {
    return Form.Element.Serializers[element.type == 'select-one' ?
      'selectOne' : 'selectMany'](element);
  },

  selectOne: function(element) {
    var value = '', opt, index = element.selectedIndex;
    if (index >= 0) {
      opt = element.options[index];
      value = opt.value;
      if (!value && !('value' in opt))
        value = opt.text;
    }
    return [element.name, value];
  },

  selectMany: function(element) {
    var value = new Array();
    for (var i = 0; i < element.length; i++) {
      var opt = element.options[i];
      if (opt.selected) {
        var optValue = opt.value;
        if (!optValue && !('value' in opt))
          optValue = opt.text;
        value.push(optValue);
      }
    }
    return [element.name, value];
  }
}

/*--------------------------------------------------------------------------*/

var $F = Form.Element.getValue;

/*--------------------------------------------------------------------------*/

Abstract.TimedObserver = function() {}
Abstract.TimedObserver.prototype = {
  initialize: function(element, frequency, callback) {
    this.frequency = frequency;
    this.element   = $(element);
    this.callback  = callback;

    this.lastValue = this.getValue();
    this.registerCallback();
  },

  registerCallback: function() {
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  },

  onTimerEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  }
}

Form.Element.Observer = Class.create();
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.Observer = Class.create();
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});

/*--------------------------------------------------------------------------*/

Abstract.EventObserver = function() {}
Abstract.EventObserver.prototype = {
  initialize: function(element, callback) {
    this.element  = $(element);
    this.callback = callback;

    this.lastValue = this.getValue();
    if (this.element.tagName.toLowerCase() == 'form')
      this.registerFormCallbacks();
    else
      this.registerCallback(this.element);
  },

  onElementEvent: function() {
    var value = this.getValue();
    if (this.lastValue != value) {
      this.callback(this.element, value);
      this.lastValue = value;
    }
  },

  registerFormCallbacks: function() {
    var elements = Form.getElements(this.element);
    for (var i = 0; i < elements.length; i++)
      this.registerCallback(elements[i]);
  },

  registerCallback: function(element) {
    if (element.type) {
      switch (element.type.toLowerCase()) {
        case 'checkbox':
        case 'radio':
          Event.observe(element, 'click', this.onElementEvent.bind(this));
          break;
        case 'password':
        case 'text':
        case 'textarea':
        case 'select-one':
        case 'select-multiple':
          Event.observe(element, 'change', this.onElementEvent.bind(this));
          break;
      }
    }
  }
}

Form.Element.EventObserver = Class.create();
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.Element.getValue(this.element);
  }
});

Form.EventObserver = Class.create();
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  getValue: function() {
    return Form.serialize(this.element);
  }
});
if (!window.Event) {
  var Event = new Object();
}

Object.extend(Event, {
  KEY_BACKSPACE: 8,
  KEY_TAB:       9,
  KEY_RETURN:   13,
  KEY_ESC:      27,
  KEY_LEFT:     37,
  KEY_UP:       38,
  KEY_RIGHT:    39,
  KEY_DOWN:     40,
  KEY_DELETE:   46,

  element: function(event) {
    return event.target || event.srcElement;
  },

  isLeftClick: function(event) {
    return (((event.which) && (event.which == 1)) ||
            ((event.button) && (event.button == 1)));
  },

  pointerX: function(event) {
    return event.pageX || (event.clientX +
      (document.documentElement.scrollLeft || document.body.scrollLeft));
  },

  pointerY: function(event) {
    return event.pageY || (event.clientY +
      (document.documentElement.scrollTop || document.body.scrollTop));
  },

  stop: function(event) {
    if (event.preventDefault) {
      event.preventDefault();
      event.stopPropagation();
    } else {
      event.returnValue = false;
      event.cancelBubble = true;
    }
  },

  // find the first node with the given tagName, starting from the
  // node the event was triggered on; traverses the DOM upwards
  findElement: function(event, tagName) {
    var element = Event.element(event);
    while (element.parentNode && (!element.tagName ||
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
      element = element.parentNode;
    return element;
  },

  observers: false,

  _observeAndCache: function(element, name, observer, useCapture) {
    if (!this.observers) this.observers = [];
    if (element.addEventListener) {
      this.observers.push([element, name, observer, useCapture]);
      element.addEventListener(name, observer, useCapture);
    } else if (element.attachEvent) {
      this.observers.push([element, name, observer, useCapture]);
      element.attachEvent('on' + name, observer);
    }
    return this.observers[this.observers.length-1];
  },

  unloadCache: function() {
    if (!Event.observers) return;
    for (var i = 0; i < Event.observers.length; i++) {
      Event.stopObserving.apply(this, Event.observers[i]);
      Event.observers[i][0] = null;
    }
    Event.observers = false;
  },

  observe: function(element, name, observer, useCapture) {
    var element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.attachEvent))
      name = 'keydown';

    return this._observeAndCache(element, name, observer, useCapture);
  },
  
  stopObserving: function(element, name, observer, useCapture) {

    if(arguments.length == 1){
    	name = element[1];
    	observer = element[2];
    	useCapture = element[3];
    	element = element[0];
    }
    
    var element = $(element);
    useCapture = useCapture || false;

    if (name == 'keypress' &&
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
        || element.detachEvent))
      name = 'keydown';

    if (element.removeEventListener) {
      element.removeEventListener(name, observer, useCapture);
    } else if (element.detachEvent) {
      element.detachEvent('on' + name, observer);
    }
  }
});

/* prevent memory leaks in IE */
Event.observe(window, 'unload', Event.unloadCache, false);
var Position = {
  // set to true if needed, warning: firefox performance problems
  // NOT neeeded for page scrolling, only if draggable contained in
  // scrollable elements
  includeScrollOffsets: false,

  // must be called before calling withinIncludingScrolloffset, every time the
  // page is scrolled
  prepare: function() {
    this.deltaX =  window.pageXOffset
                || document.documentElement.scrollLeft
                || document.body.scrollLeft
                || 0;
    this.deltaY =  window.pageYOffset
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
  },

  realOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.scrollTop  || 0;
      valueL += element.scrollLeft || 0;
      element = element.parentNode;
    } while (element);
    return [valueL, valueT];
  },

  cumulativeOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
    } while (element);
    return [valueL, valueT];
  },

  positionedOffset: function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      element = element.offsetParent;
      if (element) {
        p = Element.getStyle(element, 'position');
        if (p == 'relative' || p == 'absolute') break;
      }
    } while (element);
    return [valueL, valueT];
  },

  offsetParent: function(element) {
    if (element.offsetParent) return element.offsetParent;
    if (element == document.body) return element;

    while ((element = element.parentNode) && element != document.body)
      if (Element.getStyle(element, 'position') != 'static')
        return element;

    return document.body;
  },

  // caches x/y coordinate pair to use with overlap
  within: function(element, x, y) {
    if (this.includeScrollOffsets)
      return this.withinIncludingScrolloffsets(element, x, y);
    this.xcomp = x;
    this.ycomp = y;
    this.offset = this.cumulativeOffset(element);

    return (y >= this.offset[1] &&
            y <  this.offset[1] + element.offsetHeight &&
            x >= this.offset[0] &&
            x <  this.offset[0] + element.offsetWidth);
  },

  withinIncludingScrolloffsets: function(element, x, y) {
    var offsetcache = this.realOffset(element);

    this.xcomp = x + offsetcache[0] - this.deltaX;
    this.ycomp = y + offsetcache[1] - this.deltaY;
    this.offset = this.cumulativeOffset(element);

    return (this.ycomp >= this.offset[1] &&
            this.ycomp <  this.offset[1] + element.offsetHeight &&
            this.xcomp >= this.offset[0] &&
            this.xcomp <  this.offset[0] + element.offsetWidth);
  },

  // within must be called directly before
  overlap: function(mode, element) {
    if (!mode) return 0;
    if (mode == 'vertical')
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
        element.offsetHeight;
    if (mode == 'horizontal')
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
        element.offsetWidth;
  },

  clone: function(source, target) {
    source = $(source);
    target = $(target);
    target.style.position = 'absolute';
    var offsets = this.cumulativeOffset(source);
    target.style.top    = offsets[1] + 'px';
    target.style.left   = offsets[0] + 'px';
    target.style.width  = source.offsetWidth + 'px';
    target.style.height = source.offsetHeight + 'px';
  },

  page: function(forElement) {
    var valueT = 0, valueL = 0;

    var element = forElement;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;

      // Safari fix
      if (element.offsetParent==document.body)
        if (Element.getStyle(element,'position')=='absolute') break;

    } while (element = element.offsetParent);

    element = forElement;
    do {
      valueT -= element.scrollTop  || 0;
      valueL -= element.scrollLeft || 0;
    } while (element = element.parentNode);

    return [valueL, valueT];
  },

  clone: function(source, target) {
    var options = Object.extend({
      setLeft:    true,
      setTop:     true,
      setWidth:   true,
      setHeight:  true,
      offsetTop:  0,
      offsetLeft: 0
    }, arguments[2] || {})

    // find page position of source
    source = $(source);
    var p = Position.page(source);

    // find coordinate system to use
    target = $(target);
    var delta = [0, 0];
    var parent = null;
    // delta [0,0] will do fine with position: fixed elements,
    // position:absolute needs offsetParent deltas
    if (Element.getStyle(target,'position') == 'absolute') {
      parent = Position.offsetParent(target);
      delta = Position.page(parent);
    }

    // correct by body offsets (fixes Safari)
    if (parent == document.body) {
      delta[0] -= document.body.offsetLeft;
      delta[1] -= document.body.offsetTop;
    }

    // set position
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  },

  absolutize: function(element) {
    element = $(element);
    if (element.style.position == 'absolute') return;
    Position.prepare();

    var offsets = Position.positionedOffset(element);
    var top     = offsets[1];
    var left    = offsets[0];
    var width   = element.clientWidth;
    var height  = element.clientHeight;

    element._originalLeft   = left - parseFloat(element.style.left  || 0);
    element._originalTop    = top  - parseFloat(element.style.top || 0);
    element._originalWidth  = element.style.width;
    element._originalHeight = element.style.height;

    element.style.position = 'absolute';
    element.style.top    = top + 'px';;
    element.style.left   = left + 'px';;
    element.style.width  = width + 'px';;
    element.style.height = height + 'px';;
  },

  relativize: function(element) {
    element = $(element);
    if (element.style.position == 'relative') return;
    Position.prepare();

    element.style.position = 'relative';
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);

    element.style.top    = top + 'px';
    element.style.left   = left + 'px';
    element.style.height = element._originalHeight;
    element.style.width  = element._originalWidth;
  }
}

// Safari returns margins on body which is incorrect if the child is absolutely
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
// KHTML/WebKit only.
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  Position.cumulativeOffset = function(element) {
    var valueT = 0, valueL = 0;
    do {
      valueT += element.offsetTop  || 0;
      valueL += element.offsetLeft || 0;
      if (element.offsetParent == document.body)
        if (Element.getStyle(element, 'position') == 'absolute') break;

      element = element.offsetParent;
    } while (element);

    return [valueL, valueT];
  }
}


////////////////////////////////////////////////////
////	Custom Stuff below this line	////////////

/**
 * Define some globals for figuring out what type of element we have
 */

function Node() {}

Node.ELEMENT_NODE = 1;
Node.ATTRIBUTE_NODE = 2;
Node.TEXT_NODE = 3;
Node.CDATA_SECTION_NODE = 4;
Node.ENTITY_REFERENCE_NODE = 5;
Node.ENTITY_NODE = 6;
Node.PROCESSING_INSTRUCTION_NODE = 7;
Node.COMMENT_NODE = 8;
Node.DOCUMENT_NODE = 9;
Node.DOCUMENT_TYPE_NODE = 10;
Node.DOCUMENT_FRAGMENT_NODE = 11;
Node.NOTATION_NODE = 12;

/**
 * This section extends (or create in IE) the NodeList object
 * to allow for enumeration of read-only node lists (element.childNodes, etc)
 * A NodeList differs from an Array in a couple ways.
 * - It is specifically designed to hold Nodes (XML nodes or HTML elements)
 * - It is read only
 * - It is often "live" meaning that if the DOM changes, this list will often change
 *   without warning.
 * - It's much faster
 */
function IENodeList(node_list){
	var _real_node_list;
	this._real_node_list = node_list;
};

NodeList_Extensions = {
	elements:	function() {
					return this.inject([], function(elementNodes, element) {
					  if(element.nodeType == Node.ELEMENT_NODE)
						elementNodes.push(element);
					  return elementNodes;
					})
				},
	firstElement:	function(){
					return this.elements().first();
				},
	lastElement:	function(){
					return this.elements().last();
				},
	inspect:	Array.prototype.inspect				
};


Object.extend(IENodeList.prototype, Enumerable);
Object.extend(IENodeList.prototype, {
	_each:	function(iterator) {
		for (var i = 0; i < this._real_node_list.length; i++)
			iterator(this._real_node_list[i]);
	},
	first:		function(){ return this._real_node_list[0] },
	last:		function(){ return this._real_node_list[this._real_node_list.length - 1] },
	indexOf:	function(object) {
				for (var i = 0; i < this._real_node_list.length; i++)
					if (this._real_node_list[i] == object) return i;
				return -1;
			}
});
Object.extend(IENodeList.prototype, NodeList_Extensions);

if(typeof NodeList != 'undefined'){
	Object.extend(NodeList.prototype, Enumerable);	
	Object.extend(NodeList.prototype, {
		_each:		Array.prototype._each,
		first:		Array.prototype.first,
		last:		Array.prototype.last,
		indexOf:	Array.prototype.indexOf
	});
	Object.extend(NodeList.prototype, NodeList_Extensions);
}

/**
 * Build a browser independent NodeList object from a node list.
 * This does nothing in FireFox, but supplies a bunch of fixes in IE.
 */
function $NL(node_list){

	if(typeof IENodeList != 'undefined' || typeof node_list != 'NodeList'){
		return new IENodeList(node_list);
	}
	else{
		return node_list;
	}

}



var XML = {};
XML.getRootNode = function(responseXML){ 
	switch(responseXML.childNodes.length){
		case 1: return responseXML.childNodes[0]; break;
		case 2: return responseXML.childNodes[1]; break;
		default: return false; break;
	}
}


	/**
 * Function is for debugging.  
 * Will iterate over all properties of the given object and alert each to the UI.
 * @param {Object} obj Object to be debugged.
 */
function whatis (obj) {
	alert(obj)
	for(i in obj) {
		alert(i);	
	}
}	

/**
 * @constructor
 * This Class provides static methods to find the relative postion based on the current browser.
 */
var Dom = function() {
	
	
};
/**
  * Static Method finds the left most X-axis coordinate of the given object.
  * @param {Object} Object or String id of the DOM desired
  * @param topNode Object or String id to declare the top node to traverse too.
  * measurment will include the topNode
  * @return int - X-axis coordinate
  * @member Dom
  */  
Dom.getLeftX = function(id, topNode) {
	var obj = $(id);
	topNode = (topNode) ? $(topNode) : topNode;
  var curleft = 0;
  
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
			if(topNode && obj == topNode)
			  break;
		}
	} else if (obj.x)
		curleft += obj.x;
	return curleft;
}
Dom.getLeftXAsPx = function(id, topNode) {
	return Dom.getLeftX(id, topNode) + "px";
}
/**
 * Static Method finds the right most X-axis coordinate of the given object.
 * @param {Object} id Object or Id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return int - X-axis coordinate
 * @member Dom
 */
Dom.getRightX = function(id, topNode) {
	var obj = $(id);
  
  return Dom.getLeftX(obj, topNode) + obj.offsetWidth;
}
Dom.getRightXAsPx = function(id,topNode) {
	return Dom.getRightX(id,topNode) + "px";
}
 /**
 * Static Method finds the bottom most Y-axis coordinate of the given object.
 * @param {Object} Object or String id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return int - Y-axis coordinate
 * @member Dom
 */ 
Dom.getBottomY = function(id,topNode) {
	var obj = $(id);
	  
	return Dom.getTopY(obj,topNode) + obj.offsetHeight;
}
Dom.getBottomYAsPx = function(id,topNode) {
	return Dom.getBottomY(id,topNode) + "px";
}
 /**
 * Static Method finds the top most Y-axis coordinate of the given object.
 * @param {Object} Object or String id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return int - Y-axis coordinate
 * @member Dom
 */ 
Dom.getTopY = function(id,topNode) {
	var obj = $(id);
	topNode = $(topNode)
	var curtop = 0;
	var index = 0;
	if (obj.offsetParent) {
		while (obj.offsetParent) {
			index++;
			curtop += obj.offsetTop		  
			obj = obj.offsetParent;
			if(topNode && obj == topNode)
			  break;
		}
	} else if (obj.y)
	curtop += obj.y;
	return curtop;
}
Dom.getTopYAsPx = function(id,topNode) {
	return Dom.getTopY(id,topNode) + "px";
}
/**
 * Static Method finds the Left, Right, Top, Bottom positions of a certain object
 * and returns an object with 4 properties: <br />
 * .leftX <br />
 * .rightX <br />
 * .topY <br />
 * .bottomY <br />
 * @param {Object} id Object or String id of the DOM desired
 * @param topNode Object or String id to declare the top node to traverse too.
 * measurment will include the topNode
 * @return Object - with 4 properties .leftX, .rightX, .topY, bottomY
 * @member Dom
 */
Dom.getBoundries = function(id,topNode) {

	 var boundries = {
		leftX:   Dom.getLeftX(id,topNode),
		rightX:  Dom.getRightX(id,topNode),
		topY:    Dom.getTopY(id,topNode),
		bottomY: Dom.getBottomY(id,topNode),
		width: Dom.getRightX(id,topNode) - Dom.getLeftX(id,topNode),
		height: Dom.getBottomY(id,topNode) - Dom.getTopY(id,topNode)		
	}
	return boundries;	
}
Dom.isInBounds = function(id, x, y) {
	var rectangle = Dom.getBoundries(id);
	
	if(x<=rectangle.leftX || x>=rectangle.rightX)
	  return false;
	if(y<=rectangle.topY || y>=rectangle.bottomY)
	  return false;
	
	return true;
}
//TODO This code has been moved into prototype and should be deleted once we are
// sure there are no other users out there on the site.
function Node() {}

Node.ELEMENT_NODE = 1; 
Node.ATTRIBUTE_NODE = 2; 
Node.TEXT_NODE = 3; 
Node.CDATA_SECTION_NODE = 4; 
Node.ENTITY_REFERENCE_NODE = 5; 
Node.ENTITY_NODE = 6; 
Node.PROCESSING_INSTRUCTION_NODE = 7; 
Node.COMMENT_NODE = 8; 
Node.DOCUMENT_NODE = 9; 
Node.DOCUMENT_TYPE_NODE = 10; 
Node.DOCUMENT_FRAGMENT_NODE = 11; 
Node.NOTATION_NODE = 12;

Node.getFirstElementNode = function(elements) {
	// Hack for Safari's issue with converting to prototype's $A() for childNodes
	var array = new Array();
	for(var i=0; i<elements.length; i++) {
		array.push(elements[i]);
	}
	elements = $A(array);
	return elements.find(function(element) {	
      if(element.nodeType == Node.ELEMENT_NODE)
  		  return element;	
    });
}
Node.getAllElementNode = function(elements) {
   	// Hack for Safari's issue with converting to prototype's $A() for childNodes
    var array = new Array();
	for(var i=0; i<elements.length; i++) {
		array.push(elements[i]);
	}
	return $A(array).inject($(), function(elementNodes, element) {	
      if(element.nodeType == Node.ELEMENT_NODE)
  	    elementNodes.push(element);
      return elementNodes;
    })
}

var isFlashHidden = false;


function hideAllFlashInSafari() {
	if (!isFlashHidden && isMacBrowser()){
		var flashObjs = $A(document.getElementsByTagName('OBJECT')).concat($A(document.getElementsByTagName('EMBED')));
		
		flashObjs.each(function(flashObj) {
			flashObj.parentNode.style.visibility   = 'hidden';	
		})
		isFlashHidden = true;		
	}
}
function showAllFlashInSafari() {
	
	if (isFlashHidden){
		var flashObjs = $A(document.getElementsByTagName('OBJECT')).concat($A(document.getElementsByTagName('EMBED')));
		
		flashObjs.each(function(flashObj) {
			flashObj.parentNode.style.visibility = 'visible';		
		})		
		isFlashHidden = false;		
	}
}
function isMacBrowser() {
	if(navigator.userAgent.toLowerCase().indexOf('mac') > -1) {
		return true;	
	}
	return false;
}

var isSelectHidden = false;
var selectsFound;
function disableAllSelectElementsInIE() {
	if (!isSelectHidden && hideSelectElements()){
		
		if(!selectsFound) {
			var selectObjs = document.getElementsByTagName('SELECT');
			selectsFound = new Array();
			
			for(var i=0; i<selectObjs.length; i++) {
				selectsFound.push(selectObjs[i]);
			}
		}
		for(var i=0; i<selectsFound.length; i++) {
			selectsFound[i].style.visibility = 'hidden';
		}
		isSelectHidden = true;		
	}
}
function enableAllSelectElementsInIE() {
	
	if (isSelectHidden && selectsFound){
		for(var i=0; i<selectsFound.length; i++) {
				selectsFound[i].style.visibility = 'visible';	
		}
		isSelectHidden = false;
	}
		
}
function hideSelectElements() {
	if(document.all) {
		return true;	
	}
	return false;
}


function hideElementsForOverlays() {
	hideAllFlashInSafari();
	disableAllSelectElementsInIE();
}

function showElementsForOverlays() {
	showAllFlashInSafari();
	enableAllSelectElementsInIE();
}



function ajaxUtils() {

}
ajaxUtils.buildAjaxUrl = function(urlPieces) {
	urlPieces.push('?random=' + Math.random());
	return urlPieces.join('');
}




/*
 * Site Constants  
 */
 menuTimeout = 250;
 IMG_ROOT = '/';
 IMG_SHARE = '/'
 var PAGE_ID = '';
 var MODEL_ID = '';
 var PAGE_LOAD = false;
/**
 * Function when called will find all Image tags and cache all images and any Over states that follows a given naming convention.
 * Such as xxxxxx.gif and xxxxxxOver.gif 
 * @type void
 */
 

 
 
OnPageLoadEvents = Class.create();
Object.extend(OnPageLoadEvents.prototype, {
	initialize: function(){
		this.actions = new Array();
		Event.observe(window, 'load', function(){this._executeActions()}.bind(this), false);
	},
	addAction: function(func) {
		
		this.actions.push(func);
	}, 
	_executeActions: function() {
		for(var i=0; i<this.actions.length; i++) {
			var action = this.actions[i];
			action.apply(window,[]);
		}
	}	
});

var onPageLoadEvents = new OnPageLoadEvents();



function cacheAllImages() {
	var cacheit = function(image, tag){
		if(image.src.indexOf(tag) == -1){
			var img = new Image();
			img.src = image.src.replace(new RegExp('(.*?)(\.(gif|png|jpg))', 'i'), '$1'+tag+'$2');
		}
	}
	/*
	$NL(document.getElementsByClassName('hover_Ov', document, 'img')).each(function(element){ 
		cacheit(element, 'Ov'); 
		Event.observe(element, 'mouseover', function(){swapOn(element, 'Ov'); });
		Event.observe(element, 'mouseout', function(){swapOut(element, 'Ov'); });
	});
	*/
	$NL(document.getElementsByClassName('hover_Over', document, 'img')).each(function(element){
		cacheit(element, '_over'); 
		Event.observe(element, 'mouseover', function(){swapOn(element, '_over'); });
		Event.observe(element, 'mouseout', function(){swapOut(element, '_over'); });
	});
	/*
	$NL(document.getElementsByClassName('hover_On', document, 'img')).each(function(element){ cacheit(element, 'On') });
	*/
}
onPageLoadEvents.addAction(cacheAllImages);
//vent.observe(window, 'load', cacheAllImages, false);



/**
 * Function can be used to swap states of any image that follows a given naming convention. 
 * Such as xxxxxx.gif (off state) and xxxxxxOver.gif (on state).
 * You can use the function on an image itself like so:  
 * &gt;img src="./vn_btnAll.gif" onmouseOver="swapOver(this);" onmouseOut="swapOver(this);" &lt;
 * or you can add it to any element, like say, an A Href, like so:
 *  &lt;a href="./goSomeWhere.html" onmouseOver="swapOver('imageId');" onmouseOut="swapOver('imageId');" &lt; &gt;img src="..../" id="imageId" &lt; &gt;a&lt;
 * @param img String/Object
 * @type void
 */
function swapOver(img, suffix) {
	if(!suffix) { suffix = '_over'; }
	var img = $(img);
	if(img.src.indexOf(suffix) > -1 || img.src == img.getAttribute('hover_src')) {
		if(img.getAttribute('nohover_src'))
			img.src = img.getAttribute('nohover_src');
		else
			img.src = img.src.replace(suffix, '');
	}
	else {
		if(img.getAttribute('hover_src')){
			img.setAttribute('nohover_src', img.src);
			img.src = img.getAttribute('hover_src');
		}
		else{
			var array = img.src.split('.');
			var src = "";		
			// Loop through all but the last
			for(var i=0; i<(array.length - 1); i++) {
				if(i!=0)
				 src += '.';
				src += array[i];
			}
			img.src = src + suffix + '.' + array[array.length-1];
		}
	}
}

function swapOn(img, suffix) {
	if(!suffix) { suffix = '_over'; }
	var img = $(img);
	if(img.src.indexOf(suffix) > -1) {
		// Do nothing.
	}
	else if(img.getAttribute('hover_src')){
		img.src = img.getAttribute('hover_src');
	}
	else {
		var array = img.src.split('.');
		var src = "";		
		// Loop through all but the last
		for(var i=0; i<(array.length - 1); i++) {
			if(i!=0)
			 src += '.';
			src += array[i];
		}
		img.src = src + suffix + '.' + array[array.length-1];
	}
}

function swapOut(img, suffix) {
	if(!suffix) { suffix = '_over'; }
	var img = $(img);
	if(img.src.indexOf(suffix) > -1 || img.src == img.getAttribute('hover_src')) {
		if(img.getAttribute('nohover_src'))
			img.src = img.getAttribute('nohover_src');
		else
			img.src = img.src.replace(suffix, '');
	}
}

function swapOut_click(event, img, suffix){
	if(!suffix) { suffix = '_over'; }
	var img = $(img);
	if(img.src.indexOf(suffix) > -1) {
		if(!Dom.isInBounds(img, Event.pointerX(event), Event.pointerY(event))){
			img.src = img.src.replace(suffix, '');
		}
	}

}

Object.extend(Element, {
	removeChildren: function(element){
		while(element.childNodes.length){
			Element.remove(element.childNodes[0]);
		}
	}
});

/**
*	Replaces ~search_string~ with replace_string in input_string.
*	Returns result.
*/

Object.extend(String.prototype, {
	templateReplace: function(search_string, replace_string){
		//var regEx = new RegExp('~' + search_string + '~', 'g')
		var output = this.replace('~' + search_string + '~', replace_string);

		return output;
	}
});




	
	
	












	/**
 * @fileoverview  tourNavigation.js contains all components for the Vehicle Navigation menu.
 * @author Aaron J Pedersen (apederson@dhapdigital.com)
 * <br/> <br/>
 * Documentation is generated by JSDoc (http://jsdoc.sourceforge.net/)
 */

/**
 * Class holds the needed information for a paticular Category of Cars.<br/>
 * Example: Luxury Sedans, which contain LS, GS, ES, and IS models of cars.
 * @constructor
 */
function VehicleCategory(id, name) {
	this.id = id;
	this.name = name;
	this.models = $A();
	this.domHotSpot = null;
	this.domNavigation = null;
	this.domOverlay = null;

  /**
   * Method adds a VehicleModel Object to an array.   
   * @param model VehicleModel Model Object representing a Lexus Model
   * @type void
   * @member VehicleCategory
   */
  this.addModel = function(model) {
  	  model.categoryId = this.id;
    	this.models.push(model);
  }
   /**
   * Method returns a VehicleModel Object from the current array. 
   * @param model VehicleModel Model Object representing a Lexus Model
   * @type VehicleModel
   * @member VehicleCategory
   */
  this.getModel = function(key) {
  	return this.models.find(function(model){
  		if(model.id.indexOf(key) >= 0)
  		  return model;
  	})
  }
}
VehicleCategory.prototype.toString = function() {
	return "Category (Object)";
}
/** Type Declarations Class/Instance **/
VehicleCategory.type = 'Category';
VehicleCategory.prototype.type = 'Category';
/*******************************************************************************/
/**
 * Class holds the needed info for a paticular Model of Car.<br/>
 * Example: LS.
 * @constructor
 */
function VehicleModel(id, name, startingPrice, endingPrice,code) {
	this.id = id.toLowerCase();
	this.name = name;
	this.startingPrice = startingPrice;
	this.endingPrice = endingPrice;
	this.code = code;
	this.categoryId = 0;
	this.features = $A();
	this.domHotSpot = null;
	this.domNavigation = null;
	this.domOverlay = null;
	
	/**
   * Method adds a VehicleFeature Object to an array.
   * @param feature VehicleFeature VehicleFeature Object representing a Lexus Model Feature
   * @type void
   * @member VehicleModel
   */
	this.addFeature = function(feature) {
		this.features.push(feature);
	}
	/**
	 * Method returns a string representing the price of the tour
	 * @type String
	 * @member VehicleModel
	 */
	this.getPriceAsString = function() {
		//return "Starting at " + this.startingPrice + 
		//       ((this.endingPrice) ? " " + this.endingPrice : "" ) + ' ';'
		return ' ';
	}
}
VehicleModel.prototype.toString = function() {
	return "Model (Object)";
}
/** Type Declarations Class/Instance **/
VehicleModel.type = 'Model';
VehicleModel.prototype.type = 'Model';

/*******************************************************************************/

/**
 * Class holds current State of the navigation<br/>
 * @constructor
 */
function tourNavigationState () {
	this._navShowing = false;
	this._categoryShowing = false;
	this._modelShowing = false;
	this._currentActive = null;

	/**
	 * @member tourNavigationState
	 * Method sets the current state of the navigation.
	 * If false, method will also update categoryShowing and modelShowing attributes
	 * @param boolean - state True/False 
	 */
	this.setNavShowing = function (state) {
		this._navShowing = state;
		
		// update dependencies if false
		if(!state) {
			this.setCategoryShowing(false);
			this.setModelShowing(false);
			this.setCurrentActive(null);
		}
	}
	/**
	 * 
	 * Method sets categoryShowing attribute, to indicate if a Category Navigation is current visible.
	 * If True, will also set _navShowing to true and will set _modelShowing to false, since only a
	 * model or category can be shown at any point.
	 * @param boolean - state True/False 
	 * @member tourNavigationState
	 */
	this.setCategoryShowing = function (state) {
		this._categoryShowing = state;
		
		// update dependencies if true
		if(state) {
			this.setNavShowing(true);
			this.setModelShowing(false);
		}
	}
	/**
	 * Method sets modelShowing attribute, to indicate if a Model Navigation is current visible.
	 * If True, will also set _navShowing to true and will set _categoryShowing to false, since only a
	 * model or category can be shown at any point.
	 * @param boolean - state True/False 
	 * @member tourNavigationState
	 */
	this.setModelShowing = function (state) {
		this._modelShowing = state;
		
		// update dependencies if true
		if(state) {
			this.setNavShowing(true);
			this.setCategoryShowing(false);
		}
	}
  /**
	 * Method sets the current object that is active on the vehicle navigation
	 * @param object - Category or Model Object
	 * @member tourNavigationState
	 */
	this.setCurrentActive = function(object) {
		this._currentActive = object;
		
		if(!object)
		  return;
	  if(object.type == VehicleCategory.type) {
	  	this.setCategoryShowing(true);	  	
	  }
	  else {
	  	this.setModelShowing(true);	  	
	  }
	}
  /**
	 * Method gets the current object that is active on the vehicle navigation
	 * @type object - Category or Model Object
	 * @member tourNavigationState
	 */
	this.getCurrentActive = function() {
		return this._currentActive;		
	}
	/**
	 * Method returns true/false depending on if a vehicle navigation is current showing
	 * @type boolean
	 * @member tourNavigationState
	 */
	this.isNavShowing = function() {
		return this._navShowing;
	}
	/**
	 * Method returns true/false depending on if a vehicle Category navigation is current showing
	 * @type boolean
	 * @member tourNavigationState
	 */
	this.isCategoryShowing = function() {
		return this._categoryShowing;
	}
	/**
	 * Method returns true/false depending on if a vehicle model navigation is current showing
	 * @type boolean
	 * @member tourNavigationState
	 */
	this.isModelShowing = function() {
		return this._modelShowing;
	}
}

/*******************************************************************************/
/**
 * Class acts as the controller to handle all interaction of the navigation bar<br/>
 * @constructor
 */
function tourNavigationManager() {
	this.categories = $A();
	this.navState = new tourNavigationState();
    this.navView = new tourNavigationView();
	this.timeOutId;
	this.timeOutSet = false;
	//this.ajaxQueue;
	this.imageCache = new Array();
	
	this.backdrop;	

	/**
	 * Method will be called on instantiation of this class.
	 * Exact features are TBD
	 * @member tourNavigationManager
	 */
	this.init = function() {
	this._setOnState();	
      
	 // Add onload event
	 onPageLoadEvents.addAction(this.init_onload.bindAsEventListener(this));
	 Event.observe(window, 'unload', Event.unloadCache);
	}
	this.init_onload = function() {
	  this._setupNavigation();
	  this._cacheMenuImages();
      this._preLoadMenus();  
	}
	/**
   * Method adds a Category Object to an array.   
   * @param model - Category Category Object representing a Lexus Category
   * @type void
   * @member tourNavigationManager
   */
  this.addCategory = function(category) {
    this.categories.push(category);
  }
  /**
   * Method returns a Category Object from the current array.
   * @param model - Category Category Object representing a Lexus Category
   * @type void
   * @member tourNavigationManager
   */
  this.getCategory = function(key) {
  	return this.categories.find(function(category){
  		if(category.id.indexOf(key.toUpperCase()) >= 0) {
  		  return category;
  		}
  	})
  }
  /**
   * Function will control the action of showing a Model or Category Vehicle Navigation Item
   * @param domObject Dom Element
   * @param categoryId String 
   * @param modelId String
   * @type void
   * @member tourNavigationManager
   */
  this.showNavigation = function (domObject, categoryId, modelId) {
  	var dataObject = this._getDataObject(categoryId, modelId);

  	if(dataObject == null)
  	  return;
  	if(this.timeoutSet) {
  		clearTimeout(this.timeoutId);
  		this.timeoutSet = false;
  	}
	//this.closeDisclaimer();
	
  	// attach Dom Hot Spot if not already done.
  	if(!dataObject.domHotSpot)
  	  dataObject.domHotSpot = domObject;
  	
  	if(this.navState.isNavShowing()) {
  	  if(this.navState.getCurrentActive() == dataObject)
  	    return;
  	  else
  		this.hideNavigation();
  	}
  	// Hide all flash objects in safari
  	hideElementsForOverlays();
  	if(dataObject.type == VehicleCategory.type) {
  		this._showCategoryNavigation(dataObject);
  	}
  	else {
  		/*
  		 * IE needs to have an expliced hide on the body to make sure
  		 * the navigation actually hides when you roll off the menu
  		 */
        if(document.all)
  		  Event.observe(document.body, 'mouseout', ieBodyhideNavigation, true);
  		  
  		this._showModelNavigation(dataObject);
  	}

  }
  /**
   * Method will control the action to hide a Model or Category Vehicle Navigation Item.
   * @param eventObject Event
   * @type void
   * @member tourNavigationManager
   */
  this.hideNavigation = function (eventObject) {
  	var currentActive = this.navState.getCurrentActive();

  	// Only Hide if its ok
  	if(!this._shouldWeHideNavigation(eventObject))
  	  return;
    
    var wrapper = $('wrapper');
    
    // Hide Navigation
    currentActive.domNavigation.style.display = 'none';
    currentActive.domOverlay.style.display = 'none';
    
    /* 
     * Removed from the actual DOM because IE has issues displaying if we dont remove,
     * since these elements could be cloned.
     */
    wrapper.removeChild(currentActive.domNavigation);
    wrapper.removeChild(currentActive.domOverlay);    
    /*
     * Removed IE specific body event
  	 * 
  	 */
    if(document.all)
      Event.stopObserving(document.body, 'mouseout', ieBodyhideNavigation, true);
      
    this.navState.setNavShowing(false);
    
    // Show all flash objects in safari
  	showElementsForOverlays();
  }
  /**
   * Method will activate a timer and execute hideNavigation().
   * @member tourNavigationManager
   */
  this.hideNavigationWithTimeOut = function(eventObject) {
    if(this.timeoutSet)
      return;
    // Only Hide if its ok
  	if(!this._shouldWeHideNavigation(eventObject))
  	  return;

  	this.timeoutId = setTimeout("navigationManager.hideNavigation()",menuTimeout);
  	this.timeoutSet = true;
  }
   /**
   * Method will figure out wether or not to hide the current openned navigation
   * @member tourNavigationManager
   */
  this._shouldWeHideNavigation = function (eventObject) {
  	var currentActive = this.navState.getCurrentActive();
  	
  	// Nothing to do if we dont have a nav showing
  	if(!currentActive || !currentActive.domNavigation || !currentActive.domOverlay)
  	  return false; 	
  	// If in bounds, do nothing
  	if(this.isEventInBounds(currentActive.domOverlay, eventObject) || this.isEventInBounds(currentActive.domNavigation, eventObject))
  	  return false;
  	return true;
  }
  /**
   * Method clears the current hideNavigation timeout setting if there is one.
   * @member tourNavigationManager
   */
  this.clearTimeOut = function() {
  	if(this.timeoutSet) {
  	  clearTimeout(this.timeoutId);
  	  this.timeoutSet = false;
  	}
  }
  /**
   * Method will handle the specific task to show a Category Navigation Item
   * @param category VehicleCategory
   * @type void 
   * @member tourNavigationManager
   */
  this._showCategoryNavigation = function(category) {
  	var status = this.navView.showCategoryNavigation(category);
  	this.navState.setCurrentActive(category);
  }
  /**
   * Method will handle the specific task to show a Model Navigation Item
   * @param category VehicleCategory
   * @type void 
   * @member tourNavigationManager
   */
  this._showModelNavigation = function(model) {
  	var status = this.navView.showModelNavigation(model);
  	this.navState.setCurrentActive(model);
  }
  /**
   * Method figures out if the cursor of the given Event is within the given DOM Element
   * @param domObject DOMElement 
   * @param eventObject Event
   * @type void 
   * @member tourNavigationManager
   */
  this.isEventInBounds = function(domObject, eventObject) {	
  	if(!eventObject)
  	  return false;
	return Dom.isInBounds(domObject,Event.pointerX(eventObject), Event.pointerY(eventObject));
  }
  /**
   * Method pre caches all menu images
   * @type void 
   * @member tourNavigationManager
   */  
  this._cacheMenuImages = function() {
		
	for(var x=0; x<this.categories.length; x++) {
		var category = this.categories[x];
	
	    // Cache Model Navigation Images
	    for(var i=0; i<category.models.length; i++) {
	    	var model = category.models[i];
	    	this._cacheModelNavigationImages(model.name);	  
	    }
	}
  }
  /**
   * Method returns either a Category or a Model object based on if a model is passed in.
   * @param category String
   * @param model String (optional)
   * @type VehicleCategory/VehicleModel
   * @member tourNavigationManager
   */
  this._getDataObject = function(category, model) {
  	var dataObject;
  	if(!model)
  	  dataObject = this._getCategoryObject(category);
  	else
  	  dataObject = this._getModelObject(category, model);
  	return dataObject;
  }
  /**
   * Method returns either a Category object based on the id passed in
   * @param categoryId String
   * @type VehicleCategory
   * @member tourNavigationManager
   */
  this._getCategoryObject = function(categoryId) { 
  	return this.getCategory(categoryId);
  }
  /**
   * Method returns either a Model object based on the id passed in
   * @param modelId String
   * @type Model
   * @member tourNavigationManager
   */
  this._getModelObject = function(categoryId, modelId) {
  	var category = this.getCategory(categoryId);
  	return category.getModel(modelId);
  }
  this._setOnState = function() {
  	if(typeof( window[ 'MODEL_ID' ] ) != "undefined") {      
      var onStateImg;
      // Check if the current page we are on is a model
      for(var i=0; i<this.categories.length; i++) {
      	var category = this.categories[i];
      	for(var x=0; x<category.models.length; x++) {
      		var model = category.models[x];
      		// Determine if we are on this current Model, change image to 'On' state, and cache image
  	 	  	if(MODEL_ID.toLowerCase() == model.id.toLowerCase()) {
  	 	    	onStateImg = $( [model.categoryId.toLowerCase(), '_' , model.id.toLowerCase()].join('') );
  	      		break;
  	      	}
      	}
      }

  	  // If we are not on a Model, look if we are on a misc Link
  	  if(!onStateImg) {
  		onStateImg = $( ['mv', MODEL_ID].join('') );
  	  }
  	
  	  if(onStateImg) {
  		onStateImg.src = onStateImg.src.replace('.gif', 'On.gif');
  		this._cacheImage(onStateImg.src);
  	  }
    }
  }
  /**
   * Method executes the preliminary setup of the navigation bar. 
   * Including: caching of all images and rollovers, and all needed
   * events.
   * @member tourNavigationManager
   */
  this._setupNavigation = function() {  
  	 var mvCategoryContainers = document.getElementsByClassName('mvCategoryContainer');
  	 var mvMiscLinkContainers = document.getElementsByClassName('mvMiscLinkContainer');
  	 
  	 // Category and Model: Add Events and cache all images
  	 for(var i=0; i<mvCategoryContainers.length; i++) {
  	 	var mvCategoryContainer = mvCategoryContainers[i];
  	 	var mvCategory = $A(document.getElementsByClassName('mvCategory',mvCategoryContainer));
  	 	var mvModels = $A(document.getElementsByClassName('mvModels',mvCategoryContainer));
  	 	
  	 	if(mvCategory != '') {
  	 	  this._setupCategory(mvCategory[0]);
  	 	  this._setupModels(mvModels[0]);
  	 	}
  	 }
  	 
  	 // Misc Links: Add Events and cache all images
	for(var i=0; i<mvMiscLinkContainers.length; i++) {
  	 	var miscDivs = ($NL(mvMiscLinkContainers[i].childNodes)).elements();
		
		for(var x=0; x<miscDivs.length; x++) {
			var miscDiv = miscDivs[x];
			var href = ($NL(miscDiv.childNodes)).firstElement();
  	 		var img = ($NL(href.childNodes)).firstElement();
  	 		
  	 		this._cacheOnOverImages(img.src);
  	 		// Cache Images
  	        //this._cacheImage(img.src);
  	        
  	        // Cache Over State
  	        //var overImgSrc = img.src.replace('On.gif', '.gif');
  	        //this._cacheImage(overImgSrc.replace('.gif', 'Over.gif'));
  	 		// Events
  	 		if(img.src.indexOf('On.gif') == -1) {
  	 			Event.observe(img, 'mouseout', function() {swapOver(this);}.bind(img), false);
  	 			Event.observe(img, 'mouseover', function() {swapOver(this);}.bind(img), false);
  	 		}
		}
	}
  }
  /**
   * Method prebuild category overlays.
   * @member tourNavigationManager
   */
  this._preLoadMenus = function() {  	
  	this.categories.each(function(category){ 
  	   this.navView.buildCategoryOverlay(category); 	  
  	}.bind(this));
  }
  /**
   * Method managers the caching of all navigation category images and
   * adds swapOver and showNavigation events to the category position of the 
   * Navigtaion bar.
   * @member tourNavigationManager
   */
  this._setupCategory = function(mvCategory) { 
    // Find Category Image and Cache
    var categoryImg = ($NL(mvCategory.childNodes)).firstElement();
    
    this._cacheOverImage(categoryImg.src);
    
    // Add Events to Category
    Event.observe(categoryImg, 'mouseover', function() {swapOver(categoryImg);this.hideNavigation()}.bindAsEventListener(this), false);
    Event.observe(categoryImg, 'mouseout', function() {swapOut(categoryImg);}, false);
    Event.observe(categoryImg, 'click', function() {swapOut(categoryImg);this.showNavigation(categoryImg, categoryImg.id)}.bindAsEventListener(this), false);   
  }
  this._cacheOverImage = function(src) {
    var overImgSrc = src.replace('_over.gif', '.gif');
  	this._cacheImage(overImgSrc.replace('.gif', '_over.gif'));
  }
  this._cacheOnOverImages = function(src) {
  	this._cacheImage(src);
  	this._cacheOverImage(src)        
  }
  /**
   * Method caches image in browser.
   * @member tourNavigationManager
   */
  this._cacheImage = function(src) {
  	// Check if we are a third party navigation and we dont have a full url
  	if(window.LEXUS_ROOT_ASSET && src.indexOf('http://') == -1) {
  		src = window.LEXUS_ROOT_ASSET + src;
  	} 

    var newImage = new Image();
  	newImage.src = src;
  	this.imageCache.push(newImage);
  }
  /**
   * Method handels the setup of all Navigation model images and
   * addes showNavigation to a mouseover event on each model.
   * @member tourNavigationManager
   */
  this._setupModels = function(mvModel) {
  	 var modelLinks = ($NL(mvModel.childNodes)).elements();

  	 // Loop through each Model, cache and apply needed events
  	 modelLinks.each(function(modelLink) {
  	 	var modelImg = modelLink.firstChild;
  	 	var ids = modelImg.id.split('_');
  	 	var catId = ids[0];
  	 	var modId = ids[1];

  	 	// Cache Model Image and Overlay
  	 	this._cacheOverImage(modelImg.src);

  	 	// Add Events to Model
  	 	Event.observe(modelImg, 'mouseover', function() {this.showNavigation(modelImg, catId, modId)}.bindAsEventListener(this), false);
  	 }.bind(this)); 	 
  }
  /**
   * Method controls the caching of all Navigation Menu Model images.
   * @member tourNavigationManager
   */
  this._cacheModelNavigationImages = function(modelName) {
  	this._cacheImage(this.navView._replaceModel(this.navView.IMG_HEAD,modelName));
    this._cacheImage(this.navView._replaceModelText(this.navView.IMG_PHOTO,modelName));
    this._cacheImage(this.navView._replaceModel(this.navView.IMG_OVERVIEW,modelName));
    this._cacheImage(this.navView._replaceModel(this.navView.IMG_BUILD,modelName));
  }



}
/**
 * Class provides static methods to build and display the navigation onto the browser
 * @constructor
 */
function tourNavigationView() {
	this.ROOT_IMG = '/images/nav/tourNav/';
	this.MODEL_REPLACE = 'model';
	this.MODEL_CODE_REPLACE = 'model-code';
	this.CATEGORY_REPLACE = 'category';
	//this.IMG_OVERVIEW = this.ROOT_IMG + 'tn_btn_~' + this.MODEL_REPLACE + '~Ov.gif';
	//this.IMG_GALLERY = this.ROOT_IMG + 'tn_btn_~' + this.MODEL_REPLACE + '~Pg.gif';
	//this.IMG_BUILD = this.ROOT_IMG + 'tn_~' + this.MODEL_REPLACE + '~Bld.gif';
	//this.IMG_HEAD = this.ROOT_IMG + 'tn_hdr_~'  + this.MODEL_REPLACE + '~_head.gif';
	//this.IMG_PHOTO = this.ROOT_IMG + 'tn_img_~'  + this.MODEL_REPLACE + '~_photo.jpg';
	this.CAT_OVERLAY = this.ROOT_IMG + 'tn_~' + this.CATEGORY_REPLACE + '~On.gif'
	                                                                         
	/**
	 * Method will control the operatations to build and show a Cateogry Navigation.
	 * Will return true if the Model navigation is displayed
	 * @param category VehicleCategory
	 * @type boolean
	 * @member tourNavigationView
	 */
	this.showCategoryNavigation = function(category) {
	    var wrapper = $('wrapper');
		this.buildCategoryOverlay(category);
		this.buildCategoryNavigation(category);
	
		// If we could not build, return, nothing else to do
		if(!category.domOverlay)
		  return;
	
		// Make sure we have the Model class NOT category
	    wrapper.appendChild(category.domOverlay);
	    category.domOverlay.style.top = Dom.getTopYAsPx(category.domHotSpot,wrapper);
	    category.domOverlay.style.left = Dom.getLeftXAsPx(category.domHotSpot,wrapper);
	    category.domOverlay.style.display = 'block';
			
		if(!category.domNavigation)
		  return
		category.domNavigation.style.top = Dom.getBottomYAsPx('mvContainer',wrapper);
	
		/*
		 * Need to calculate the position of category div
		 * This will take into consideration the total size of the
		 * category menu and compare with the total size of the navigation bar.
		 * This is to make sure we do NOT position past the right most point of
		 * the navigation bar.
		 */
		var catTotalSize = 1; // we start with one because the category div has to make up one extra right px
		var navTotalSizeRight = 0;
		var catTotalSizeRight = 0;
	
	    /* 
	     * Calculate the total width based on how many models we are showing
	     * also adds the left position of the dom hot spot (category image)
	     */
		for(var i=0; i<category.models.length; i++) {
			catTotalSize += 234;
			if(i>0) {
				catTotalSize += 1;
			}
		}
		catTotalRight = catTotalSize + Dom.getLeftX(category.domHotSpot,wrapper) - 5;
	
	    // calculate the total size and left position of Navigation bar.
	    var mvBoundry = Dom.getBoundries('mvContainer',wrapper);
	    navTotalSize = mvBoundry.width + mvBoundry.leftX
	    
	    /* 
	     * If are category div is larger than the navigation bar then we will 
	     * line it up on the right corner
	     */
		if(catTotalRight  >  navTotalSize) {
		  category.domNavigation.style.left = (mvBoundry.rightX - catTotalSize) + 'px';
		}
		/*
		 * Else position the menu at the left edge of the triggering category image
		 */
		else {
		  category.domNavigation.style.left = (Dom.getLeftX(category.domHotSpot,wrapper) - 5) + 'px';
		}
		wrapper.appendChild(category.domNavigation);
		category.domNavigation.style.display = 'block';
	    
	}
	/**
	 * Method will control the operatations to build and show a Model Navigation.
	 * Will return true if the Model navigation is displayed
	 * @param model VehicleModel
	 * @type boolean
	 * @member tourNavigationView
	 */
	this.showModelNavigation = function(model) {
		var wrapper = $('wrapper');
		this.buildModelNavigation(model);
	
		// if we could not build, return, nothing else to do
		if(!model.domNavigation)
		  return

        // show and position navigation menu	
		wrapper.appendChild(model.domNavigation);	
		model.domNavigation.style.top = Dom.getBottomYAsPx('mvContainer',wrapper);
		model.domNavigation.style.left = (Dom.getLeftX(model.domHotSpot,wrapper) - 8) + 'px';
	    model.domNavigation.style.display = 'block';
	    
	    
		this.buildModelOverlay(model);
		
		// if we could not build, return, nothing else to do
		if(!model.domOverlay)
		  return
		
		wrapper.appendChild(model.domOverlay);
		model.domOverlay.style.top = Dom.getTopYAsPx(model.domHotSpot,wrapper);
		// We move the image 4 px left because the image has 3px on each side more of space
		model.domOverlay.style.left = (Dom.getLeftX(model.domHotSpot,wrapper) - 4) + 'px';
		
		model.domOverlay.style.display = 'block';
	    
		return true;
	}
	/**
	 * Method will build the DIV Object Navigation representing a specific Model.
	 * @param model VehicleModel
	 * @type void
	 * @member tourNavigationView
	 */
	this.buildModelNavigation = function(model) {
	    if(model.domNavigation)
		  return
		
		var nav = $('mvNavigationModel_Template').cloneNode(true);
	    var links = nav.getElementsByTagName('a');
	    //var linkTxts = nav.getElementsByTagName('span');
	    var imgs = nav.getElementsByTagName('IMG');
	
	// Set all Links
	   for(var i=0; i<links.length; i++) {
	   	 var link = links[i];
	   	 var linkImg = link.firstChild;
	   	 link.href = this._replaceModelLink(link.href, model.name, model.code);	   	
	   
		 if(link.parentNode.className == 'mvNavigationLink') {
 		 	Event.observe(link, 'mouseover', function() {swapOver(this)}.bind(linkImg) );   	
 		 	Event.observe(link, 'mouseout', function() {swapOut(this)}.bind(linkImg) );
		 }
	   }
	/*   
	// Set all Link Texts
		for(var i=0; i<linkTxts.length; i++){
			var txt = linkTxts[i];
			if(txt.className == 'vn_snBY') txt.innerHTML = this._replaceModelText(txt.innerHTML, model.name);	
		}*/
	   
	// Set all images
		for(var i=0; i<imgs.length; i++) {
			var img = imgs[i];
			var pieces = img.src.split('#');
			img.src = this._replaceModelText(pieces[pieces.length - 1], model.name);	   	
	   		img.alt = this._replaceModel(img.alt, model.name.replace(new RegExp("h$"),' Hybrid'));		   	
		}

		nav.id = 'mvNav_' + model.categoryId + "_" + model.name;
    
	//Build Features
	  var containerDivs = nav.getElementsByTagName('DIV');
		var topChildrenDivs = containerDivs[0].getElementsByTagName('DIV');
		var featuresDiv = topChildrenDivs[topChildrenDivs.length - 1];
		var startingPrice = topChildrenDivs[topChildrenDivs.length - 2];
		
		for(var i=0; i<model.features.length; i++) {
			var featureDiv = document.createElement('DIV');
			featureDiv.className = 'mvNavigationFeature';
			featureDiv.innerHTML = model.features[i];
			
			/*
			var disclaimers = featureDiv.getElementsByTagName('A');
			
			for(var x=0; x<disclaimers.length; x++) {
				var disclaimer = disclaimers[x];
				
				disclaimer.href = 'javascript:void(0);'
				
				disclaimer.onclick = function() {navigationManager.launchDisclaimer(this);}.bind(model.name);
			}
			*/
			featuresDiv.appendChild(featureDiv);		
		}
		
		
	//Build Staring Price
		startingPrice.innerHTML = model.getPriceAsString();
		
		
		
    //Add Events
		if(document.all)
		  Event.observe(nav, 'mouseout', function() {event.cancelBubble = true;navigationManager.hideNavigationWithTimeOut(event)})
		else
		  Event.observe(nav, 'mouseout', function(event) {event.cancelBubble = true;navigationManager.hideNavigationWithTimeOut(event)})
		
		Event.observe(nav, 'mouseover', function() {navigationManager.clearTimeOut()})

		model.domNavigation = nav;
		
		this._addLegalDisclaimerEvents(model.name,model.domNavigation);
		$('mvContainer').appendChild(model.domNavigation);
	}
	/**
	 * Method will build the DIV Object for the Model Overlay representing a specific Model.
	 * @param model VehicleModel
	 * @type void
	 * @member tourNavigationView
	 */
	this.buildModelOverlay = function(model) {
		if(model.domOverlay)
		  return

		var overlayDiv = document.createElement('DIV');
		var overlayHref = document.createElement('A');
		var overlayImg = document.createElement('IMG');
	
		overlayDiv.className = 'mvOverLay';
	
		// browser conflict
		if(document.all)	
		  overlayDiv.onmouseout = function() { event.cancelBubble = true;navigationManager.hideNavigationWithTimeOut(event)};
		else
		  overlayDiv.onmouseout = function(event) {event.cancelBubble = true;navigationManager.hideNavigationWithTimeOut(event)};
	
		overlayDiv.onmouseover = function() {navigationManager.clearTimeOut()};

		overlayHref.href = model.domHotSpot.parentNode.href;
	    overlayHref.target = model.domHotSpot.parentNode.getAttribute('TARGET');

	    var imagepath = model.domHotSpot.src.split('tourNav');
		overlayImg.src =  imagepath[0]  + 'tourNav/tn_btn_' + model.name + "_over.gif";
		overlayImg.alt = model.name.replace(new RegExp("h$"),' Hybrid');

		overlayHref.appendChild(overlayImg);
		overlayDiv.appendChild(overlayHref);
	
		model.domOverlay = overlayDiv;
		$('mvContainer').appendChild(model.domOverlay);  
	}
	/**
	 * Method will build the DIV Object Navigation representing a specific Model.
	 * @param model VehicleModel
	 * @type void
	 * @member tourNavigationView
	 */
	this.buildCategoryNavigation = function(category) {
		if(category.domNavigation)
		  return
	   
		var categoryDiv = document.createElement('DIV');
		categoryDiv.id = 'mvCat_' + category.name;
		categoryDiv.className = 'mvNavigationCategory';
	    
	    for(var i=0; i<category.models.length; i++) {    	
	  	// Build Model Navigation if needed
		  	if(!category.models[i].domNavigation) {
		  	  this.buildModelNavigation(category.models[i]);
		  	  
		  	}
		
		  	var cloneDiv = category.models[i].domNavigation.cloneNode(true);
		  	
		  	cloneDiv.id = category.name + '_' + cloneDiv.id;
		  	cloneDiv.className = 'mvNavigationModelForCategory';
		  	cloneDiv.style.display = 'block';
		
			this._addLegalDisclaimerEvents(category.models[i].name,cloneDiv);

		  	if(i>0) {
		        var catDivider = document.createElement('DIV');
		        var catDividerImg = document.createElement('IMG');
		        
		      catDivider.className = 'mvNavigationCategoryDivider';	        
		      catDividerImg.src = IMG_ROOT + 'spacer.gif'
		        
		  	  catDivider.appendChild(catDividerImg);
		  	  categoryDiv.appendChild(catDivider)
		  	}
		  	categoryDiv.appendChild(cloneDiv);
	  }
	  category.domNavigation = categoryDiv;
	  $('mvContainer').appendChild(categoryDiv);
	}
	/**
	 * Method will build the DIV Object for the Model Overlay representing a specific Model.
	 * @param model VehicleModel
	 * @type void
	 * @member tourNavigationView
	 */
	this.buildCategoryOverlay = function(category) {
	  if(category.domOverlay)
	    return

	  var overlayDiv = $('mvCategoryOverLay_Template').cloneNode(true);
	  var divs = overlayDiv.getElementsByTagName('DIV');
	  var topDiv = divs[0];
	
	  overlayDiv.id = 'overlayDiv' + category.id;
	
	  // Create Category Overlay Image Example: 'vn_ESOn.gif'
	  var imgTopDiv = ($NL(topDiv.childNodes)).firstElement();
	  var pieces = imgTopDiv.src.split('#');
	  imgTopDiv.src = this._replaceCategory(pieces[pieces.length - 1], category.id);
      imgTopDiv.alt = category.name;
	  //Need to calc cached images width;
	  var imgWidth = new Image();
	  imgWidth.src = imgTopDiv.src;
	  // Have to set a width in IE
	  if(document.all)
	    overlayDiv.style.width = imgWidth.width;

	  category.domOverlay = overlayDiv;
	  $('mvContainer').appendChild(category.domOverlay);
	}
	this._addLegalDisclaimerEvents = function(modelId, div) {
		var featureDivs = document.getElementsByClassName('mvNavigationFeatures', div, 'DIV');
		var priceDiv = document.getElementsByClassName('mvNavigationPrice', div, 'DIV')[0];
		
		for(var x=0; x<featureDivs.length; x++) {
			var featureDiv  = featureDivs[x];
			
			var disclaimers = featureDiv.getElementsByTagName('A');
			
			for(var x=0; x<disclaimers.length; x++) {
				var disclaimer = disclaimers[x];
				
				disclaimer.href = 'javascript:void(0);'
				

			}
		}
		
		var hrefs = priceDiv.getElementsByTagName('A'); 
		var disclaimer;
		//if(hrefs.length > 0) {
			disclaimer = hrefs[0];
		
		/* }
		else {
			disclaimer = document.createElement('SPAN');
			disclaimer.className = 'span_disclaim';
			disclaimer.innerHTML = '[<a href="javascript:void(0);" class="link_disclaim">1</a>]';
			priceDiv.appendChild(disclaimer);
		}	
		*/
		
	}
	/**
	 * Method replaces all ~model~ wildcards with actual model name for links
	 */
	this._replaceModel = function(str, name) {
	name = name.replace('-', ' ');
	  if(name.match(new RegExp("h$"))) {
	  	name = name.replace(new RegExp("h$"),'_HYBRID');
	  }
	  return str.templateReplace(this.MODEL_REPLACE, name);	
	}
	/**
	 * Method replaces all ~model~ wildcards with actual model name
	 */
	this._replaceModelText = function(str, name) {
		//name = name.toLowerCase();
	  if(name.match(new RegExp("h$"))) {
	  	name = name.replace(new RegExp("h$"),' HYBRID');
	  }
	  return str.templateReplace(this.MODEL_REPLACE, name);	
	}
	this._replaceModelCode = function(str, name) {
		return str.templateReplace(this.MODEL_CODE_REPLACE, name);	
	}
	this._replaceModelLink = function(str, name, code) {
	
	  //str = str.replace('about:blank','');
	  //str = str.replace('javascript:void(0);', '');
	  str = this._replaceModelText(str, name);
	  //str = this._replaceModelCode(str, code);
	  
	  str = str.toLowerCase();
	  return str;
	}
	/**
	 * Method replaces all ~category~ wildcards with actual category name
	 */
	this._replaceCategory = function(str, cat) {
	  return str.templateReplace(this.CATEGORY_REPLACE, cat);	
	}
}
function ieBodyhideNavigation() {	
	navigationManager.hideNavigationWithTimeOut();
}
var navigationManager = new tourNavigationManager();




	var category = new VehicleCategory('SH', 'Sacred Hana');
	 
	 	
	 	var model = new VehicleModel('SH', 'Sacred-Hana','$295','a person','9270');
	 	
			model.addFeature('Full Breakfast ');
			
			model.addFeature('Full Lunch ');
			
			model.addFeature('Free Drinks All Day ');
			
			model.addFeature('Private Luxry SUVs ');
						
			
			
	 	
	 	category.addModel(model);
	 
	navigationManager.addCategory(category);
	
	
		


	
	var category = new VehicleCategory('KC', 'Kahakaloa Cliffs');
	 
	 	
	 	var model = new VehicleModel('KC', 'Kahakaloa-Cliffs','$295','a person','91');
	 	
	 	
			model.addFeature('Full Breakfast ');
			
			model.addFeature('Full Lunch ');
			
			model.addFeature('Free Drinks All Day ');
			
			model.addFeature('Private Luxry SUVs ');
	 	
	 	category.addModel(model);
	 
	 	
	 
	navigationManager.addCategory(category);

	
	var category = new VehicleCategory('SW', 'Sunsets and WIne');
	 
	 	
	 	var model = new VehicleModel('SW', 'Sunsets-and-Wine','$295','a person','9270');
	 	
	 	
			model.addFeature('Full Breakfast ');
			
			model.addFeature('Full Lunch ');
			
			model.addFeature('Free Drinks All Day ');
			
			model.addFeature('Private Luxry SUVs ');
	 	
	 	category.addModel(model);
	 
	navigationManager.addCategory(category);

	


	




	
