// This file is dependent on the onload stack javascript include.

// It is a principal of this author to not record information about the visual qualities of HTML Elements ourside of the DOM.
// Most of the time, qualities that can be modified are already being tracked by the browser and tracking them again would add
// unnessary weight. The only time that an element should tracked outside of the DOM is during manipulation.

CS_DHTML = function(){ 
   //creating a method is equivalent
   //to creating an object in JS. - GM
   this.selectedObj = null; // Used to track the object event target.
   this.offsetX, this.offsetY; 
   this.scrollComps = false; // used to set moveScrolCompl event handler if scroll comp(s) exist on page.
   this.timeout; // used to store currently executing timeout reference.
   this.defaultMenuTimeout = 500;
   this.origFontSize; // This used in this.userFontResize;
   this.fontSizeEl = null; // Used to track element used to test for user change of font size using browser
   this.initialized = 0; // Used to determine of CS_DHTML has been initialized.
   document.CS_DHTML = this;
}

CS_DHTML.prototype.removeChild = function(parentId,childId){
  var parentId = parentId || 0;
  var childId  = childId || 0;
  var parentEl = this.getElementById(parentId);
  var childEl  = this.getElementById(childId);
  if(parentEl != null && childEl != null){
    return parentEl.removeChild(childEl);
  }	
  return false;
} 

CS_DHTML.prototype.getElementById = function(obj){
  // Obj can be string id or element. If el then just return el.
  var obj = obj || null;
  if(typeof obj == 'string'){
    if(document.getElementById) return document.getElementById(obj);
    else if(document.all) return document.all(obj);
  }else if(typeof obj == 'object') return obj;
  return null;
}

CS_DHTML.prototype.setSelectedElement = function(evt){
   var target = (evt.target) ? evt.target : evt.srcElement;
   if(target.id){
     this.selectedObj = this.getElementById(target.id);
   }   
   return (this.selectedObj != null);
}

//  Track element targeted with mousedown.
CS_DHTML.prototype.engage = function(evt){
  var evt = (evt) ? evt : event;
  CS_DHTML.setSelectedElement(evt)
   if(CS_DHTML.selectedObj){
      if(evt.clientY){ //IE 5+ and Net 6+
         CS_DHTML.offsetY = evt.clientY - ((CS_DHTML.selectedObj.offsetTop) ? CS_DHTML.selectedObj.offsetTop : 0) ;
      }else if(evt.offsetY){ //IE 4
         CS_DHTML.offsetY = evt.offsetY - ((evt.offsetY < -2) ? 0 : document.body.scrollTop);
      }
      return false;
   }
}

CS_DHTML.prototype.fadeMenu = function(evt,wrapId,iframeId,time){
  var wrapId = wrapId || null;
  var iframeId = iframeId || null;
  var time = time || this.defaultMenuTimeout;
  var evt = evt || null;
  this.timeout = setTimeout("CS_DHTML.switchMenuVisibility('" +wrapId+ "','" +iframeId+ "','hidden');",time);
  if(evt != null) evt.cancelBubble = true; 
}

CS_DHTML.prototype.stopFadeMenu = function(){
  this.stopTimeout();
}

CS_DHTML.prototype.stopTimeout = function(){
  clearTimeout(CS_DHTML.timeout);
}

// Stop strackimg the element being affected/modified
CS_DHTML.prototype.release = function(){
   if(CS_DHTML.selectedObj) CS_DHTML.selectedObj = null;
   return false;
}

CS_DHTML.prototype.getStyleObj = function(obj){
   var obj = obj || null;
   var el;
   if (obj.style) return obj.style;
   else{
      el = this.getElementById(obj);
      return (el != null) ? el.style : null;
   }
}

CS_DHTML.prototype.setStyle = function(obj,attrib,value){
  //value must be a string
  var obj = obj || null;
  var attrib = attrib || null;
  var value = value || '';
  var styleObj = this.getStyleObj(obj);
  if(styleObj != null && attrib != ''){
    eval("styleObj." + attrib + " = '" + value + "'");
    return true;
  }
  return false;
}

CS_DHTML.prototype.getStyle = function(obj,attrib){
  var obj = obj || null;
  var attrib = attrib || null;
  var styleObj = this.getStyleObj(obj);
  if(styleObj != null && attrib != ''){
    return eval('styleObj.' + attrib);
  }
  return false;
}

CS_DHTML.prototype.shiftTo = function(obj, x, y){
  // if strings are passed in then they must include the px suffix
  var obj = obj || null;
  var x = x || 0;
  var y = y || 0;
  if( this.setStyle(obj,'left',(typeof x == 'string')? x : x.toString() + 'px') 
      && this.setStyle(obj,'top',(typeof y == 'string')? y : y.toString() + 'px')) return true;
  else return false;
}


CS_DHTML.prototype.switchVisibility = function(obj,state){
  // obj can be a string or DOM element object.
  var obj = obj || null;
  // optional parameter state should be string "hidden" or "visible";
  var state = state || null;
  if(state == 'visible' || state == 'hidden'){
    return CS_DHTML.setStyle(obj,'visibility',state);
  } else {
    obj = this.getElementById(obj);
    if(obj != null && obj.style){ 
      state = this.getComputedStyle(obj,'visibility');
      obj.style.visibility = (state == "hidden" || state == null) ? "visible" : "hidden";
      return true;
    }
  }
  return false;
}

CS_DHTML.prototype.switchDisplay = function(obj,state){
  // obj can be a string or DOM element object.
  var obj = obj || null;
  // optional parameter state should be string "block" or "none";
  var state = state || null;
  if(state == 'block' || state == 'none'){
    return this.setStyle(obj,'display',state);
  } else {
    obj = this.getElementById(obj);
    if(obj != null && obj.style){
      state = this.getComputedStyle(obj,'display');
      obj.style.display = (state == "none" || state == null) ? "block" : "none";
      return true;
    }
  }
}

CS_DHTML.prototype.switchMenuVisibility = function(wrapId,iframeId,state){
  // If state is passed, it should be passed as if for the switchVisibility method ('hidden','visible')
  var wrapId = wrapId || null;
  var iframeId = iframeId || null;
  var state = state || null;
  state = (state == 'hidden') ? 'hidden' : (state == 'visible') ? 'visible' : null;
  var wrapEl = this.getElementById(wrapId);
  if(wrapEl != null){
    this.switchVisibility(wrapId,state);
    if(iframeId != null){
      state = (state == 'hidden') ? 'none' : (state == 'visible') ? 'block' : null;
      this.setStyle(iframeId,'height',wrapEl.offsetHeight-2);
      //Minus size of drop shadow. Concidered making it a pram but will leave as is for now.
      this.switchDisplay(iframeId,state);
    }
  }
}

CS_DHTML.prototype.setHeight = function(obj,h){
  // obj can be id string or element obj
  var h = h || 0;
  return this.setStyle(obj,'height',(typeof h == 'string')? h : h.toString() + 'px');
}

CS_DHTML.prototype.getHeight = function(obj){
  // obj can be id string or element obj
  return this.getStyle(obj,'height');
}

CS_DHTML.prototype.setWidth = function(obj,w){
  // obj can be id string or element obj
  var w = w || 0;
  return this.setStyle(obj,'width',(typeof w == 'string')? w : w.toString() + 'px');
}

CS_DHTML.prototype.getWidth = function(obj){
  // obj can be id string or element obj
  return this.getStyle(obj,'width');
}

CS_DHTML.prototype.addEventListener = function(obj,eventType,funcRef,captureFlag){
  // Event type should be string minus 'on' prefix, eg. mousedown instead of onmousedown.
  // funcRef should be a reference to a function not a string.
  var obj = obj || 0;
  var eventType = eventType || 0;
  var funcRef = funcRef || 0;
  var captureFlag = captureFlag || true;
  if(obj && eventType && typeof funcRef == 'function'){
    if(typeof obj == 'string') obj = this.getElementById(obj);
    if(obj != null){
      if(obj.addEventListener) obj.addEventListener(eventType, funcRef, captureFlag);
      else if(obj.attachEvent) obj.attachEvent('on'+eventType, funcRef);
      else eval("obj.on" + eventType + "=funcRef");
    }
  }
}

CS_DHTML.prototype.cancelEvent = function(){
  // This happens last due to event bubbling so it won't interfere with element events.
  return false;
}

CS_DHTML.prototype.getComputedStyle = function(obj,attrib){
  // obj can be id string or HTML object
  // attrib must be in hyphenated, CSS format
  var obj = obj || null;
  var attrib = attrib || null;
  var curSize = null;
  /* Safari does not support getComputedStyle - DOM2 method */
  if(typeof obj == "string") obj = this.getElementById(obj);
  if(obj != null && attrib != null){
    if(window.getComputedStyle){
      var compStyleObj = window.getComputedStyle(obj,"");
      curSize = compStyleObj.getPropertyValue(attrib);
    }else if(obj.currentStyle){
      var parts = attrib.split('-');
      attrib = parts[0];
      for(var i = 1; i < parts.length; i++){
        attrib += parts[i].substr(0,1).toUpperCase() + parts[i].substring(1,parts[i].length);
      }
      curSize = eval('obj.currentStyle.' + attrib);
    }
  }
  return curSize
}

//Creates invisible document node to use for fontSize change test.
CS_DHTML.prototype.createFontSizeTest = function(){
  var div = document.createElement('div');
  div.style.fontSize = "11px";
  div.style.display = "none";  
  var text = document.createTextNode("test text");
  div.appendChild(text); 
  this.fontSizeEl = div;
  addToOnLoad("document.body.appendChild(CS_DHTML.fontSizeEl)");
  addToOnLoad("CS_DHTML.origFontSize = CS_DHTML.getComputedStyle(CS_DHTML.fontSizeEl,'font-size')");
  addToOnLoadLast("CS_DHTML.addEventListener(document.body,'mouseover',document.CS_DHTML.userFontResize,false)");
}

// If user executes font resize with browser then do soft refresh which
// executes onloads, i.e. scrollComp initialization.
CS_DHTML.prototype.userFontResize = function(){
  // Can't use this reference because this method is assigned to an 
  // event handler for another object and this becomes that element object.
  if(document.CS_DHTML && document.CS_DHTML.fontSizeEl != null){
    curSize = document.CS_DHTML.getComputedStyle(document.CS_DHTML.fontSizeEl,'font-size');
    if(curSize != null && curSize != document.CS_DHTML.origFontSize) history.go(0);
  }
}


// This method returns the topY position of the 
CS_DHTML.prototype.moveScrollCompToSelectedItem = function(idRoot){
  var idRoot = idRoot || null;
  var listItem = this.getElementById(idRoot + 'SelectedItem');
  var wrapStyle = this.getStyleObj(idRoot + 'Wrapper');
  var wrapHeight = (wrapStyle != null) ? parseInt(wrapStyle.height) : null
  var listItemBottomY = listItem.offsetTop + listItem.offsetHeight;

  if(listItem != null && wrapHeight != null && listItemBottomY > wrapHeight){
    var panel = this.getElementById(idRoot + 'Panel');
    var bar = this.getElementById(idRoot + 'Bar');
    var pathStyle = this.getStyleObj(idRoot + 'Path');
    if(panel == null || bar == null && pathStyle == null ) return;

    var newPanelY;
    var newBarY;
    //Move Panel
    newPanelY =  0 - listItem.offsetTop;
    newBarY = (listItemBottomY/panel.offsetHeight) * (parseInt(pathStyle.height));
    var barMaxY = parseInt(pathStyle.height) - bar.height;
    if(newBarY > barMaxY) newBarY = barMaxY;
    this.shiftTo(panel, null,newPanelY);
    this.shiftTo(bar,null,newBarY);
  }
}

// Move Panel and Bar;
CS_DHTML.prototype.moveScrollComp = function(evt){
   var evt = (evt) ? evt : event;
   if(CS_DHTML.selectedObj != null){
      var bar = CS_DHTML.selectedObj;
      var barStyle = CS_DHTML.getStyleObj(bar);
      var idRoot = bar.id.substring(0,bar.id.lastIndexOf("Bar"));
      var pathStyle  = CS_DHTML.getStyleObj(idRoot + "Path");
      var wrapHeight = parseInt(CS_DHTML.getHeight(idRoot + "Wrapper"));
      var panel      = CS_DHTML.getElementById(idRoot + "Panel");
      var panelStyle = CS_DHTML.getStyleObj(panel);

      // If the scrollPanel is taller then the content then don't scroll is inactive.
      // This test is handled here instead of scroll comp initialization in case content changes dynamically or via font increase by user.
      if(wrapHeight >= panel.offsetHeight){
         barStyle.top = "0px";
         panelStyle.top = "0px";
         return false;
      }   
      

      // Move Bar
      evt.cancelBubble = true;
      var newY = (evt.clientY) ? (evt.clientY - CS_DHTML.offsetY) : (evt.pageY) ? (evt.pageY - CS_DHTML.offsetY) : null;
      var barMaxY = parseInt(pathStyle.height) - bar.height;
      if(newY < 0) newY = 0; // bar start top position must be 0 in relation to container.
      else if(newY > barMaxY) newY = barMaxY;
      CS_DHTML.shiftTo(bar, null, newY);

      // Move Panel
      var ratio = parseInt(barStyle.top) / (parseInt(pathStyle.height) - parseInt(bar.height));
      var newPanelTop = 0 - ((panel.offsetHeight - wrapHeight) * ratio);
      // 0 should always be the panel start position. Use wrapper div with relative positioning.
      panelStyle.top = newPanelTop + "px";
      return false
   }
}

CS_DHTML.prototype.setScrollDisplay = function(idRoot,state){
  // This works differently then the other switches. If you don't pass a state then the state is set to off.
  // This is why this method name is started with set instead of switch. :)
  var state = state || 'none';
  this.switchDisplay(idRoot+'BarWrapper',state);
}

CS_DHTML.prototype.initScrollComp = function(idRoot){
   // id param should be the root used to name the elements of scroll comp, i.e. scrollBar, scrollPanel, and scrollPath;
   // Add event handler to target, i.e. scrollBar onmousedown event listener;

   // Was initially going to put content size test here and not place event handler if not needed but decided to put
   // test in moveScrollComp method so that if content changes dynamically then scroll can become active.
   var bar = this.getElementById(idRoot + "Bar");
   var wrapEl = this.getElementById(idRoot + "Wrapper");
   var wrapStyle = (wrapEl != null) ? wrapEl.style : null;
   var panelEl = this.getElementById(idRoot + "Panel");
   var barWrapperEl = this.getElementById(idRoot + "BarWrapper");

   if(bar != null && wrapStyle != null && panelEl != null){
     if(parseInt(wrapStyle.height) >= panelEl.offsetHeight && barWrapperEl != null){
       this.setScrollDisplay(idRoot,'none');
       var listWrapper = this.getElementById(idRoot + 'ListWrapper');
       var listWrapper = this.getElementById(idRoot + 'ListWrapper');
       if(listWrapper != null) var panelWidth = listWrapper.offsetWidth;
       this.setHeight(idRoot+'Wrapper',panelEl.offsetHeight);
       var barWrapperWidth = this.getWidth(idRoot+ 'BarWrapper')
       if(panelWidth != null && barWrapperWidth != null){
         this.setWidth(panelEl,parseInt(panelWidth) + parseInt(barWrapperWidth));
       }
     }else{
       // The following line of code blocks dragging of the wrapper during text content text selection.
       wrapEl.onmousedown = this.cancelEvent;
       bar.onmousedown = this.engage;
       var selectedItem = this.getElementById(idRoot + 'SelectedItem');
       // The following test is done here so that that the scrollbar initialization can be
       // triggered by an event as well as upon document initialization.
       if(this.initialized && !this.scrollComps) document.onmousemove = CS_DHTML.moveScrollComp;
       if(selectedItem != null) this.moveScrollCompToSelectedItem(idRoot);
       // This switch is used to add the moveScroll event handler if a scrollbar is on the page. See this.init()
       this.scrollComps = true;
     }
   }
}

CS_DHTML.prototype.debug = function(str){
  //Used for testing event listener/handlers
  var str = str || 'here';
  alert(str);
}

CS_DHTML.prototype.preload = function(){
  this.createFontSizeTest();
}

CS_DHTML.prototype.onload = function(){
  if(this.scrollComps) document.onmousemove = CS_DHTML.moveScrollComp;
  document.onmouseup = this.release;
  this.initialized = 1;
}

// Instanciate Object
var CS_DHTML = new CS_DHTML;
CS_DHTML.preload();
addToOnLoadLast("CS_DHTML.onload()");

// The following line of code is an example of the line used to initialize a scrollComp. The line should appear above the HTML code.
//addToOnLoad("CS_DHTML.initScrollComp('scroll')");

// The following is an example of the HTML Code used to create the scrollComp
/*
   <script language="JavaScript"><!--
      addToOnLoad("CS_DHTML.initScrollComp('restaurants')");
   //--></script>
   <div class="browseBySubHeaderBlock">Browse by Restaurants</div>
   <div class="browseByListWrapper">
      <div id="restaurantsWrapper" class="scrollPanelWrapper" style="height:160px;">
         <div id="restaurantsPanel" class="scrollPanel">
            Content Here
            Content Here
            Content Here
            Content Here
            Content Here
            Content Here
            Content Here
            Content Here
            Content Here
            Content Here
         </div>
         <div id="restaurantsBarWrapper" class="scrollBarWrapper" style="left:132px; width:11px; height:160px;">
            <div id="pathLine" class="browseByScrollPath" style="height:160px;"></div>
            <div id="restaurantsPath" style="position:absolute; top:1px; height:158px">
               <img id="restaurantsBar" style="position:absolute;top:0;" src="http://images.citysearch.com/./images/scroll_bar.gif" border="0"/>
            </div>
         </div>
      </div>   
   </div>
*/

