/***********************************************************************************************
 * Software License Agreement (BSD License - http://www.opensource.org/licenses/bsd-license.php)
 * 
 * NeauxScript V0.2-2
 * Copyright (c) 2007-2008  NeauxWare Technologies
 * All rights reserved.

 * Redistribution and use in source and binary forms, with or without modification, are permitted
 * provided that the following conditions are met:
 * ===> Redistributions of source code must retain the above copyright notice, this list of  
 *      conditions and the following disclaimer. 
 * ===> Redistributions in binary form must reproduce the above copyright notice, this list  
 *      of conditions and the following disclaimer in the documentation and/or other materials 
 *      provided with the distribution. 
 * ===> Neither the name of the NeauxWare Technologies nor the names of its contributors may be used to 
 *      endorse or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ***********************************************************************************************/
Neaux = {
  Module: {
    Name: "NeauxScript",
    Version: "0.2-2",
    Date: "16-Mar-2008"
  },
  Author: "H.G.Laffler",
  Organization: "Neauxware Technologies",
  Home: "http://www.neauxware.com",
  SWVersion : function() { return this.Module.Name+" V"+Neaux.Module.Version+" ("+Neaux.Module.Date+") "; },
  Copyright: function() { return " \xA92007-2008 "+this.Organization+" ("+this.Home+") All Rights Reserved."; },
  toString : function() { return this.SWVersion()+"; "+this.Copyright(); },
  ___end : null
}

// Neaux.Api - Provide simple interface to objects
Neaux.Api = {
  _ObjectRegistry : [], // Used to keep object references in APIs so we can have multiple instances
  ___end : null
}

// Neaux.Agent - Browser Stuff.
Neaux.Agent = {
  SetOpacity : function( aElement, aOpacity, aCompElement) {
    // Detect Agent support for Opacity if it hasn't already been done
    if ( typeof this._OpacitySupport == "undefined")
      this._DetectOpacitySupport();
    // If no arguments have been passed or Opacity is not supported, then return a boolean
    if ( this._OpacitySupport == "none" || !aElement)
      return (this._OpacitySupport != "none");
    
    // The optional complimentary element argument provides crossfade capability
    // the target element is assigned the specified opacity percentage
    // the complimentary element is assigned the remainder (100-x)
    var theCompOpacity = aCompElement ? parseFloat(100.0 - aOpacity) : undefined;
    if ( this._OpacitySupport != "ie") {
      // Bug in Mozilla/Gecko
      aOpacity = (aOpacity == 100.0) ? 0.999999 : parseFloat(aOpacity/100.0);
      if ( aCompElement) theCompOpacity = (theCompOpacity == 100.0) ? 0.999999 : parseFloat(theCompOpacity/100.0);
    }
    switch ( this._OpacitySupport) {
      case "ie":
        aElement.style.filter = "alpha(opacity:"+aOpacity+")"; 
        if (aCompElement) aCompElement.style.filter = "alpha(opacity:"+theCompOpacity+")"; 
        break;
      case "w3c":
        aElement.style.opacity = aOpacity; 
        if (aCompElement) aCompElement.style.opacity = theCompOpacity; 
        break;
      case "mozilla":
        aElement.style.MozOpacity = aOpacity; 
        if (aCompElement) aCompElement.style.MozOpacity = theCompOpacity; 
        break;
      case "khtml":
        aElement.style.KhtmlOpacity = aOpacity; 
        if (aCompElement) aCompElement.style.KhtmlOpacity = theCompOpacity; 
        break;
      default:
        return false;
    }
    return true;
  },
  _DetectOpacitySupport : function() {
    if ( document.all && document.body.style.filter != undefined)
      this._OpacitySupport = "ie";
    else if ( document.body.style.opacity != undefined)
      this._OpacitySupport = "w3c";
    else if ( document.body.style.MozOpacity != undefined)
      this._OpacitySupport = "mozilla";
    else if ( document.body.style.KhtmlOpacity != undefined)
      this._OpacitySupport = "khtml";
    else
      this._OpacitySupport = "none"; 
  },
  _OpacitySupport: undefined,
  ___end : null
}
// ISSUE: NOT SURE IF THIS SHOULD GO INTO THE AGENT OBJECT, MAYBE IF A DOM OBJECT COMES ALONG (LOW PRIORITY)
Neaux.Agent.ElementRect = function( aElement) {
  var thePtr = aElement; 
  this.x = this.y = 0;
  while (thePtr != null) {
    this.x += thePtr.offsetLeft;
    this.y += thePtr.offsetTop;
    thePtr = thePtr.offsetParent;
  } 
  this.width = aElement.width;
  this.height = aElement.height;
}


// Neaux.Event - Wrappers to manage agent events & object contexts
Neaux.Event = {
  // Event Registry
  //    When an event is triggered and a method of an object is specified as the Event Handler
  //    (e.g. this.Handler), the method is called, however, "this" is defined in the context
  //    of the window class.  A method of resolving "this" is required.  The Event Registry
  //    allows the element ID (or any tag) to be associated with an object.  This is stored
  //    as an associative array.
  //    
  Registry : [],
  Register : function( aTag, aThis) {
    this.Registry[aTag] = aThis;
  },
  //
  // Attach/Detach
  //    Cross-browser implementation to add/remove event handler for a specified element.
  //    This is for event bubbling only.
  Attach : function( aElement, aEvent, aHandler) {
    if ( document.attachEvent)
      aElement.attachEvent("on"+aEvent,aHandler);
    else if (window.addEventListener)
      aElement.addEventListener(aEvent,aHandler,false);
    else
      aElement["on"+aEvent] = aHandler;
  },
  Detach : function( aElement, aEvent, aHandler) {
    if ( document.detachEvent)
      aElement.detachEvent("on"+aEvent,aHandler);
    else if (window.addEventListener)
      aElement.removeEventListener(aEvent,aHandler,false);
    else
      aElement["on"+aEvent] = null;
  },
  //
  //  GetEvent,_IETweak
  //    Cross-browser issue.  We want to deal with a single event object and method of
  //    obtaining access to that object. For IE, the event is stored window.event and in 
  //    Mozilla, it is passed is as an argument.  Many of the properties have different 
  //    names (e.g., target(moz) & srcElement(ie)).
  //    If an event handler calls GetEvent, which returns a mozilla-compliant event object,
  //    internally, _IETweak is called for IE, which translates relevant IE-specific properties
  //    to mozilla-compliant properties
  GetEvent : function() {
    if ( window.event)
      return this._IETweak(window.event);       // Populate standard event properties for IE
    else
      return this.GetEvent.caller.arguments[0]; // Get argument off callers stack
  },
  _IETweak : function ( aEvent) {
    // Note: Opera goes down this path.  It supports both event conventions.
    //       It also throws an DOM exception if event is modified.
    //       It also doesnt support caller method in JS... !window.event.target handles this.
    if ( window.event && !window.event.target) {
      aEvent.target = aEvent.srcElement;
      aEvent.pageX = aEvent.clientX + document.body.scrollTop;
      aEvent.pageY = aEvent.clientY + document.body.ScrollLeft;
      aEvent.charCode = (aEvent.type == "keypress") ? aEvent.keyCode : 0;
      switch ( aEvent.type) {
        case "mouseover":
          aEvent.relatedTarget = aEvent.fromElement; break;
        case "mouseout":
          aEvent.relatedTarget = aEvent.toElement; break;
      }
    }
    return aEvent;
  },
  //
  // Timer Functions: SetTimeout,ClearTimeout,SetInterval,ClearInterval
  //    Using timers are an issue when using objects because the timeout handler is called
  //    in the context of the window object, therefore, "this" is the window object, not
  //    you class object instance as you would expect.  The solution is to put the event
  //    handler in an envelope that has access to the calling object instance.
  SetTimeout : function( aObj, aHandler, aDuration) {
  	return setTimeout( function(){Neaux.Event._Callback(aObj,aHandler);}, aDuration);
  },
  ClearTimeout : function( aTimerID) { 
    return clearTimeout(aTimerID); 
  },
  SetInterval : function( aObj, aHandler, aDuration) {
  	return setInterval( function(){Neaux.Event._Callback(aObj,aHandler);}, aDuration);
  },
  ClearInterval : function( aTimerID) { 
    return clearInterval(aTimerID); 
  },
  _Callback : function( aObj, aFn) { aFn.call(aObj); },
  ___end : null
}

// Slideshow API
Neaux.Api.DoSlideShow = function( aTarget) {
  var theArg = null;
  if (arguments.length > 1) {
    if ( arguments.length == 2)
      theArg = arguments[1];
    else {
      theArg = new Array();
      for( var i=1; i < arguments.length; ++i) {
        if ( arguments[i])
          theArg.push(arguments[i]);
      }          
    }
  }
  var theSlideShow = new Neaux.Slideshow();
  if ( aTarget) theSlideShow.SetTarget(aTarget);
  if ( theArg && theArg.length > 0) theSlideShow.Load( theArg);
  theSlideShow.SetTransition();
  theSlideShow.Start();
  
  Neaux.Api._ObjectRegistry.push(theSlideShow);
  return theSlideShow;
}
Neaux.Api.DoRollover = function( aTarget, aID) {
  var theSuffix = typeof(arguments[2]) == "function" ? arguments[3] : arguments[2] ;
  var theCallback = typeof(arguments[2]) == "function" ? arguments[2] : null;
  var theRollover = new Neaux.Rollover( aTarget, aID, theSuffix);  
  if (theCallback != null)
    theRollover.Callback = theCallback;
  Neaux.Api._ObjectRegistry.push(theRollover);
  return theRollover;
}


Neaux.ImageDescriptor = function( aImage) {
  this.Image = null;    // The image element object 
  if ( aImage) this.Load( aImage);
}

Neaux.ImageDescriptor.prototype.Load = function( aImage) {
  if ( typeof aImage == "string") {
    this.Image = new Image();
    this.Image.src = aImage;
  }
  else if ( typeof aImage == "object")
    this.Image = aImage;
  else
    this.Image = null;
}


// Gallery Object - Container for image descriptors
Neaux.Gallery = function() {
  this.Nodes = [];          // The image descriptors for the gallery
  this.IsOnDemand = false;  // By default, images load automatically
  this._Img = null;         // The current image node context

  // An argument list in the constructor is an implicit load operation,
  // so just pass on the arguments...
  if ( arguments.length > 0)
    this.Load.apply(this,arguments);
}

Neaux.Gallery.prototype.Node = function( aValue) {
  // Load Image for On-demand scenario
  return this.Nodes[aValue];
}

Neaux.Gallery.prototype.Load = function() {
  for (var i = 0; i < arguments.length; ++i)
    this.AddNode(arguments[i]);
}

Neaux.Gallery.prototype.AddNode = function( aNode) {
  if ( typeof aNode == "string") {
    // String can be FileName or Element Identifier
    if (aNode.indexOf(".") > 0)
      this.AddImage(aNode);
    else {
      var el = document.getElementById(aNode);
      if ( el)
        this.AddHTML(el);
    }
  }
  else if ( typeof aNode == "object") {
    if ( aNode instanceof Array) {
      for ( var i=0; i < aNode.length; ++i)
        this.AddNode(aNode[i]);
    }
    else
      throw Error("Gallery::AddNode - Object references not implemented");
      // Object can be an Array, An Object Literal, a Gallery, an Image, a Container Element 
  }
}

Neaux.Gallery.prototype.AddImage = function( aImage) {
  var theNode = new Neaux.ImageDescriptor( aImage);
  this.Nodes.push(theNode);
}

Neaux.Gallery.prototype.AddHTML = function( aElement) {
  var theLI = aElement.getElementsByTagName("li");
  var theImage,theAnchor;
  for ( var i=0; i < theLI.length; i++) { 
    theAnchor = theLI[i].getElementsByTagName("a");
    if ( theAnchor && theAnchor.length > 0 && theAnchor[0].href)
      this.AddImage(theAnchor[0].href);
  } 
}

// Navigation - Context methods
Neaux.Gallery.prototype.Reset = function() { 
  this._Img = null; 
}
Neaux.Gallery.prototype.Set = function( aValue) {
  if ( !aValue) aValue = this._Img;
  return ( aValue >= 0 || aValue < this.Nodes.length) ? this.Node(aValue) : null
}
Neaux.Gallery.prototype.First = function() {
  this._Img = 0;
  return this.Node(0);
}
Neaux.Gallery.prototype.Prev = function() {
  if (this._Img == null || this._Img == 0)
    this._Img = this.Nodes.length;
  return this.Node(--this._Img);
}
Neaux.Gallery.prototype.Next = function() {
  if (this._Img == null || ++this._Img == this.Nodes.length) 
    this._Img = 0;
  return this.Node(this._Img);
}
Neaux.Gallery.prototype.Last = function() {
  this._Img = this.Nodes.length - 1;
  return this.Node(this._Img);
}
Neaux.Gallery.prototype.Count = function() { 
  return this.Nodes.length; 
}
//Neaux.Gallery.prototype. = function() {}


// SlideShow Object - Rendering Engine For Gallery
Neaux.Slideshow = function( aTarget) {
  this.Target = aTarget ? this.SetTarget(aTarget) : null;
  this.Interval = 2000;                 // Auto-swap interval in milliseconds
  this._IsManualMode = false;           // Auto-swap if false
  this._TimerID = null;                 // TimerID for Auto-swap timer
  this._IsTransitionEnabled = false;    // Apply Image Transition between Swaps
  this._TranstionObj = null;            // The Image Transition object used

  // If a gallery of images are specified, load them.
  if ( arguments.length > 1) {
    var theArgs = new Array();
    for( var i=1; i < arguments.length; ++i) {
      alert(arguments[i]);
      theArgs.push(arguments[i]);
    }
    this.Load(theArgs);
  }
}
Neaux.Slideshow.prototype = new Neaux.Gallery();

Neaux.Slideshow.prototype.SetTarget = function( aTarget) {
  this.Target = aTarget ? typeof aTarget == "string" ? document.getElementById(aTarget) : aTarget : null;
}

Neaux.Slideshow.prototype.SetManual = function( aSet) {
  this._IsManualMode = (aSet == false) ? false : true; // if undefined set to true
}

Neaux.Slideshow.prototype.SetTransition = function( aSet) {
  if ( aSet == undefined) aSet = true;
  if ( aSet && this._TransitionObj == null) {
    this._TransitionObj = new Neaux.ImageTransition(this.Target);
    this._TransitionObj.PersistClone = true;
    this._TransitionObj.SetCallback( this._SwapComplete, this);
  }
  this._IsTransitionEnabled = aSet;
}

Neaux.Slideshow.prototype.Start = function() {
  this._Manual = false;
  this.ShowNext();
}
Neaux.Slideshow.prototype.Stop = function() {
  this._Manual = true;
  if (this._TimerID)
    Neaux.Event.ClearTimeout(this._TimerID);
}
Neaux.Slideshow.prototype.Show = function() {
  throw Error( "SlideShow::Show is not implemented");
}

Neaux.Slideshow.prototype.ShowFirst = function() { this.SwapTarget(this.First()); }
Neaux.Slideshow.prototype.ShowNext = function() { this.SwapTarget(this.Next()); }
Neaux.Slideshow.prototype.ShowPrev = function() { this.SwapTarget(this.Prev()); }
Neaux.Slideshow.prototype.ShowLast = function() { this.SwapTarget(this.Last()); }

Neaux.Slideshow.prototype.SwapTarget = function( aNode) {
  if ( this._TimerID) return; // To prevent multiple timers
  var isSameImage = this.Target.src == aNode.Image.src;
  if ( !isSameImage && this._IsTransitionEnabled) {
    if ( this._TransitionObj._TimerID) return; // To prevent multiple timers
    this._TransitionObj.Start( aNode.Image);
  }
  else {
    this.Target.src = aNode.Image.src;
    this._SwapComplete();
  }
}

Neaux.Slideshow.prototype._TimeoutHandler = function() {
  this._TimerID = null;
  this.ShowNext();
}

Neaux.Slideshow.prototype._SwapComplete = function() {
  if (!this._IsManualMode)
    this._TimerID = Neaux.Event.SetTimeout(this, this._TimeoutHandler, this.Interval);
}


Neaux.ImageTransition = function( aTarget) {
  this._Target = typeof(aTarget) == "string" ? document.getElementById(aTarget) : aTarget;

  this.Duration = 2000;         // Duration of Crossfade in milliseconds
  this.Tps = 20;                // Transitions per Second
  this.PersistClone = false;    // True to save cloned image after transition

  this._nTrans = null;          // Transition Iterator
  this._totTrans = null;        // Number of Transitions to be performed
  this._TimerID = null;         // Transition Timer ID
  this._Clone = null;           // The Cloned Copy of the Image Container
  this._Callback = null;        // Callback to be invoked after transition completes
  this._CallbackObj = null;     // Object Callback is applied to
}

Neaux.ImageTransition.prototype.SetCallback = function( aFn, aObj) {
  this._Callback = aFn;
  if ( aObj) this._CallbackObj = aObj;
}

Neaux.ImageTransition.prototype.Start = function( aNewImage) {
  var imageSrc = typeof aNewImage == "string" ? aNewImage : aNewImage.src;
  if ( Neaux.Agent.SetOpacity()) {
    this._CloneInit();                            // Initialize Cloned Image object to perform transition    this._nTrans = 0;
    this._nTrans = 0;                             // Initialize Iterator
    this._totTrans = this.Duration*this.Tps/1000  // Determine number of incremental transitions required
    
    this._Clone.src = imageSrc ;
    this._SetCurrentOpacity();
    this._Clone.style.visibility = "visible";

    // Queue a timer to call the _NextTrans method such that specified Transactions per section is performed.
    var theInterval = 1000/this.Tps;  // FORMERLY this.Duration/this._totTrans;
    this._TimerID = Neaux.Event.SetInterval(this, this._NextTransition, theInterval);
  }
  else {
    // If Opacity is not supported, then just swap the image
    this._Target.src = imageSrc;
    this.DoCallback();
  }
}

Neaux.ImageTransition.prototype._CloneInit = function() {
  if ( !this._Clone)
    this._CloneCreate();
  var rect = new Neaux.Agent.ElementRect(this._Target); 
  this._Clone.style.left = rect.x + "px";
  this._Clone.style.top = rect.y + "px";
  this._Clone.style.width = rect.width + "px";
  this._Clone.style.height = rect.height + "px";
  rect = null;
}
Neaux.ImageTransition.prototype._CloneCreate = function() {
  if ( this._Clone == null) {
    this._Clone = document.createElement("img");
    var theBody = document.getElementsByTagName("body")[0];
    theBody.appendChild(this._Clone);
  }
  this._Clone.style.visibility = "hidden";
  this._Clone.style.position = "absolute";
}

Neaux.ImageTransition.prototype._NextTransition = function() {
  ++this._nTrans;
  this._SetCurrentOpacity();
  if ( this._nTrans == this._totTrans) {
    Neaux.Event.ClearInterval(this._TimerID);
    this._TimerID = null;
    this._Target.src = this._Clone.src;
    Neaux.Agent.SetOpacity(this._Target, 100);
    //Note Getting flashing effect in Safari here...
    this._Clone.style.visibility = "hidden";
    if ( !this.PersistClone) {
      this._Clone.parentNode.removeChild(this._Clone);
      this._Clone = null;
    }
    if (this._Callback != null)
      this._Callback.call(this._CallbackObj);
  }
}

Neaux.ImageTransition.prototype._SetCurrentOpacity = function() {
  var theOpacity = 100*this._nTrans/this._totTrans;
  Neaux.Agent.SetOpacity(this._Clone, theOpacity, this._Target);
}

Neaux.ImageTransition.prototype.DoCallback = function() {
  if (this._Callback != null)
    this._Callback.call(this._CallbackObj);
}


Neaux.Rollover = function( aTarget, aID, aSuffix) {
  this._Target = document.getElementById(aTarget);
  var theContainer = document.getElementById(aID);
  var theImages = theContainer.getElementsByTagName("img");
  this.Suffix = aSuffix == undefined ? "_thumb" : aSuffix;
  this.Callback = null;
  this._Gallery = [];
  Neaux.Event.Register(aID, this);
  var j, k, theSrc, theStr, theImage;
  for ( var i=0; i < theImages.length; i++) {
    j = theImages[i].src.lastIndexOf(this.Suffix);
    if ( j > 0) { 
      // Preload the target images & store in associative array
      theSrc = theImages[i].src;
      k = theSrc.lastIndexOf('.');
      theStr = theSrc.substring(theSrc.lastIndexOf('/') + 1, k) ;
      theImage = new Image();
      theImage.src = theSrc.substring(0,j) + theSrc.substring(k,theSrc.length);
      this._Gallery[theStr] = theImage;
      Neaux.Event.Attach( theImages[i], "mouseover", this.SwapHandler);  
      Neaux.Event.Attach( theImages[i], "mouseout", this.SwapHandler);  
    }
  }
}
Neaux.Rollover.prototype.SwapHandler = function() {
  var theEvent = Neaux.Event.GetEvent();
  var thePtr = theEvent.target;
  while(!thePtr.id)
    thePtr = thePtr.parentNode;
  var _this = Neaux.Event.Registry[thePtr.id];
  _this.Swap( theEvent.target, theEvent);
}
Neaux.Rollover.prototype.Swap = function( aImage, aEvent) {
  if ( aEvent.type == "mouseover") {
    var theStr = aImage.src.substring(aImage.src.lastIndexOf('/') + 1, aImage.src.lastIndexOf('.')) ;
    this._Target.src = this._Gallery[theStr].src;
  }
  if ( this.Callback)
    this.Callback(aImage,aEvent.type,aEvent);
}
