フェードイン・アウトしながら画像を切り替える。しかもオブジェクト指向?

//@cc_on
function Decorator (alpha) {
  //@ this.filter = 'Alpha(opacity=' + alpha + ')';
  this.opacity  = alpha / 100 + '';
}

function Starter (callbackfn) {
  this.timerID = (function (o) {
    return setInterval(function () { return callbackfn.call(o); }, o.interval);
  })(this);
}

function Waiter (callbackfn) {
  this.timerID = (function (o) {
    return setTimeout(function () { return callbackfn.call(o); }, o.span);
  })(this);
}

function Stopper () {
  clearInterval(this.timerID);
}

function Timer () {
  this.timerID = null;
}

Timer.prototype.timerStart = function (listener) {
  return Starter.call(this, listener);
};

Timer.prototype.timerWait = function (listener) {
  return Waiter.call(this, listener);
};

Timer.prototype.timerStop = function () {
  return Stopper.call(this);
};

//___________________

function ImageChanger () {
  this.Initializer.apply(this, arguments);
}

ImageChanger.prototype = new Timer;
ImageChanger.prototype.constructor = ImageChanger;

ImageChanger.prototype.Initializer = function (tId, span, step, interval) {
  this.target = document.getElementById(tId);
  this.span = span;
  this.step = step;
  this.interval = interval;
  this.ImgList = [];
  this.counter = 0;
  this.mode = 3;
}

ImageChanger.prototype.addImage = function () {
  var c = 0;
  var o;
  var img;

  while (o = arguments[c++]) {
    img = new Image;
    img.src = o;
    this.ImgList.push(img);
  }
};

ImageChanger.prototype.start = function (mode, no) {
  if (undefined !== no) {
    this.counter = no;
  }
  if (undefined !== mode) this.mode = mode;
  switch (this.mode) {
    case 1:
      this.opacity = 0;
      Decorator.call(this.target.style, this.opacity);
      this.timerStart(this.fadeIn);
      break;
    case 2:
      this.timerWait(this.nextMode);
      break;
    case 3:
      this.opacity = 100;
      Decorator.call(this.target.style, this.opacity);
      this.timerStart(this.fadeOut);
      break;
  };
}

ImageChanger.prototype.fadeIn = function () {
  this.opacity += this.step;
  if (100 < this.opacity) {
    this.opacity = 100;
  }
  Decorator.call(this.target.style, this.opacity);
  if (100 === this.opacity) {
    this.timerStop();
    this.nextMode();
  }
}

ImageChanger.prototype.fadeOut = function () {
  this.opacity -= this.step;
  if (0 > this.opacity) {
    this.opacity = 0;
  }
  Decorator.call(this.target.style, this.opacity);
  if (0 === this.opacity) {
    this.timerStop();
    this.nextMode();
  }
}

ImageChanger.prototype.stop = function () {
  return this.mode = 0;
};

ImageChanger.prototype.nextMode = function () {
  if (0 !== this.mode) {
    if (++this.mode == 4) {
      this.mode = 1;
      this.counter = (this.counter + 1) % this.ImgList.length;
      this.target.src = this.ImgList[this.counter].src;
    }
    this.start();
  }
}

// 使い方、対象のエレメント、表示時間、透明度の変化量%、インターバルタイム
changer = new ImageChanger('elementのid', 3000, 5, 40);

var c=0;

//画像を切り替えたいだけ追加する
while (c++<11) changer.addImage('./img/photo' + c + '.jpg');

//フェードアウトから始まる
changer.start(3);