var carjen = {};

//CONFIG 

  carjen.config = {
    XHRTimeout: 10000, //ajax timeout
		pathToStyles: '/stylesheets/', //path to stylesheets /orthotest/stylesheets/ I think
		pathToImages: '/images/'
  };
  
//UTILITY FUNCTIONS
//EVENT HANDLING

  //ie event cache
  carjen.cache = [];
  

  //window loading events
  carjen.loadCache = [];

  //METHODS

  //PRIVATE

  //run a function to see if we can access the document.body
  //and then run all the events in the load cache
  (function(){
    var fn = function(){
	    if(document.body){//ready??
			  var f = carjen.loadCache.length - 1;
			  for(f; f >= 0; f--){
				  try{
			      carjen.loadCache[f].apply(document);
					  carjen.loadCache.splice(f, 1);
					}
					catch(err){
					  break;//to maintain the correct order of events	
					}
			  }
			  if(carjen.loadCache.length == 0){
				  window.clearInterval(loading);
				}
			}
    }
		var loading = window.setInterval(fn, 10);
  })();

  //PUBLIC

  //add an event handler to be called the window context when the DOM is ready 
  carjen.addLoadEvent = function(args){
	  //push into load cache
    for(var f = 0; f < arguments.length; f++){
      carjen.loadCache.unshift(arguments[f]);
	  }
	  //push into event cache for IE cleanup
  }

  //unload event handler
  carjen.addUnloadEvent = (function(){
    if (window.addEventListener){ //w3c DOM model
      return carjen.addUnloadEvent = function(){
		    var len = arguments.length;
			  for (var i=0; i < len; i++){
          window.addEventListener('unload', arguments[i], false);
			  }	 
      }
    }
    else if(window.attachEvent){//IE Model
      return carjen.addUnloadEvent = function(){
		    var len = arguments.length;
		    for (var i=0; i < len; i++){
          window.attachEvent('onunload', arguments[i]);
			  }
		  }
    }
	  else{
	    //doesn't support either
		  return false;         
	  }
  })();

  //add event
  carjen.addEvent=(function(){
    var i = 0;
    if (window.addEventListener){ //w3c DOM model
      return carjen.addEvent = function(el, typ, funct, capture){
		    var capture = capture || false;
        el.addEventListener(typ, funct, capture);	 
      }
    }
    else if (window.attachEvent){ //IE DOM model
      return carjen.addEvent = function(el, typ, funct){
		    i++;
        var handle = 'fx' + typ + i;//unique id
        if(el.nodeType){
          el.setAttribute(handle,'on');
          var fn = function(){
				    var e = window.event;
            var trg = e.srcElement;//null for window
            while (trg.getAttribute(handle)!='on'){  
              trg = trg.parentNode;//walk up dom
						  //if we don't find anything?
						  if(!trg.tagName) return;
            }
            funct.call(trg, window.event);
				  }	
				  carjen.cache.push([el, typ, handle, funct, fn]);
        }
			  else{
			    fn = funct;
			  }
        el.attachEvent('on' + typ, fn);
      }
    }
	  else{ //neither
	    return false;
	  }
  })();


  //remove an event
  carjen.removeEvent = function(){ //w3c
    if (window.removeEventListener){
      return carjen.removeEvent = function(el, typ, funct, capture){
		    var capture = capture || false;
		    el.removeEventListener(typ, funct, capture);
		  }
	  }
	  else if(window.detachEvent){ //ie
      return carjen.removeEvent = function(el, typ, funct){
		    var eventCache = carjen.cache;
		    for(var i= eventCache.length-1; i>=0; i--){
				  if(eventCache[i][0] == el
				   && eventCache[i][1] == typ 
				   && el.getAttribute(eventCache[i][2])
				   && funct === eventCache[i][3]){
					   el.detachEvent('on'+typ, eventCache[i][4]);
				     eventCache.splice(i,1);
				  }
			  }
		  }
	  }
	  else{ //neither
	    return false;
	  }
  }();

  //CLEANUP

  //unattach all events
  //used to prevent IE memory leaks
  carjen.cleanup = function(){
    if(!window.detachEvent) return; //w3c
      var eventCache = carjen.cache;
	    for(var i = eventCache.length - 1; i>=0; i--){
        eventCache[i][0].detachEvent('on' + eventCache[i][1], eventCache[i][4]);
	  }
  };

  //call the cleanup function
  carjen.addUnloadEvent(carjen.cleanup);

  //FUNCTION METHODS
  //called by functions

  //	you have to get the event -- f(e){carjen.prevent(e);}

  //stop bubbling
  //lazy load factory
  carjen.stop = function(e){
    if(e.stopPropagation){ //ie
      e.stopPropagation();//first call stop
      return carjen.stop = function(e){
        e.stopPropagation();
      };
    }
    else{
      e.cancelBubble = true;
      return carjen.stop = function(e){
        e.cancelBubble = true;
      }
    }
  };

  //prevent default behavior
  //lazy load factory
  carjen.prevent = function(e){
    if(e.preventDefault){//w3c
      e.preventDefault();//first call prevent
      return carjen.prevent = function(e){
        e.preventDefault();
      }
    }
    else{
      e.returnValue = false;
      return carjen.prevent = function(e){
        e.returnValue = false;
      }
    }
  };

  //original target
  carjen.target = function(e){
    if(e.target){//w3c
		  return e.target;
	  }
	  else if(e.srcElement){//ie
		  return e.srcElement;
	  }
  };

  //DOM STUFF

  //element by id
  carjen.ids = function(){
    //accepts a list of strings 
    var len = arguments.length; 
    if (len == 1){//single 
      return document.getElementById(arguments[0]); 
    } 
    else { 
      var elements = []; 
		  for (var i = 0; i < len; i++){ 
		    elements.push(document.getElementById(arguments[i])); 
		  } 
		  return elements; 
	  } 
  }; 
	
	
  carjen.classNames = function(){ 
	  var elements = []; 
	  var nodes, classes;
	  if (arguments[0].nodeType){//node supplied 
	    nodes = arguments[0].getElementsByTagName('*');  
		  classes = 1; 
	  } 
	  else{ //no node use document
	    nodes = document.getElementsByTagName('*'); 
	    classes = 0; 
	  } 
	  var argLen = arguments.length; 
	  for(classes; classes < argLen; classes++){//loop thru classes 
	    var reg = new RegExp('\\b' + arguments[classes] + '\\b'); 
	    for(var node = 0, len = nodes.length; node < len; node++){
	      //loop thru nodes 
		    if(reg.exec(nodes[node].className)){//has right class 
		      elements.push(nodes[node]);
		    } 
	    }
	  } 
	  return elements; 
  }; 


  carjen.tagNames = function(){ 
   elements =[]; 
	 if(arguments[0].nodeType){//node passed 
	   var node = arguments[0]; 
		 for(var i = 1; i < arguments.length; i++){//tags to get 
		   var tags = node.getElementsByTagName(arguments[i]); 
			 for(var tag=0, len = tags.length; tag < len; tag++){//tag returned 
			   elements.push(tags[tag]); 
			 }
		 } 
	 } 
	 else{ //no node  
	   for(var i = 0; i < arguments.length; i++){//tags to get 
		   var tags = document.getElementsByTagName(arguments[i]);  
		   for(var tag=0, len = tags.length; tag < len; tag++){//tag returned 
		     elements.push(tags[tag]); 
		   }
		 }
	 }
	 return elements; 
  };

  carjen.makeTag = function(tag, txt, attrs){
    var t = document.createElement(tag);
	  if (txt && typeof txt == 'string'){//node contents
	    var tx = document.createTextNode(txt);
		  t.appendChild(tx);
	  }
	  else if(txt && typeof txt == 'object'){//attributes
	    for(var att in txt){
        if(att == 'id'){//ie hack
			    t.id = txt[att];
			  }
		    else{
			    t.setAttribute(att, txt[att]);
			  }
		  }
	  }
	  if(attrs){
	    for(var atr in attrs){
        if(atr == 'id'){//ie hack
			    t.id = attrs[atr];
			  }
		    else{
			    t.setAttribute(atr, attrs[att]);
			  }
		  }
	  }
	  return t;
  };

  carjen.appendAfter = function(newNode, oldNode){
	  //are there any sibling after this?
    if(oldNode.nextSibling){
	    //yes so insert before next sibling
	    oldNode.parentNode.insertBefore(newNode, oldNode.nextSibling);
	  }
	  else if(oldNode.parentNode){
	    //no, just append to parent
	    oldNode.parentNode.appendChild(newNode);
	  }
  };
  
  //document scroll
  carjen.getScroll = function(){
	  var scrollTop = document.body.scrollTop;
    if (scrollTop == 0){
      if (window.pageYOffset){
        scrollTop = window.pageYOffset;
      }
      else{
        scrollTop = (document.body.parentElement) ? document.body.parentElement.scrollTop : 0;
      }
    }
    return scrollTop;
  }
   
  //MUST NOT BE IN QUIRKSMODE
  carjen.getWindowHeight = function(doc){
  //gets the height of the window
  //optionally the document height
    var heights = [window.innerHeight, document.body.clientHeight, document.documentElement.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight];    
		 //this is slow consider maths.max min    
    if(doc){
      heights.sort(function(a, b){return b - a;});
    }
    else{
      heights.sort(function(a, b){return a - b;});
    }
    return heights[0];
  }
  
  carjen.getOffset = function(node, offset){
  //get the offset of an element
  //can be either top or left
    if(offset == 'top'){
	    var y = 0;
	    while(node){
		    y += node.offsetTop;
			  node = node.offsetParent;
		  }
		  return y;
	  }
    else if(offset == 'left'){
	    var x= 0;
	    while(node){
		    x += node.offsetLeft;
			  node = node.offsetParent;
		  }
		  return x;
	  }
  };
  
  //DOM walker
  carjen.walkDOM = function(node){
    //get a real node
    while(node.nodeType != 1){
      if(node.nextSibling){
        node = node.nextSibling;
      }
      else{
        return false;
      }
    }
    return node;
  }
  
  //CSS STUFF
  
  //add class
  carjen.addClass = function(el, clas){
	  if(el.className){
	    el.className = el.className + ' ' + clas;
	  }
	  else{
	    el.className = clas;
	  } 
  };

  //remove class
  carjen.removeClass = function(el, clas){
	  if(el.className){
	    var reg = new RegExp('\\b' + clas + '\\b');
		  el.className = el.className.replace(reg, '');
	  }
  };

  //has class
  carjen.hasClass = function(el, clas){
    if(el.className){
	    var reg = new RegExp('\\b' + clas + '\\b');
		  var result = el.className.match(reg);
      if(result) return true;
	  }
	  else{
	    return false;
	  }
  };
  
  //getStyle is chancy at best -- it will return different values for different  browsers
  // well for ie and DOM complient ones anyway
  //factory
  carjen.getStyle = (function(){
    if(document.defaultView && document.defaultView.getComputedStyle){
      return carjen.getStyle = function(el, prop){
		    return document.defaultView.getComputedStyle(el, null)[prop];
		  }
    } 
	  else{
	    //we assume that we're using the IE model
		  //but we'll save later
      return getStyle = function(el, prop){
			  if(el.currentStyle){
			    return el.currentStyle[prop];
			  }
			  else{
			    return el.style[prop];
			  }		
		  }
    }
  })();
  
  //OTHER
  
  //generate unique id
  carjen.counter = function(guid){
    //if this was created with the new declaration
    if(this instanceof carjen.counter){
      this.i = 0;
	    this.get = function(){
	      this.i++;
		    if(guid){
		      this.guid = 'uid' + this.i;
			    return this.guid;
		    }
			  else{
		      return this.i;
			  }
	    }	  
	  }
	  else{
	    //otherwise return a new
	    return new carjen.counter(guid);
	  }
  }
  
  //  HAS CSS?
  carjen.style = true; //assume on
  carjen.hasCSS = function(){
    //insert a node and test it
    var n = carjen.makeTag('div', 'test', {style:'display:none;',id:'CSS'});
    document.body.appendChild(n);
    if(carjen.getStyle(n, 'display') != 'block'){
      carjen.style = false;
    } 
    n.parentNode.removeChild(n); 
  };
  
  carjen.addLoadEvent(carjen.hasCSS);
  


  
  //AJAX STUFF
  
  //PRIVATE METHODS
  
  //basically setting up stuff for the 
  //actual XHR class

  //factory to store the request to use
  carjen.XHRfactory = (function(){
    var factories = [
	    function(){return new XMLHttpRequest();},
		  function(){return new ActiveXObject('Msxml2.XMLHTTP');},
		  function(){return new ActiveXObject('Microsoft.XMLHTTP');}
	  ];
	  for(var i=0; i<factories.length; i++){
	    try{
		    var factory = factories[i];
			  var request = factory();
			  if(request !== null){
			    return carjen.XHRfactory = factory;
			  }
		  }
		  catch(e){
		  }
	  }	
	  //no go
	  return false;
  })();

  //parse an object passed as args
  carjen.XHRparse = function(args){
    var keys = [];
	  for(i in args){
	    var k = i + '=' + args[i];
		  keys.push(k);
	  }
    return keys.join('&');
  }

  //display a working message
  carjen.XHRWorking = function(){
    var msg = carjen.makeTag('div', 'working', {id:'xhr_message'});
    msg.style['top'] = 5 + carjen.getScroll() + 'px';
	  document.body.appendChild(msg);
	  return;
  }

  //remove the working message
  carjen.XHRStop = function(resp){
    var msg = carjen.ids('xhr_message');
    if(!msg) return;
      msg.innerHTML = resp;    
    var fn = function(){
	    if(msg) msg.parentNode.removeChild(msg);
	  }
	  window.setTimeout(fn, 3000);
  }



  //XHR CLASS

  //basic class
  carjen.XHR = function(url){
    this.url = url;
  }

  //CLASS METHODS

  // request
  //callback returns 'Connection error' on timeout failure
  //NEEDS WORK -- MAY RETURN NON 200 STATUS
  carjen.XHR.prototype.XHRnew = function(callback, args, type, msg){
    if(msg) carjen.XHRWorking();//working message
    var _r = carjen.XHRfactory();
	  if(typeof args == 'object'){
	    args = carjen.XHRparse(args);
	  }
	  var out = carjen.config.XHRTimeout;
	  var f = function(){
		  _r.abort();
	    callback('Connection error');
		  if(msg) carjen.XHRStop('request timed out');	    
	  }
    var abt = window.setTimeout(f, out);
	  _r.onreadystatechange = function(){
	    if(_r.readyState == 4 && _r.status == 200){
			  if(msg) carjen.XHRStop('success');
		    callback(_r.responseText);
		    window.clearTimeout(abt);
		  }
	  }
	  if(type == 'get'){ 
	  	var page = this.url + '?' + args;
      _r.open('GET', page);
	    _r.send(null);
	  }
	  else{
      _r.open('POST', this.url);
      _r.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
	    _r.send(args);	  
	  }
  }

  //THE XHR API

  carjen.XHRrequest= function(url, callback, args, type, msg){
    var request = new carjen.XHR(url);
    request.XHRnew(callback, args, type, msg);
  }
 
  
  //DRAG CLASS
  carjen.Drag = function(el){
  
    //attributes
    this.element = el;
    this.parent = el.parentNode;
    this.siblings = [];
    this.sibOffsets = [];
    this.marker = 0;

    var that = this;   //scope fix
    
    //following aren't prototypal as they are called by events
    
    //do drag
    this.doDrag = function(e){  
        var y = e.clientY;
        var scroll = carjen.getScroll();  
        //scroll ??       
        if(y < 50 && scroll > 0){
          window.scrollBy(0, -30); 
        }
        else if(y > carjen.getWindowHeight() - 50 && y + scroll < carjen.getWindowHeight(true)){ 
           window.scrollBy(0, 30);
        }
        //now decide whether to move the marker
        //logic
        //loop through the sibs to get the position of the last node#
        //if this is less than the mark insert before 
        //if greater insert after
        //if = then we're dealing with last node 
        var len = that.siblings.length;
        for(var i = 0; i < len; i++){
          if(y + carjen.getScroll() < that.sibOffsets[i]) break;
        } 
        if(i < that.marker){//move before
          var mark = that.parent.removeChild(carjen.ids('marker'));
          that.siblings[i].parentNode.insertBefore(mark, that.siblings[i]);
          that.marker--;
        }
        else if(i > that.marker && that.siblings[i]){//move after
   
          var mark = that.parent.removeChild(carjen.ids('marker'));
          that.siblings[i].parentNode.insertBefore(mark, that.siblings[i]);
          that.marker++;     
        
        }
        else if(that.marker == i && that.marker == that.siblings.length - 1){ //last node

          //append after last node
          //or insert before last node
          //logic
          //y > sibs[i] and last node != marker add last
          //y < sibs[i] and last node == maker add before last
          var mark = carjen.ids('marker');
          var last = that.parent.lastChild;
          var yAt = y + carjen.getScroll();      
          if(mark !== last && yAt + 20 > that.sibOffsets[i]){
            //add at end
            mark.parentNode.appendChild(mark);
          } 
          else if(mark === last && yAt + 20 <= that.sibOffsets[i]){
            that.siblings[i].parentNode.insertBefore(mark, that.siblings[i]);   
          }      
        }
        //the actual moving
	      that.element.style['left'] = e.clientX + 'px';
	      that.element.style['top'] = y + /*carjen.getScroll() + */ 'px';
    };
    
    //stop the drag
    this.stopDrag = function(){
      carjen.removeEvent(document.body, 'mouseup', that.stopDrag);
      carjen.removeEvent(document.body, 'mousemove', that.doDrag);
      //remove class
      carjen.removeClass(that.element, 'dragged');
      that.element.style['top'] = '';
      that.element.style['left'] = '';
      //insert element
      var mark = carjen.ids('marker');
      mark.parentNode.replaceChild(that.element, mark);
      //now do the AJAX stuff
      var f = function(response){ //callback function
        //do nothing
      }
      carjen.XHRrequest('server.php', f, {string:that.element.tagName}, 'post', true);
    };   
  
  }
  
  
  //drag class methods
  //init PUBLIC
  carjen.Drag.prototype.initDrag = function(){
    //add event handler for mouse up
    carjen.addEvent(document.body, 'mouseup', this.stopDrag);
    carjen.addEvent(document.body, 'mousemove', this.doDrag);
    //remove element from flow & add marker
    var mark = carjen.makeTag('hr');
    mark.id = 'marker';
    carjen.addClass(this.element, 'dragged');
    //get siblings
    this.getSibs();
    var p = this.marker;
    var len  = this.siblings.length - 1;
    if(p > len){//last node
      this.parent.appendChild(mark); 
    } 
    else{ 
      this.siblings[p].parentNode.insertBefore(mark, this.siblings[p]);
    }
  }
  
  //PRIVATE
  
  //set sibling attributes
  carjen.Drag.prototype.getSibs = function(){
    //first real node
    var node = carjen.walkDOM(this.parent.firstChild);
    var index = 0;
    while(node.nextSibling){//fill the sibs array
      if(node !== this.element){ //not the dragged element
        this.siblings.push(node);
        index++;
      }
      else{
        this.marker = index;  //marker
      }    
      var node = carjen.walkDOM(node.nextSibling); 
    }
    //now loop through the sibs getting offsets
    for(var i = 0; i < this.siblings.length; i++){
      this.sibOffsets.push(parseInt(carjen.getOffset(this.siblings[i], 'top'),10));  
    }
  };
  

  //PAGE CODE
  
  //MENUS
  //xhr menus init
  carjen.menu = function(){
    var links = carjen.tagNames(carjen.ids('sidebar'), 'a');
    for(var i = 0; i < links.length; i++){
      carjen.addEvent(links[i], 'click', carjen.getMenu);
    }
  };  
  
  //make menu items draggable
  //focus
  carjen.btnFocus = function(){
    this.id = 'focused';
    carjen.addEvent(this, 'blur', carjen.btnOut);
  }
  
  //blur
  carjen.btnOut = function(){
    this.id = null;
  }
  
  //catch up down arrows
  carjen.keyWatch = function(e){
    var btn = carjen.ids('focused');
    if(btn){
    var el = btn.parentNode;
      if(e.keyCode == 38){//up
        var prev = el.previousSibling;
        if(!prev) return;
        while(prev.nodeType != 1){  //walk to real node
          if(!prev.previousSibling) return;
          prev = prev.previousSibling;
        }
        carjen.prevent(e); //stop default
        el.parentNode.insertBefore(el, prev); 
        btn.focus();  //refocus on button
     }
     if(e.keyCode == 40){ //down
       var next = el.nextSibling;
       if(!next) return;
       while(next.nodeType != 1){
         if(!next.nextSibling) return;
         next = next.nextSibling;
       }
       carjen.prevent(e);  
       carjen.appendAfter(el, next); 
       btn.focus();
     }   
    }
  }
  
  //DRAGGING

  //start a new drag event
  carjen.startDrag = function(e){
    //start a new drag??
    var targ = carjen.target(e);//target
    if(carjen.hasClass(targ, 'dragger')){
      //new drag instance
      var dragHandler = new carjen.Drag(targ.parentNode);
      dragHandler.initDrag(); //init
    }   
  }


  //fetch a menu
  carjen.getMenu = function(e){
    carjen.prevent(e);
    var menu = this.firstChild.id;
    //callback function
    var fn = function(rtn){
      if(rtn == 'Connection error'){//something wrong just get
        location.href = 'http://www.carjen.co.uk/orthotest/plugins/linkmanager/adminindex.php?toggle=' + menu;
        return;  
      }
      var d = carjen.makeTag('div', {id:'Xmenu'});
      d.innerHTML = rtn;
      var old = carjen.ids('Xmenu');
      if(old){//menu 
        old.parentNode.removeChild(old);  
      }
      var ank = carjen.ids(menu).parentNode;
      ank.parentNode.insertBefore(d, ank.nextSibling);
      //MAKE DRAGGABLE
      //add event handler to start drag
      carjen.addEvent(document.body, 'mousedown', carjen.startDrag);
  
	    //add drag buttons
	    var els = carjen.tagNames(d, 'li');
	    for(var i = 0; i < els.length;i++){
	      var btn = carjen.makeTag('button','drag');
	      carjen.addClass(btn, 'dragger');
	      carjen.addEvent(btn, 'focus', carjen.btnFocus);
		    els[i].insertBefore(btn, els[i].firstChild);
	    };
	
      //add keypress handler
      carjen.addEvent(document.body, 'keyup', carjen.keyWatch);    	
  
    }
      carjen.XHRrequest('common.php', fn, {getMenu:menu}, 'get', false);
  };
  
  //add menu init to load
  //carjen.addLoadEvent(carjen.menu);

  //TABS
  
  //change tab
  carjen.changeTab = function(e){
    carjen.prevent(e);
    var id = this.href.substr(this.href.length - 4); //to show
    var show = carjen.ids(id).parentNode;
    var hide = carjen.ids('current');
    hide.id = '';
    show.id = 'current';
    //change the current tab
    var cur = carjen.classNames(this.parentNode, 'navCurrent');
    carjen.removeClass(cur[0], 'navCurrent');
    carjen.addClass(this, 'navCurrent');
  };
  
  
  carjen.makeTabs = function(){
    //do we have CSS?
    if(!carjen.style) return;		
    //find first h2  
    //CONSIDER DOING THIS ANOTHER WAY  
    var tabbers = carjen.tagNames(carjen.ids('content'), 'h2');
	  if(tabbers.length < 4) return; //we're not needed here
    var node = tabbers[1];
    var dad = node.parentNode; //cache    
    //insert the navigation
    var navBox = carjen.makeTag('div', {id:'navTabBox'});
    var c = carjen.counter(true);//unique id
    for(var i = 1; i < 4; i++){
      var uid = c.get();
      tabbers[i].id = uid;
      var tab = carjen.makeTag('a', tabbers[i].innerHTML);
      tab.setAttribute('href', '#' + uid); // hmmmm
      carjen.addEvent(tab, 'click', carjen.changeTab);
      if(i == 1) carjen.addClass(tab, 'navCurrent'); //current
      navBox.appendChild(tab);
    }
    dad.insertBefore(navBox, node);
    //make the tab containers
    var tabNodes = [];
    var i = 0;//counter		
		if(node.tagName.toUpperCase() == 'H2'){ 
		  tabNodes.push(node);//cope with a 'real' node
		}
		var maxHeights = [0, 0, 0];
    while(node.nextSibling && i < 3){//fill the node array
      //4 NEEDS TO BE IN THE CONFIG
      var node = carjen.walkDOM(node.nextSibling);
			if(!node) break;
			//get the height
			// added by michael_h on 09/03/2009
			//checks for opera
			function getHeight()
			{
			//
			var op = navigator.userAgent.indexOf("Opera") > -1;
			var db = node;
			return op ? db.style.pixelHeight : db.offsetHeight;
			}

			maxHeights[i] += getHeight() + 40;
			tabNodes.push(node);			
      if(node.tagName.toUpperCase() == 'H2')
			{			
			i++;
			}
    }
		//find the maximum height
				
		//var biggest = maxHeights.sort(function(a,b){return b - a})[0];
		var biggest = maxHeights[0];
		//alert(maxHeights[0]+" "+maxHeights[1]+" "+maxHeights[2]);
		for(var i = 1; i < 4; i++)
		{
		if(maxHeights[i] > biggest)
		{
		biggest = maxHeights[i];
		}
		}
		
    var tabCont = carjen.makeTag('div', {id:'tabContainer'});
		//alert(biggest);
		tabCont.style['height'] = biggest + 'px';
		var first = true;
    for(var i = 0; i < 3; i++){//3 NEEDS TO BE IN CONFIG
      var tabbox = carjen.makeTag('div');
      if(first){//first pass
        first = false;
        tabbox.id = 'current';      
      }
      tabbox.appendChild(tabNodes.shift()); //the h2
      while(tabNodes[0] && tabNodes[0].tagName.toUpperCase() != 'H2'){ //until next h2
        tabbox.appendChild(tabNodes.shift());
      }
      tabCont.appendChild(tabbox);
      carjen.addClass(tabbox, 'tabbox');
    }
    carjen.appendAfter(tabCont, carjen.ids('navTabBox'));
  };
  
  
  
  //init tabs
			//this is reliant on the document actually being rendered
		//so we are going to call it on load for safari using a browser
		//sniff. I know that this is wrong but I can't fix it for now
		if(/WebKit|Khtml/i.test(navigator.userAgent)){//safari
		  carjen.addEvent(window, 'load', carjen.makeTabs); 
		}
		else{
		  carjen.addLoadEvent(carjen.makeTabs);
		}
  
   
  //THUMBNAIL IMAGES 
	
	//HELPER FUNCTIONS
	
	//make mask
	carjen.makeMask = function(){
	  //make a page covering background div to prevent click-thru
    var mask = carjen.makeTag('div', {id:'mask'});
		mask.style['height'] = carjen.getWindowHeight(true) + 'px';
		document.body.appendChild(mask);	
	}

	//make the image container
	carjen.makeBox = function(){
	  //make the container box
		var outerBox = carjen.makeTag('div', {id:'outerBox'});
		var y = (window.pageYOffset)? window.pageYOffset : document.documentElement.scrollTop;
		outerBox.style['top'] = 5 + y + 'px';	
		var box = carjen.makeTag('div', {id:'innerBox'});		
		outerBox.appendChild(box);					
    //initial height/width
		box.style['height'] = 200 + 'px';
		box.style['width'] = 200 + 'px';
		return outerBox;
	}
	

	
	//THE ANIMATE CLASS
	carjen.Animate = function(node, height, width, rate){
  //The animation class
	//are we called with the new keyword
	if(this instanceof carjen.Animate){
	  //private properties
	  this.node = node;//the node to be animated
	  this.runrate = (rate) ? rate : 10;
	  //animation finished
	  this.w_over = false;
	  this.h_over = false; 
	  //the amount to change
	  this.heightChange = height - node.offsetHeight;
	  this.widthChange = width - node.offsetWidth;
	  //the directions of change
	  this.heightDirection = (this.heightChange > 0) ? true : false;
	  this.widthDirection = (this.widthChange > 0) ? true : false;
	  //start values
	  this.heightCount = node.offsetHeight;
	  this.widthCount = node.offsetWidth;
	  var that = this;//bind
	  //height changing function
	  this.f_height = function(){
	    //clear interval??
	    if(that.heightChange == 0){
		    window.clearInterval(that.h_ctrl);
        if(that.h_sequence){//height followed by width
          that.w_ctrl = window.setInterval(that.f_width, that.runrate);
			  }
			  that.h_over = true;
		  }
		  //height changes
		  if(that.heightChange > 0 && that.heightDirection){
		    //grow height
				if(that.heightChange < 5){
				  that.heightCount += that.heightChange;
					that.heightChange = 0;
				}
				else{
			    that.heightCount += 5;
			    that.heightChange -= 5;
				}
				that.node.style['height'] = that.heightCount + 'px';
		  }	
		  else if(that.heightChange <= 0 && !that.heightDirection){ 
		    //shrink height
				if(that.heightChange > -5){
				  that.heightCount += that.heightChange;
					that.heightChange = 0;
				}
				else{
			    that.heightCount -= 5;
			    that.heightChange += 5;			
				}
		    that.node.style['height'] = that.heightCount + 'px';
		  }
	  }
	  //width changing function
	  this.f_width = function(){
	    //clear interval??
	    if(that.widthChange == 0){
		    window.clearInterval(that.w_ctrl);
        if(that.w_sequence){//width followed by height
          that.h_ctrl = window.setInterval(that.f_height, that.runrate);
			  }
			  that.w_over = true;
		  }
		  //width changes
		  if(that.widthChange > 0 && that.widthDirection){
		    //grow width
				if(that.widthChange < 5){
				  that.widthCount += that.widthChange;
					that.widthChange = 0;
				}
				else{
			    that.widthCount += 5;
			    that.widthChange -= 5;
				}
				that.node.style['width'] = that.widthCount + 'px';
		  }	
		  else if(that.widthChange < 0 && !that.widthDirection){ 
		    //shrink width
				if(that.widthChange > -5){
				  that.widthCount += that.widthChange;
					that.widthChange = 0;
				}
				else{
			    that.widthCount -= 5;
		      that.widthChange += 5;	
				}			
				that.node.style['width'] = that.widthCount + 'px';
		  }
	  }
	}
	else{//not called with the new keyword
	  return new carjen.Animate(node, height, width, rate);
	}
};

//PROTOTYPAL METHODS
  //exposes two methods
	// # run -- run the animation
	//# finished -- to query whether the function has returned
	
	//function over??
	carjen.Animate.prototype.finished = function(){
	  if(this.w_over && this.h_over){
	    return true;
		}
		else{
		  return false;
		}
	};
	
	//run the animation
	carjen.Animate.prototype.run = function(seq){
	  if(!seq){//run both at once
	    this.h_ctrl = window.setInterval(this.f_height, this.runrate);	
			this.w_ctrl = window.setInterval(this.f_width, this.runrate);
		}
		else if(seq == 'height'){
		  this.h_sequence = true;
			this.h_ctrl = window.setInterval(this.f_height, this.runrate);
		}
		else if(seq == 'width'){
		  this.w_sequence = true;
			this.w_ctrl = window.setInterval(this.f_width, this.runrate);
    }
	};
	
	
	//remove the image
	carjen.closeBox = function(e){
	  //hide the event
	  carjen.prevent(e);
		carjen.stop(e);
	  //remove the container & mask
		var box = carjen.ids('outerBox');
		box.parentNode.removeChild(box);
		var mask = carjen.ids('mask');
		mask.parentNode.removeChild(mask);
		//remove event handler
		carjen.removeEvent(document.body, 'click', carjen.closeBox);
		//reinsert open handler
	  carjen.addEvent(document.body, 'click', carjen.showLarge);
	};
	
	//the actual image stuff
	carjen.showImage = function(ref){ 
		//remove the open image
		carjen.removeEvent(document.body, 'click', carjen.showLarge);	  
  	//the image
    var img = new Image();	
    var box = carjen.ids('innerBox');//display box
		//the function to call onload
		img.onload = function(){
		  var fn = carjen.Animate(box, img.height, img.width, 1);
		  fn.run('height');	
		  var callback = function(){
		    if(fn.finished()){//animation over
			    window.clearInterval(animation);
				  box.appendChild(img);//add the image
				}
			}
		  var animation = window.setInterval(callback, 50);
		}
	  //NOW set the href
		img.src = ref;	
	};
	
	//attachment
	carjen.showLarge = function(e){
	  var targ = carjen.target(e);
		if(carjen.hasClass(targ, 'smallimage') || carjen.classNames(targ, 'smallimage')[0]){//is a thumb or contains one
			//get the href
			if(targ.tagName.toUpperCase() == 'IMG'){//image
	      var ref = targ.parentNode.href;
			}
			else{//anchor
			  var ref = targ.href;
			}
			if(!ref.match('.jpg')) return;//all products page
			carjen.prevent(e);
		  carjen.makeMask();//mask
      var imgBox = carjen.makeBox(); //make the image container 
			document.body.appendChild(imgBox);
			//add close event handlers
			carjen.addEvent(document.body, 'click', carjen.closeBox);
			//load and show
			carjen.showImage(ref);
		}
	};
	
	//add event delegate onto body
	carjen.isThumb = function(){    
	  carjen.addEvent(document.body, 'click', carjen.showLarge);
	};
	
	carjen.addLoadEvent(carjen.isThumb);
	
	//THE DIVS STUFF
	
	carjen.equalize = function(){
	  var divs = carjen.ids('secondarynav', 'content')
	  if(divs.length == 1) return; //no nav?
    if(divs[0].offsetHeight > divs[1].offsetHeight){
		  divs[1].style['height'] = divs[0].offsetHeight + 'px';
		}
		else{
		  divs[0].style['height'] = divs[1].offsetHeight + 'px';
		}
	};
	  
		//another browser sniff coming up
		if(/WebKit|Khtml/i.test(navigator.userAgent)){//safari
		  carjen.addEvent(window, 'load', carjen.equalize); 
		}
		else{
		  carjen.addLoadEvent(carjen.equalize);
		}
	carjen.addEvent(window, 'resize', carjen.equalize);
	
	//SEARCH BOX
	carjen.searchOut = function(){
	  if(this.value == ''){
	  var lbl = carjen.tagNames(this.parentNode, 'label')[0];
	  lbl.innerHTML = 'Search This Site';
		}
	}
	
	
	carjen.searchFocus = function(){
	  var lbl = carjen.tagNames(this.parentNode, 'label')[0];
	  lbl.innerHTML = '';
		carjen.addEvent(this, 'blur', carjen.searchOut);
	}
	
	
	carjen.searchBox = function(){
	  var box = carjen.ids('searchboxneedle');
		carjen.addEvent(box, 'focus', carjen.searchFocus);
	}	
	
	carjen.addLoadEvent(carjen.searchBox);
	
	//link in stylesheet
	document.write("<link href='" + carjen.config.pathToStyles + 'carjen_js.css' + "' rel='stylesheet' type='text/css' media='all' />");
 

