
// Multi Mode Scroller (26-August-2011)
// by Vic Phillips - http://www.vicsjavascripts.org.uk/

/*
  A scroller  with options for 'Continuous', 'End Control' and 'Step' scroll applications
  and with both 'Horizontal' and 'Vertical' modes of execution.
  The scroll may be controlled by inline event calls.

  The functional code size is:
   3.23K with only 'Continuous'.
   5.00K with only 'Step'.
   4.15K with only 'End Control'.
   8.05K with 'Continious', 'Step', 'End Control' and 'Persistence'.

  ****** Application Notes.

   **** The HTML and CSS Code.

    The scroller is rendered in a parent DIV.
    The parent DIV must be assigned a unique ID name.
    This parent DIV must have a CSS position of 'relative' or 'absolute'
    and the width and height defined by CSS class rule or inline style.

    The slider content is nested in the first DIV element nested in the parent DIV.
    The slider content elements must have their width and height defined by CSS class rule or inline style.
    Do not use margin-Right for 'Horizontal' or margin-Bottom' for 'Vertical' applications.


   **** Initializing the Script.

    Each instance of the script must be initialized by calling function 'zxcMMScroller'
    after the page has loaded passing the instance options as an object.
    Each instance of the script must be assigned to a global variable to allow the script to be controlled by inline event calls.
    e.g.
     E1=new zxcMMScroller({
      ID:'example1',     // the unique ID name of the parent DIV.                                                       (string)
      Mode:'Horizontal', //(optional) the mode of execution, 'Horizontal' or 'Vertical'.                                (string, default = 'Horizontal')
      Duration:-2,       //(optional) the scroll direction and duration in seconds, >0 = clockwise, <0 = anticlockwise. (number, default = -10)
      Wrap:false,        //(optional) false = scroll will not wrap first to last or last to first.                      (boolean, default = true)
      StartScroll:true,  //(optional) true = start the scroll on initialisation.                                        (number, default = true)
      DaysPersistence:1, //(optional) the number of days to remember and restore the last position.                     (number, default = no persistence)
      // Step Scroller Options (optional)
      StepCenter:true,   //(optional) true = center the current content element in the parent DIV.                      (boolean, default = false)
      StepHold:1000,     //(optional) the hold duration between steps in milli seconds.                                 (number, default = StepDuration)
      // End Scroller Options (optional)
      EndSize:100,       // the scroll is controlled by mousemove over the ends.                                        (number)
      EndSpeed:5         //(optional) the maximum scroll speed on mousemove over the ends.                              (number, default = option Speed)
     });

   **** Controlling the Script.

    The script is controlled by inline event calls to functions 'Scroll' and 'End'

    *** Continuous Scroll Applications.

     ** Function 'Scroll'.
      function 'Scroll' is used to control the speed and direction of the scroll.
      e.g.
       <div id="example1" class="horizontal" onmouseover="E1.Pause();" onmouseout="E1.Scroll();" >
      where
       E1  = the global variable assigned the script instance.
       parameter 0 = (optional) the scroll speed, >0 = clockwise, <0 = anticlockwise. (number, default = the default scroll speed)

     ** Function 'ScrollTo'.
      function 'ScrollTo' is used to scroll to a specific image.
      e.g.
       <div id="example1" class="horizontal" onmouseover="E1.Pause();" onmouseout="E1.Scroll();" >
      where
       E1  = the global variable assigned the script instance.
       parameter 0 = the index number of the spcific image.    (number)

    *** Step Applications.

     ** Function 'Step'.
      function 'Step' is used to pause or start the step scroll.
      e.g.
       <div id="E1" class="horizontal" style="left:0px;width:400px;height:150px;"  onmouseover="E1.Step(false);" onmouseout="E1.Step(true);" >
      where
       E1  = the global variable assigned the script instance.
       parameter 0 = false = pause the step auto rotation, true = start the step auto rotation. (boolean)

     ** Function 'StepTo'.
      function 'StepTo' is used to step scroll to a specific content element or to change the step scroll direction.
      e.g.
        <input type="button" name="" value="StepTo 2" onmouseup="E1.StepTo(2);"/>
        <input type="button" name="" value="Step Right" onmouseup="E1.StepTo(true,1);"/>
     where
       E1  = the global variable assigned the script instance.
       parameter 0 = number = the index number of the content element, true = start auto rotation. (number or boolean)
       parameter 1 = (optional) the scroll direction, > 0 = clockwise, < 0 = anticlockwise.        (number, default = the default direction)


     ** Function 'StepNext'.
      function 'StepNext' is used to step scroll to the next or previous content element(this will also to change the step scroll direction).
      e.g.
        <input type="button" name="" value="Next" onmouseup="E1.StepNext(1);"/>
        <input type="button" name="" value="Previous" onmouseup="E1.StepNext(-1);"/>
     where
       E1  = the global variable assigned the script instance.
       parameter 0 = (optional) the scroll direction, > 0 = previous, < 0 = next. (number, default = the default direction)

    *** End Control Applications.

      The speed and direction of the scroll by mousemove and mouseover of the parent DIV.
      The mousemove and mouseout events are added by the script.


*/


// ****** Functional Code - NO NEED to Change.


function zxcMMScroller(o){
 var oop=this,mde=o.Mode,mde=typeof(mde)=='string'&&mde.charAt(0).toUpperCase()=='V'?['top','offsetTop','offsetHeight','height',1]:['left','offsetLeft','offsetWidth','width',0],obj=document.getElementById(o.ID),slide=obj.getElementsByTagName('*')[0],fst=slide.getElementsByTagName('*')[0],sz,nu,psz=obj[mde[2]],esz=o.EndSize,esz=typeof(esz)=='number'&&esz>0?esz:false,clds=slide.childNodes,lst=clds[clds.length-1],lst,ctr=o.StepCenter==true,slider=document.createElement('DIV'),ary=[],z0=0,z1=0,sec=o.Duration,ssz=o.ParentSize,ss=o.StartScroll,ss=typeof(ss)!='boolean'||ss==true,hold=o.StepHold,hold=typeof(hold)=='number'?hold:false,nme=o.ID+'=',days=o.DaysPersistence,days=typeof(days)=='number'&&this.cookie?days:false,now;
 obj.style.overflow='hidden';
 slide.style.position='absolute';
 slide.style[mde[3]]='10000px';
 for (;z0<clds.length;z0++){
  if (clds[z0].nodeType==1){
   lst=clds[z0];
   ary.push(lst);
  }
 }
 sz=lst[mde[1]]+lst[mde[2]],nu=Math.ceil(psz/sz)+(!ctr?1:2);
 slider.style.position='absolute';
 obj.appendChild(slider);
 for (;z1<nu;z1++){
  slide=z1>0?slide.cloneNode(true):slide;
  slide.style[mde[0]]=sz*z1-(!ctr?0:sz)+'px';
  slider.appendChild(slide);
 }
 this.obj=obj;
 this.mde=mde;
 this.sz=sz;
 this.slider=slider;
 this.dly=null;
 this.ary=ary;
 this.step=false;
 this.mm=false;
 this.end=false;
 this.ms=this.ud=typeof(sec)=='number'?Math.abs(sec)*1000:10000;
 this.ud=typeof(sec)=='number'&&sec>0?true:false;
 this.days=days;
 this.nme=nme;
 now=this.days&&this.cookie()?this.cookie()*1:this.ud?0:-sz;
 slider.style[mde[0]]=now+'px';
 this.now=now;
 this.wrap=typeof(o.Wrap)!='boolean'||o.Wrap?[-this.sz,0]:[-sz+obj[mde[2]],-ary[1][mde[1]]];
 if (esz&&this.endinit){
  this.endinit(o,mde,obj,esz,sec,fst,lst,ss);
 }
 else if (oop.engine){
  this.mde=mde[0];
  if (hold&&this.stepinit){
   this.stepinit(o,mde,obj,ary,sz,hold,ctr,now,ss);
  }
  else if (this.Scroll&&ss){
   this.scroll();
  }
 }
}

zxcMMScroller.prototype={

// Required for 'Continious Scroll', 'Step Scroll' and 'End Control' Applications.
 engine:function(s,f,ms,scale,t){
  clearTimeout(this.to);
  this.animate(s,f,new Date().getTime(),ms*(!scale?1:Math.abs((f-s)/scale)),t);
 },

 animate:function(s,f,srt,mS,t){
  var oop=this,op=oop.oop,ms=new Date().getTime()-srt,d=(f-s)/mS*ms+s;
  if (isFinite(d)){
   oop.now=d;
  }
  oop.slider.style[oop.mde]=oop.now+'px';
  if (ms<mS){
   oop.dly=setTimeout(function(){oop.animate(s,f,srt,mS,t); },10);
  }
  else {
   oop.now=f;
   if (!oop.step){
    if (!t&&oop.wrap[1]==0){
     oop.now=oop.ud?0:-oop.sz;
     oop.scroll();
    }
   }
   else if (oop.run){
    oop.dly=setTimeout(function(){ oop.StepTo(null,true); },oop.hold);
   }
  }
 },

// Required for 'Continious Scroll' Applications.
 Scroll:function(ud){
  this.Pause();
  this.ud=typeof(ud)=='number'&&(ud==1||ud==-1)?ud<0?true:false:typeof(ud)=='string'?!this.ud:this.ud;
  this.ms=typeof(ud)=='number'&&ud>1?ud*1000:this.ms;
  this.scroll();
 },

 Pause:function(){
  clearTimeout(this.dly);
  if (this.days){
   this.cookieset(this.now);
  }
 },

 ScrollTo:function(t){
  this.scroll(t);
 },

 scroll:function(t){
  this.Pause();
  var oop=this,ary=oop.ary,f=this.now,to=ary[t]?-ary[t][oop.mde[1]]:oop.wrap[oop.ud?0:1];
  oop.engine(oop.now,to,oop.ms,oop.sz,typeof(ary[t])=='number');
 },


// Optional 'Step' Code
 stepinit:function(o,mde,obj,clds,sz,hold,ctr,now,ss){
  var oop=this,hold=o.StepHold,lst,ary=[],z0=0;
  for (;z0<clds.length;z0++){
   if (clds[z0].nodeType==1){
    lst=-clds[z0][mde[1]]+(!ctr?0:(this.obj[mde[2]]-clds[z0][mde[2]])/2);
    ary.push(lst);
   }
  }
  ary.push(lst-(sz+lst)+ary[0]);
  oop.step=true;
  oop.lgth=ary.length-1;
  oop.ary=ary;
  oop.ctr=ctr;
  oop.cnt=now?now*1:oop.ud?oop.lgth:0;
  now=oop.ary[now]?now:0;
  oop.slider.style[mde[0]]=oop.ary[now]-sz+'px'
  oop.now=now?ary[now]:ary[oop.ud?oop.lgth:0];
  oop.cnt=now;
  oop.hold=hold*1000;
  oop.nowrap=typeof(o.Wrap)!='boolean'||o.Wrap?false:[-sz+obj[mde[2]],ary[1]];
  oop.slider.style[mde[0]]=ary[now]+'px';
  oop.engine.now=ary[now];
  oop.now=ary[now];
  if (ss){
   oop.Step(true,oop.hold);
  }
 },

 Step:function(step,ms){
  if (this.ary){
   var oop=this,e=window.event||arguments.callee.caller.arguments[0];
   if (ms||this.ckevt(e)){
    if (!step){
     clearTimeout(this.dly);
     this.run=false;
    }
    else {
     this.dly=setTimeout(function(){ oop.StepTo(null,true); },ms||500);
    }
   }
  }
 },

 StepTo:function(nxt,run){
  this.ud=typeof(nxt)=='boolean'&&typeof(run)=='number'?run>0:this.ud;
  var oop=this,ud=this.ud?1:-1,ary=this.ary,nw=this.nowrap,std=typeof(nxt)!='number',lgth=this.lgth,now,to,cnt=this.cnt,cnt=!std&&cnt==lgth?0:cnt;
  if (ary){
   run=typeof(nxt)=='boolean'&&nxt?true:run;
   if (std){
    if (!nw&&((ud<0&&cnt==lgth)||(ud>0&&cnt==0))){
     cnt=ud<0?0:lgth;
    }
    now=ary[cnt];
    nxt=cnt-ud;
    nxt=!nw?nxt:Math.max(Math.min(nxt,lgth-1),0);
   }
   if (typeof(ary[nxt])=='number'&&nxt!=cnt){
    clearTimeout(this.dly);
    this.run=run==true;
    to=ary[nxt];
    if (nw&&!this.ctr&&((ud<0&&to<nw[0])||(ud>0&&to>nw[1]))){
     to=nw[ud<0?0:1];
    }
    else {
     this.cnt=nxt;
    }
    if (this.days){
     this.cookieset(this.cnt);
    }
    this.engine(std?now:this.now,to,this.ms);
   }
  }
 },

 StepNext:function(ud){
  this.ud=typeof(ud)=='number'?ud>0:this.ud;
  this.StepTo();
 },

 ckevt:function(e){
  var obj=e.relatedTarget?e.relatedTarget:e.type=='mouseout'?e.toElement:e.fromElement;
  if (!obj||obj==this.obj){
   return false;
  }
  while (obj.parentNode){
   if (obj==this.obj){
    return false;
   }
   obj=obj.parentNode;
  }
  return true;
 },

// Optional 'End Control' Code
 endinit:function(o,mde,obj,esz,spd,fst,lst,ss){
  var oop=this,max=o.MaxEndSpeed,min=o.MinEndSpeed,now=this.days&&this.cookie()?this.cookie()*1:-fst[mde[1]];
  this.nowrap=typeof(o.Wrap)!='boolean'||o.Wrap?false:[-lst[mde[1]]-lst[mde[2]]+obj[mde[2]],-fst[mde[1]]];
  this.slider.style[mde[0]]=(this.nowrap?-fst[mde[1]]:0)+'px';
  this.slider.style[mde[0]]=now+'px';
  this.slider=[this.slider,now];
  this.panends=[esz,obj[mde[2]]-esz];
  this.ends=[-1,1];
  this.min=typeof(min)=='number'?min:0;
  this.maxspd=typeof(max)=='number'?max:spd;
  this.inc=this.min;
  obj.onmousemove=function(event){ oop.emove(event); }
  obj.onmouseout=function(event){ oop.epause(event); }
  if (ss){
   this.escroll();
  }
 },

 epause:function(e){
  var oop=this,e=window.event||e;
  if (this.ckevt(e)){
   this.inc=this.min;
   clearTimeout(this.dly);
   if (this.days){
    this.cookieset(this.slider[1]);
   }
   this.dly=setTimeout(function(){ oop.escroll(); },25);
  }
 },

 escroll:function(){
  clearTimeout(this.dly);
  var oop=this,slider=this.slider,ud=this.inc,sz=this.sz,min=this.min,min=ud!=0?Math.abs(min)*(ud>0?1:-1):min,nw=this.nowrap,now=slider[1]+ud;
  slider[1]=!nw?((ud<0&&now<-sz)||(ud>0&&now>0)?ud<0?0:-sz:slider[1])+ud:(ud<0&&now<nw[0])||(ud>0&&now>nw[1])?nw[ud<0?0:1]:now;
  slider[0].style[this.mde[0]]=slider[1]+'px';
  this.min=min;
  this.dly=setTimeout(function(){ oop.escroll(); },20);
 },

 emove:function(e){
  var e=window.event||e,mde=this.mde[4],x=this.mse(e)[mde]-this.pos(this.obj)[mde],ends=this.panends;
  this.inc=(x>ends[1]?this.maxspd*((x-ends[1])/ends[0]):x<ends[0]?-this.maxspd*(1-x/ends[0]):0);
  if (this.dly==null){
   this.escroll();
  }
 },

 mse:function(e){
  if (window.event){
   var docs=[document.body.scrollLeft,document.body.scrollTop];
   if (!document.body.scrollTop){
    docs=[document.documentElement.scrollLeft,document.documentElement.scrollTop];
   }
   return [e.clientX+docs[0],e.clientY+docs[1]];
  }
  return [e.pageX,e.pageY];
 },

 pos:function(obj){
  var rtn=[0,0];
  while(obj){
   rtn[0]+=obj.offsetLeft;
   rtn[1]+=obj.offsetTop;
   obj=obj.offsetParent;
  }
  return rtn;
 },

// Optional 'Persistance' Code
 cookie:function(){
  var re=new RegExp(this.nme+'[^;]+','i');
  if (document.cookie.match(re)){
   return document.cookie.match(re)[0].split("=")[1];
  }
  return null
 },

 cookieset:function(v){
  document.cookie=this.nme+v+';expires='+(new Date(new Date().getTime()+this.days*86400000).toGMTString())+';path=/';
 }

}



   
