/**
 * @name jTicker
 * @type jQuery
 * 
 * Usage: tickerObject = jQuery("#tickerID").jTicker({*optional settings*});
 * 
*/


// Create a block to maintain closure
(function($){

  // Add the jTicker function to the jQuery ($) object
  // This allows the function to be called from outside the closure block (ie, on the page)
  $.fn.jTicker = function(Settings) {
    // **Important**
    // Instantiate and return the $.jTicker object (function)
    // This allows a ticker to be contained in a variable as an object, meaning:
    //    Settings are now public properties of the object, allowing them to be changed on the fly
    //    Subfunctions are public methods of the object, allowing them to be called for *each object*
    return new $.jTicker(this, Settings);
  }
  
  // Most object functionality is contained in this object function (returned above) to allow these
  // properties to be accessed publicly
  $.jTicker = function(tickerElement, Settings) {
    // Anchor the ticker *self* to a variable, allowing it to be called from within subfunctions
    var tickerObj = this;
    // Merge the provided Settings with default Settings.  Now the Settings properties are
    // attached to the ticker instance, allowing multiple tickers with different settings
    tickerObj.Settings = $.extend({ // Default settings
      stepTime : 100, // Ticker refresh rate
      loopDelay : 2000, // Delay between ticker messages
      placeHolder1 : "_", // Placeholder, the animated typing cursor
      placeHolder2 : "" // Alternate placeholder, causes animated flashing
    },Settings); // Optional settings
    // This is used to determine if the ticker should continue to another message after printing
    tickerObj.animating = 0;
    tickerObj.continuation = 0;
    // These are nodes containers for the current ticker item and it's virtual copy, used to keep from
    // truncating node contents when skipping around.
    tickerObj.currentItem = null;
    tickerObj.currentClone = null;
    
    // Start the ticker into continuous animation, optionally skipping to a ticker item by index (position).
    tickerObj.start = function(position) {
      // Keep looping when finished with a ticker item
      tickerObj.animating = 1;
      // Send the ticker on it's way
      tickerObj.goto(position);
    };
    
    // Precursor to animation, this tells the object where to go,
    // resets the ticker items, hides them, and starts the animation process
    tickerObj.goto = function(position) {
      // Exit the function and return failure if there are no items in the ticker
      if (!jQuery(">:nth-child(" + position + ")", tickerElement).get(0)) return false;
      // Reset currentLength, which iterates the string by one character during the step() method
      tickerObj.currentLength = 0;
      // The position is optional.  If nothing is provided, default to the first item
      var position = position ? position : 1;
      // Hide all the elements.  This way only one is displayed at a time
      jQuery(">*", tickerElement).hide();
      // If the ticker was manually jumped, reset the contents using the virtual backup
      if (tickerObj.currentItem) jQuery(tickerObj.currentItem).text(tickerObj.currentClone.firstChild.nodeValue);
      // Select and store the current ticker item into the currentItem property for later reference
      tickerObj.currentItem = jQuery(">:nth-child(" + position + ")", tickerElement).get(0);
      // Store a virtual copy of the current item to be used as a backup
      tickerObj.currentClone = jQuery(tickerObj.currentItem).clone().get(0);
      // Empty the original copy and display the empty node.  This is the element to be "typed" into
      jQuery(tickerObj.currentItem).empty().show();
      // Start the function which loops into animation
      tickerObj.animate();
      // Return something, in this case the object
      return true;
    };
    
    // This animation function is used to store the animation loop and step through frames
    tickerObj.animate = function() {
      // Create the loop to animate the ticker.  We store the loop to a handle so it can be stopped later
      tickerObj.currentLoop = setTimeout(function(){tickerObj.animate()}, tickerObj.Settings.stepTime);
      // Iterate through one frame of the animation.
      tickerObj.step();
    };
    
    // This is the function which handles each frame of animation individually
    tickerObj.step = function() {
      // Set and increment a variable to determine the length
      // Without this, the placeholder animation would throw off the length
      var currentLength = tickerObj.currentLength;
      tickerObj.currentLength++;
      // If the item isn't completely "typed" add to it
      if (tickerObj.currentClone.firstChild.nodeType == 3 && tickerObj.currentClone.firstChild.nodeValue.length >= currentLength) {
        var placeHolder = tickerObj.Settings.placeHolder1;
        if (currentLength % 2 == 0) {
          placeHolder = tickerObj.Settings.placeHolder2;
        }
        jQuery(tickerObj.currentItem).text(tickerObj.currentClone.firstChild.nodeValue.substring(0, currentLength) + placeHolder);
      } 
      // If the item is complete and the ticker is set to continuously animate, go to the next item
      else if (tickerObj.currentClone.firstChild.nodeValue.length <= currentLength && tickerObj.animating == 1) {
        jQuery(tickerObj.currentItem).text(tickerObj.currentClone.firstChild.nodeValue);
        var currentIndex = jQuery(":visible", tickerElement).get(0).nodeIndex;
        next = 1;
        if (jQuery("> *", tickerElement).length > currentIndex) {
          var next = currentIndex + 1;
        } else if (jQuery("> *", tickerElement).length < currentIndex) {
          tickerObj.stop();
          return false;
        }
        tickerObj.stop();
        tickerObj.currentLoop = setTimeout(function(){tickerObj.stop();tickerObj.start(next);}, tickerObj.Settings.loopDelay);
      } 
      // If the item is complete and the ticker isn't continuous, stop looping
      else {
        tickerObj.stop();
      }
    }
    
    // This function progresses to the next ticker item
    tickerObj.next = function() {
      tickerObj.sequence(1, (jQuery("> *", tickerElement).length > tickerObj.currentIndex()), 1);      
    };
    
    // This function regresses to the previous ticker item
    tickerObj.previous = function() {
      tickerObj.sequence(jQuery("> *", tickerElement).length, (tickerObj.currentIndex() > 1), -1);
    };
    
    // This is used by previous() and next() to move in either direction
    tickerObj.sequence = function(base, conditional, direction) {
      tickerObj.stop();
      // Reset the ticker item text to remove the placeHolder
      jQuery(tickerObj.currentItem).text(tickerObj.currentClone.firstChild.nodeValue);
      var currentIndex = tickerObj.currentIndex();
      // Default to the first incase the passed condition isn't met
      var next = base;
      // Act using the passed in condition
      if (conditional) {
        // Override the default based on the given direction (1 || -1)
        var next = tickerObj.currentIndex() + direction;
      }
      // If it's set to animate, continue using start, else continue using goto
      if (tickerObj.continuation) {
        tickerObj.currentLoop = setTimeout(function(){tickerObj.start();tickerObj.start(next);}, 0);
      } else {
        tickerObj.currentLoop = setTimeout(function(){tickerObj.start();tickerObj.goto(next);}, 0);
      }
    };
    
    // This is the function used to stop the animation
    tickerObj.stop = function() {
      // Clear the continuous animation state
      tickerObj.animating = 0;
      // Clear the animation loop
      clearTimeout(tickerObj.currentLoop);
    };
    
    // Grab the index of the current active ticker element
    tickerObj.currentIndex = function() {
      var currentIndex = jQuery(":visible", tickerElement).get(0).nodeIndex;
      return currentIndex;
    };
    
    // Start the ticker when it's created
    tickerObj.start();
    
    // Return success
    return true;
  }

})(jQuery)