  /**
   * extension of prototype Blender library with some useful methods
   *
   * @copyright Profi Webmedia
   * @author    Thomas Juhnke <tommy(at)profi(dot)it
   * @since     12.11.2008 10:00:41
   */

  var Blender = Class.create(Blender, {
    loadExtensions: function(options)
    {
      // per default, the extensions are disabled
      this.extensions = Object.extend({
        step: {
          enabled: false,
          prevAction: '#menu-image-navigation a[rel=prev]',
          nextAction: '#menu-image-navigation a[rel=next]'
        },
        jumpTo: {
          enabled: false,
          jumpAction: '#menu-image-select li a'
        },
        play: {
          enabled: false,
          playAction: '#menu-image-select li.play-button'
        }
      }, options || {});

      if (this.extensions.step.enabled)
      {
        this.stepPrevObserver = this.stepPrev.bind(this);
        this.stepNextObserver = this.stepNext.bind(this);

        $$(this.extensions.step.prevAction).first().observe('click', this.stepPrevObserver);
        $$(this.extensions.step.nextAction).first().observe('click', this.stepNextObserver);
      }

      if (this.extensions.jumpTo.enabled)
      {
        this.jumpToObserver = this.jumpToIndex.bind(this);

        $$(this.extensions.jumpTo.jumpAction).invoke('observe', 'click', this.jumpToObserver);
      }

      if (this.extensions.play.enabled)
      {
        this.playButtonObserver = this.restartAnimation.bind(this)
        $$(this.extensions.play.playAction).invoke('observe', 'click', this.playButtonObserver);
      }
    },

    /**
     * bend some observers (especially for prev/next navigation)
     *
     */
    bendObservers: function()
    {
      // override this.loadedObserver to a new event listener which doesn't re-set the timeout
      this.loadedObserver = this.loadedAndHold.bind(this);
    },

    /**
     * allows forward navigation in the image list
     *
     */
    stepNext: function(event)
    {
      Event.stop(event);

      // this.timeout will only exist if the first image was blended...
      if (Object.isUndefined(this.timeout))
        return;

      this.bendObservers();
      this.setStepActionLoadingState();

      //this.stopped = true;
      try { clearTimeout(this.timeout); } catch(ex) { }
      try { Effect.Queues.get(this.options.id).each(function(effect) { effect.cancel() }) } catch(ex) { }

      if(this.oldImg) {
        this.container.removeChild(this.oldImg);
      }
      this.oldImg = this.img;
      if(this.stopped || this.list.length == 0) {
        return;
      }
      ++this.index;
      if(this.index >= this.list.length) {
        this.index = 0;
      }
      this.img = Builder.node("img", this.options.attributes);
      Event.observe(this.img, "load", this.loadedObserver);
      this.img.src = this.list[this.index];
    },

    /**
     * allows backward navigation in the image list
     *
     */
    stepPrev: function(event)
    {
      Event.stop(event);

      if (Object.isUndefined(this.timeout))
        return;

      this.bendObservers();
      this.setStepActionLoadingState();

      //this.stopped = true;
      try { clearTimeout(this.timeout); } catch(ex) { }
      try { Effect.Queues.get(this.options.id).each(function(effect) { effect.cancel() }) } catch(ex) { }

      if(this.oldImg) {
        this.container.removeChild(this.oldImg);
      }
      this.oldImg = this.img;
      if(this.stopped || this.list.length == 0) {
        return;
      }
      --this.index;
      if(this.index < 0) {
        this.index = this.list.length - 1;
      }
      this.img = Builder.node("img", this.options.attributes);
      Event.observe(this.img, "load", this.loadedObserver);
      this.img.src = this.list[this.index];
    },

    /**
     * overridden loaded() method: will not set the layout
     * this method is bend to the this.loadedObserver event listener
     *
     */
    loadedAndHold: function()
    {
      this.setStepActionLoadingState();
      Event.stopObserving(this.img, "load", this.loadedObserver);
      if(this.options.autoSize) {
        this.resize(this.img);
      }
      this.img.setOpacity(0);
      this.container.appendChild(this.img);
      this.img.setStyle({ position: "absolute", top: this.container.getStyle("padding-top"), left: this.container.getStyle("padding-left") });
      new Effect.Opacity(this.oldImg, { duration: this.options.fadeDuration, from: 1.0, to: 0.0, queue: { scope: this.options.id } });
      new Effect.Opacity(this.img, { duration: this.options.fadeDuration, from: 0.0, to: 1.0, queue: { scope: this.options.id } });
      //this.timeout = setTimeout(this.nextObserver, (this.options.fadeDuration + this.options.displayDuration) * 1000);
    },

    /**
     * this method will set some loading indicator to prev/next action elements
     * this maybe has to be adjusted for some special layouts
     *
     * @see voellanerhof.com for a sample
     */
    setStepActionLoadingState: function()
    {
      // just do this stuff if step extension is enabled...
      if (this.extensions.step.enabled)
      {
        // try to stop observing of action elements
        Event.stopObserving($$(this.extensions.step.prevAction).first(), 'click', this.stepPrevObserver);
        Event.stopObserving($$(this.extensions.step.nextAction).first(), 'click', this.stepNextObserver);

        // toggle the classname
        $$(this.extensions.step.prevAction).first().toggleClassName('loading');
        $$(this.extensions.step.nextAction).first().toggleClassName('loading');

        // check if `loading` class exists, if not...
        if (!$$(this.extensions.step.prevAction).first().hasClassName('loading') &&
            !$$(this.extensions.step.nextAction).first().hasClassName('loading'))
        {
          // re-bind event listeners
          $$(this.extensions.step.prevAction).first().observe('click', this.stepPrevObserver);
          $$(this.extensions.step.nextAction).first().observe('click', this.stepNextObserver);
        }
      }
    },

    /**
     * method allows jumping to a specific image in the image array
     *
     */
    jumpToIndex: function(event)
    {
      Event.stop(event);

      /* remove active class names */
      $$(this.extensions.jumpTo.jumpAction).invoke('removeClassName', 'active');
      /* add the active class name */
      Event.element(event).addClassName('active');

      var index = $$(this.extensions.jumpTo.jumpAction).indexOf(Event.element(event));

      // this.timeout will only exist if the first image was blended...
      if (Object.isUndefined(this.timeout))
        return;

      this.bendObservers();
      this.setStepActionLoadingState();

      //this.stopped = true;
      try { clearTimeout(this.timeout); } catch(ex) { }
      try { Effect.Queues.get(this.options.id).each(function(effect) { effect.cancel() }) } catch(ex) { }

      if(this.oldImg) {
        this.container.removeChild(this.oldImg);
      }
      this.oldImg = this.img;
      if(this.stopped || this.list.length == 0) {
        return;
      }
      // set to fetched index
      this.index = index;
      if(this.index >= this.list.length) {
        this.index = 0;
      }
      this.img = Builder.node("img", this.options.attributes);
      Event.observe(this.img, "load", this.loadedObserver);
      this.img.src = this.list[this.index];
    },

  	/**
  	 * Loads the next image in list
  	 * @private
  	 */
  	next: function() {
  		if(this.oldImg) {
  			this.container.removeChild(this.oldImg);
  		}
  		this.oldImg = this.img;
  		if(this.stopped || this.list.length == 0) {
  			return;
  		}
  		++this.index;
  		if(this.index >= this.list.length) {
  			this.index = 0;
  		}
  		this.img = Builder.node("img", this.options.attributes);
  		Event.observe(this.img, "load", this.loadedObserver);
  		this.img.src = this.list[this.index];

      if (this.extensions.jumpTo.enabled)
      {
        var activeJumpToItem = $$(this.extensions.jumpTo.jumpAction).find(function(element, index) {
          return index == this.index
        }, this);
        if ('undefined' != typeof activeJumpToItem)
        {
          $$(this.extensions.jumpTo.jumpAction).invoke('removeClassName', 'active');
          $(activeJumpToItem).addClassName('active');
        }
      }
  	},

    restartAnimation: function(event) {
      this.loadedObserver = this.loaded.bind(this);
      this.next();
    }
  });

