// Pan Carousel(14-June-2010)
// by Vic Phillips http://www.vicsjavascripts.org.uk

// To continiously pan elements nested in a DIV element.
// The pan speed and direction is relative to the mouse position over the <DIV> parent DIV.
// The pan may also be activated by event calls from other elements.
// With both 'Vertical' and 'Horizontal' applications.
// There may be as many applications on a page as required.

// ****** Application Notes.

// **** The HTML and CSS Code.
//
// The images are nested in a DIV.
// This DIV must be nested in a parent DIV.
// The parent DIV  must be assigned a unique ID name.
// These DIVs must have a style position of 'relative' or 'absolute'
// and the parent DIV width and height assigned by CSS class or inline style.
//

// **** Initialisation.
//
// Instances of the script are generated on completion of the page load.
// The script instance must be assigned to a global variable if the instance
// is to be controlled by event calls to instance functions by external elements.
// e.g.
//  var PC=new zxcPanCarousel({
//   ID:'tst',           // the unique ID name of the slide parent DIV element.                                    (string)
//   Mode:'Horizontal',  // (optional) the mode, 'Horizontal' or 'Vertical'.                                       (string, default = 'Horizontal')
//   Distance:250,       // (optional) the distance from the ends to pan.                                          (digits, default = 100)
//   MaxSpeed:10,        // (optional) the maximum speed to pan.                                                   (digits, default = 5)
//   ReverseEnds:false,  // (optional) reverse the ends mouseover pan direction.                                   (boolean, default = false)
//   Auto:2,             // (optional) the speed of automatic pan on mouseout of the  parent DIV element.          (digits, default = NO automatic pan)
//   AddEvents:[         // (optional) an array defining the event calls to add to elements(see Script Functions). (array, default = no event calls)
//    // where each field is an array:
//    //  field 0 = the unique ID name of the element.     (string)
//    //  field 1 = the function name.                     (string)
//    //  field 2 = the event type.                        (string)
//    //  field 3 = the parameter to pass to the function. (digits)
//    ['B','Back','mousedown',4],
//    ['B','Pause','mouseup']
//   ]
//  });
//

// **** Script Functions

// The script instance must be assigned to a global variable if script functions
// are to be called by inline event from other elements.

// To Pan
//  to pan the images forward.
//  Inline Example: PC.Pan(1);
//  where parameter 0 = specifies the pan speed, +ve numbers pan clockwise and -ve numbers pan anticlockwise. (digits)

// To Pause
//  pause panning.
//  Inline Example: PC.Pause();
//  Note that Pause guards against false mouseouts of the instance parent node.

// Alernatively the elements may be assigned a unique ID name and the event call attached calling function
//  PC.AddEvents([  // an array of arrays defining the events to be added. (array)
//   where each field is an array:
//    field 0 = the unique ID name of the element.     (string)
//    field 1 = the function name.                     (string)
//    field 2 = the event type.                        (string)
//    field 3 = the parameter to pass to the function. (see Script Functions)
//    ['B','Pan','mousedown',-4],
//    ['B','Pause','mouseup']
//  ]);
//
//
//


// **** Functional Code(4.15K) - NO NEED to Change

function zxcPanCarousel(o){
 this.mde=o.Mode.charAt(0).toUpperCase()=='V'?['top','height',1,'offsetTop','offsetHeight']:['left','width',0,'offsetLeft','offsetWidth'];
 this.p=document.getElementById(o.ID);
 var slide=this.p.getElementsByTagName('DIV')[0];
 slide.style[this.mde[1]]='100000px';
 for (var clds=slide.childNodes,fst,lst,z0=0;z0<clds.length;z0++){
  if (clds[z0].nodeType==1){
    if (!fst){
     fst=clds[z0];
    }
    lst=clds[z0];
  }
 }
 this.to=null;
 var wh=lst[this.mde[3]]+lst[this.mde[4]];
 this.slide=[slide];
 var nu=Math.floor(this.p[this.mde[4]]/wh)+3;
 for (var max,z1=0;z1<nu;z1++){
  max=wh*z1-wh;
  if (z1>0){
   this.slide[z1]=slide.cloneNode(true);
   this.p.appendChild(this.slide[z1]);
  }
  this.slide[z1].style[this.mde[1]]=wh+'px';
  this.slide[z1].style[this.mde[0]]=max+'px';
 }
 this.min=wh;
 this.max=max;
 this.lgth=this.slide.length;
 var pan=typeof(o.Distance)=='number'?o.Distance:100;
 this.addevt(this.p,'mousemove','move');
 this.addevt(this.p,'mouseout','Pause');
 this.ends=[pan,this.sv(this.p,this.mde[1])-pan];
 this.rev=typeof(o.ReverseEnds)=='boolean'&&o.ReverseEnds?-1:1;
 this.auto=typeof(o.Auto)=='number'?o.Auto:false;
 this.maxspd=o.MaxSpeed||5;
 this.inc=this.auto||0;
 this.run=true;
 this.AddEvents(o.AddEvents);
 if (this.auto){
  this.scroll();
 }
}


zxcPanCarousel.prototype={

 Pan:function(nu){
  this.inc=typeof(nu)=='number'?nu:this.maxspd/2;
  if (this.run){
   this.scroll();
  }
 },

 Pause:function(){
  var e=window.event?window.event:arguments.callee?arguments.callee.caller.arguments[0]:false;
  if (e&&e.type=='mouseout'){
   var obj=e.relatedTarget||e.toElement;
   if (obj){
    while (obj.parentNode){
     if (obj==this.p){
      return false;
     }
     obj=obj.parentNode;
    }
   }
  }
  if (this.auto){
   this.inc=this.auto*(this.inc<0?1:-1);
  }
  else {
   this.run=true;
   clearTimeout(this.to);
  }
 },

 AddEvents:function(ary){
  ary=typeof(ary)=='object'&&ary.constructor==Array?ary:[];
  for (var but,type,z1=0;z1<ary.length;z1++){
   var but=document.getElementById(ary[z1][0]),type=this.evttype(ary[z1][2]);
   if (but&&this[ary[z1][1]]&&type){
    this.addevt(but,type,ary[z1][1],ary[z1][3]);
   }
  }
 },

 move:function(p,e){
  var x=this.mse(e)[this.mde[2]]-this.pos(this.p)[this.mde[2]];
  this.inc=this.maxspd*(x<this.ends[0]?1-x/this.ends[0]:x>this.ends[1]?(x-this.ends[1])/this.ends[0]*-1:0)*this.rev;
  if (this.run){
   this.scroll();
  }
 },

 scroll:function(){
  clearTimeout(this.to)
  this.run=false;
  for (var z0=0;z0<this.lgth;z0++){
   this.slide[z0].style[this.mde[0]]=this.sv(this.slide[z0],this.mde[0])+Math.ceil(this.inc)+'px';
   if ((this.inc<0&&this.sv(this.slide[z0],this.mde[0])<-this.min)||(this.inc>0&&this.sv(this.slide[z0],this.mde[0])>this.max)){
    this.slide[z0].style[this.mde[0]]=this.sv(this.slide[z0],this.mde[0])+(this.min*this.slide.length*(this.inc<0?1:-1))+'px';
   }
  }
  var oop=this;
  this.to=setTimeout(function(){ oop.scroll(); },50);
 },

 evttype:function(et){
  type=(typeof(et)=='string'?et:'').toLowerCase().replace('on','');
  return type=='click'||type=='mousedown'||type=='mouseup'||type=='mouseover'||type=='mouseout'||type=='static'?type:false;
 },

 addevt:function(o,t,f,p){
  var oop=this;
  if (o.addEventListener) o.addEventListener(t,function(e){ return oop[f](p,e);}, false);
  else o.attachEvent('on'+t,function(e){ return oop[f](p,e); });
 },

 mse:function(e){
  if (document.all) return [e.clientX+this.docs()[0],e.clientY+this.docs()[1]];
  return [e.pageX,e.pageY];
 },

 docs:function(){
  if (!document.body.scrollTop) return [document.documentElement.scrollLeft,document.documentElement.scrollTop];
  return [document.body.scrollLeft,document.body.scrollTop];
 },

 pos:function(obj){
  var rtn=[0,0];
  while(obj){
   rtn[0]+=obj.offsetLeft;
   rtn[1]+=obj.offsetTop;
   obj=obj.offsetParent;
  }
  return rtn;
 },

 sv:function(obj,par){
  if (obj.currentStyle) return parseInt(obj.currentStyle[par.replace(/-/g,'')]);
  return parseInt(document.defaultView.getComputedStyle(obj,null).getPropertyValue(par.toLowerCase()));
 }

}

