(function()
{
  require("Transition.js");
  
  
  /* ************************************************************************ *
   * Constants
   * ************************************************************************ */
   
  var ID_CONTAINER             = "visual_messages",
      ID_RECOMMENDS            = "our_recommends",
      ID_CAMP_BANNERS          = "ongoing_campaigns",
      ID_CAMP_TOGGLER          = "campaign_selector",
      CLASS_NAME_RCMD_ITEM     = "recommended-products",
      CLASS_NAME_RCMD_TRIGGER  = "-heading",
      CLASS_NAME_RCMD_VISUAL   = "-figure",
      CLASS_NAME_RCMD_LINKS    = "-content",
      CLASS_NAME_CAMP_SELECTED = "-selected",
      ROTATION_INTEVAL_MSEC    = 5000,
      TRANSITION_DURATION      = 0.34,
      TRANSITION_FUNCTION,
      CONTENT_BODY_WIDTH,
			CLIENT_VIEW_NAME =
				(/\biP(?:hone|ad|od)\b/.test(navigator.platform) || /\bAndroid\b/.test(navigator.userAgent))
					? "body" : "documentElement";
  
  
  /* ************************************************************************ *
   * Fields
   * ************************************************************************ */
  
  var container,
      recommends,
      recommendsTransitionId,
      allRecommendItems,
      campBanners,
      campBannersTransitionId,
      allCampBannersItems,
      allCampBannersToggler,
      currentCampBannerIndex,
      rotationIntervalId,
      hasFocus;
  
  
  /* ************************************************************************ *
   * Event Handlers and Callbacks
   * ************************************************************************ */
  
  /**
   * リフォーム メニューから枠外へのマウスアウトを捕捉します。
   * リフォーム メニューを非表示にし、ローテーション バナーを表示します。
   * @param {MouseEvent} event 
   */
  function recommends_mouseout(event)
  {
    var mouseX,
        mouseY,
        mouseOnAreaX,
        mouseOnAreaY;
    
    with (event.currentTarget.getBoundingClientRect())
    {
      mouseX = event.clientX - left;
      mouseY = event.clientY - top;
    }
    
    mouseOnAreaX =
      (!isNaN(mouseX) && mouseX > 0 && mouseX < event.currentTarget.offsetWidth);
    
    mouseOnAreaY =
      (!isNaN(mouseY) && mouseY > 0 && mouseY < event.currentTarget.offsetHeight);
    
    if (!mouseOnAreaX || !mouseOnAreaY)
    {
      startRotation();
      showCampBanners();
      setRecommendsTriggerStateToInitial();
    }
  }
  
  /**
   * リフォーム メニュー一覧の各項目へのマウスオーバーを捕捉します。
   * 対象となった項目に対応するリフォーム メニューを表示し、ローテーション バナーを非表示にします。
   * @param {MouseEvent} event 
   */
  function trigger_mouseover(event)
  {
    var i,
        self,
        parent,
        containingBox,
        currentOpacities,
        opacityValue,
        trigger,
        anchor,
        visual,
        links,
        triggerImage;
    
    hideCampBanners();
    
    // イベントをトリガしたのが、メニュー項目のコンテナなのか
    // 項目内のリンクなのかを判別する
    if (StyleManager.hasClass(event.currentTarget, CLASS_NAME_RCMD_TRIGGER))
      self = event.currentTarget;
    else
    {
      parent = event.currentTarget.parentNode;
      
      while (null != parent)
      {
        if (StyleManager.hasClass(parent, CLASS_NAME_RCMD_TRIGGER))
        {
          self = parent;
          break;
        }
        parent = parent.parentNode;
      }
    }
    
    // メニュー項目の画像のロールオーバー機能を一時的に無効にする
    triggerImage = self.getElementsByTagName("IMG").item(0);
    
    if (null != triggerImage)
      rollover.pauseRollover(triggerImage);
    
    // このメニュー項目に対応するリフォーム メニュー枠を取得する
    parent = self.parentNode;
    
    while (null != parent)
    {
      if (StyleManager.hasClass(parent, CLASS_NAME_RCMD_ITEM))
      {
        containingBox = parent;
        break;
      }
      parent = parent.parentNode;
    }
    
    // すべてのリフォーム メニューを一旦、初期化する
    // また、現時点での透明度を取得する
    currentOpacities = new Array();
    
    for (i = 0; i < allRecommendItems.length; i++)
    {
      visual = DOMOperator.getElementsByClassName(
        allRecommendItems[i], CLASS_NAME_RCMD_VISUAL, false);
      
      links = DOMOperator.getElementsByClassName(
        allRecommendItems[i], CLASS_NAME_RCMD_LINKS, false);
      
      visual.style.display = "block";
      links.style.display = "block";
      
      if (allRecommendItems[i] === containingBox)
      {
        if (1 == StyleManager.getOpacity(visual))
        {
          disableRecommendsContent();
          visual.style.display = "block";
          links.style.display = "block";
          return;
        }
        
        StyleManager.setOpacity(visual, 0);
        StyleManager.setOpacity(links,  0);
        currentOpacities.push(0);
        continue;
      }
      
      currentOpacities.push(StyleManager.getOpacity(visual));
      
      trigger = DOMOperator.getElementsByClassName(
        allRecommendItems[i], CLASS_NAME_RCMD_TRIGGER, false);
      
      anchor = trigger.getElementsByTagName("A").item(0);
      triggerImage = trigger.getElementsByTagName("IMG").item(0)
      
      if (anchor != null)
        EventManager.addEventListener(anchor, "focus", arguments.callee);
      
      if (null != triggerImage)
      {
        rollover.resumeRollover(triggerImage);
        rollover.setRolloverState(triggerImage, false);
      }
      
      EventManager.addEventListener(trigger, "mouseover", arguments.callee);
    }
    
    // このメニューが表示されている間は、このイベントが再トリガーされないようにする
    EventManager.removeEventListener(
      event.currentTarget, event.type, arguments.callee);
    
    // リフォーム メニューを表示して終了する
    Transition.terminate(recommendsTransitionId)
    
    recommendsTransitionId = Transition.start(
      TRANSITION_DURATION, TRANSITION_FUNCTION, false,
      function(position, loopCount)
      {
        for (var i = 0; i < allRecommendItems.length; i++)
        {
          visual = DOMOperator.getElementsByClassName(
            allRecommendItems[i], CLASS_NAME_RCMD_VISUAL, false);
          
          links = DOMOperator.getElementsByClassName(
            allRecommendItems[i], CLASS_NAME_RCMD_LINKS, false);
          
          if (allRecommendItems[i] === containingBox)
          {
            StyleManager.setOpacity(visual, position);
            StyleManager.setOpacity(links,  position);
          }
          else
          {
            if (position < 1)
            {
              opacityValue = currentOpacities[i] - currentOpacities[i] * position;
              
              StyleManager.setOpacity(visual, opacityValue);
              StyleManager.setOpacity(links,  opacityValue);
            }
            else
            {
              visual.style.display = "none";
              links.style.display  = "none";
            }
          }
        }
      }
    );
  }
  
  /**
   * ローテーション バナー切り替えボタンへのフォーカス設定を捕捉します。
   * 対象となったボタンに対応するバナー画像を表示します。
   * @param {MouseEvent} event 
   */
  function campBannerToggler_focus(event)
  {
    var index = DOMOperator.getBirthOrder(
      event.currentTarget.parentNode, true, true);
    
    activateCampBannerToggler(index);
		
    if (index == currentCampBannerIndex)
    {
      showCampBanners();
      return;
    }
    
    activateCampBanner(index);
  }
  
  /**
   * ローテーション バナー切り替えボタンへのマウスオーバーを捕捉します。
   * 対象となったボタンに対応するバナー画像を表示します。
   * @param {MouseEvent} event 
   */
  function campBannerToggler_mouseover(event)
  {
    var index;
    
    index = DOMOperator.getBirthOrder(
      event.currentTarget.parentNode, true, true);
    
    activateCampBannerToggler(index);
		
    if (index == currentCampBannerIndex)
      return;
    
    activateCampBanner(index);
  }
  
  /**
   * ローテーション バナーの自動切り替えを停止させる、汎用イベントリスナです。
   * @param {Event} event 
   */
  function _event_stopRotation(event)
  {
    stopRotation();
  }
  
  /**
   * ローテーション バナーの自動切り替えを開始する、汎用イベントリスナです。
   * @param {Event} event 
   */
  function _event_startRotation(event)
  {
    startRotation();
  }
  
  /**
   * ヴィジュアル メッセージの枠外のリンクへのフォーカス設定を捕捉します。
   * ヴィジュアル メッセージ枠内からのフォーカス移動であれば
   * 停止しているローテーション バナーの自動切り替えを再開します。
   * @param {FocusEvent} event 
   */
  function outsideAnchor_focus(event)
  {
    if (hasFocus)
    {
      hasFocus = false;
      startRotation();
      setRecommendsTriggerStateToInitial();
    }
  }
  
  /**
   * ヴィジュアル メッセージの枠内のリンクへのフォーカス設定を捕捉します。
   * ローテーション バナーの自動切り替えを停止します。
   * @param {FocusEvent} event 
   */
  function insideAnchor_focus(event)
  {
    var parent;
    
    hasFocus = true;
    stopRotation();
    parent = event.currentTarget.parentNode;
    
    while (null != parent)
    {
      if (StyleManager.hasClass(parent, CLASS_NAME_RCMD_ITEM))
      {
        hideCampBanners();
        break;
      }
      parent = parent.parentNode;
    }
  }
  
  /**
   * ブラウザ ウィンドウのサイズ変更を捕捉します。
   * ヴィジュアル メッセージの枠幅がサイトのコンテンツ幅よりも大きく、
   * ウィンドウの幅がコンテンツ幅より大きくても
   * 横スクロールバーが表示される場合があるため、それを回避します。
   * @param {UIEvent} event 
   */
  function window_resize(event)
  {
    document[CLIENT_VIEW_NAME].style.overflowX =
      (document[CLIENT_VIEW_NAME].clientWidth <= (container.offsetWidth + container.getBoundingClientRect().left)
        && document[CLIENT_VIEW_NAME].clientWidth > CONTENT_BODY_WIDTH)
					? "hidden"
					: "auto";
    
    container.parentNode.style.overflow =
      (document[CLIENT_VIEW_NAME].clientWidth < CONTENT_BODY_WIDTH)
        ? "hidden"
        : "visible";
  }
  
  /**
   * window.setTimeout() のコールバック関数です。
   * ローテーション バナーの自動切り替えを実行します。
   */
  function window_timeout()
  {
    activateCampBanner(currentCampBannerIndex + 1, startRotation);
  }
  
  /**
   * ページが読み込まれた後に呼び出されるコールバック関数です。
   */
  function contentLoaded()
  {
    var i,
        items,
        ch,
        parent;
    
  ROOT:
    for (i = 0, items = document.body.getElementsByTagName("A");
         i < items.length;
         i++)
    {
      ch = items[i];
      parent = ch.parentNode;
      
      while (null != parent)
      {
        if (parent === container)
        {
          EventManager.addEventListener(ch, "focus", insideAnchor_focus);
          continue ROOT;
        }
        parent = parent.parentNode;
      }
      
      EventManager.addEventListener(ch, "focus", outsideAnchor_focus);
    }
    
    CONTENT_BODY_WIDTH = document.getElementById("struct_content_body").offsetWidth;
    
    EventManager.addEventListener(window, "resize", window_resize);
    
    window_resize();
    
    startRotation();
  }
  
  
  /* ************************************************************************ *
   * Functions
   * ************************************************************************ */
  
  /**
   * リフォーム メニュー内の各コンテンツを非表示にします。
   */
  function disableRecommendsContent()
  {
    var i, visual, links;
    
    for (i = 0; i < allRecommendItems.length; i++)
    {
      visual = DOMOperator.getElementsByClassName(
        allRecommendItems[i], CLASS_NAME_RCMD_VISUAL, false);
      
      links = DOMOperator.getElementsByClassName(
        allRecommendItems[i], CLASS_NAME_RCMD_LINKS, false);
      
      visual.style.display = "none";
      links.style.display = "none";
    }
  }
  
  /**
   * リフォーム メニューの一覧のロールオーバー状態を初期状態に戻します。
   */
  function setRecommendsTriggerStateToInitial()
  {
    var i,
        anchor,
        trigger,
        triggerImage;
    
    for (i = 0; i < allRecommendItems.length; i++)
    {
      trigger = DOMOperator.getElementsByClassName(
        allRecommendItems[i], CLASS_NAME_RCMD_TRIGGER, false);
      
      anchor = trigger.getElementsByTagName("A").item(0);
      triggerImage = trigger.getElementsByTagName("IMG").item(0)
      
      if (null != triggerImage)
      {
        rollover.resumeRollover(triggerImage);
        rollover.setRolloverState(triggerImage, false);
      }
      
      EventManager.addEventListener(trigger, "mouseover", trigger_mouseover);
      EventManager.addEventListener(anchor, "focus", trigger_mouseover);
    }
  
  }
  
  /**
   * ローテーション バナーを非表示にし、リフォーム メニューを表示します。
   */
  function hideCampBanners()
  {
    var i,
        currentOpacity;
    
    if (campBanners.style.display == "none")
      return;
    
    for (i = 0; i < allCampBannersItems.length; i++)
    {
      if (i != currentCampBannerIndex)
      {
        allCampBannersItems[i].style.display = "none";
        StyleManager.setOpacity(allCampBannersItems[i], 0);
      }
      else
        allCampBannersItems[i].style.display = "block";
    }
    
    currentOpacity = StyleManager.getOpacity(
      allCampBannersItems[currentCampBannerIndex]);
    
    Transition.terminate(campBannersTransitionId);
    
    campBannersTransitionId = Transition.start(
      TRANSITION_DURATION, TRANSITION_FUNCTION, false,
      function(position, loopCount)
      {
        StyleManager.setOpacity(
          allCampBannersItems[currentCampBannerIndex],
          currentOpacity - position * currentOpacity);
        
        if (position == 1)
          campBanners.style.display = "none";
      });
  }
  
  /**
   * ローテーション バナーを表示し、リフォーム メニューを非表示にします。
   */
  function showCampBanners()
  {
    var i,
        currentOpacity,
        opacityOffset;
    
    if (campBanners.style.display == "block" && !Transition.running(campBannersTransitionId))
      return;
    
    for (i = 0; i < allCampBannersItems.length; i++)
    {
      if (i != currentCampBannerIndex)
      {
        allCampBannersItems[i].style.display = "none";
        StyleManager.setOpacity(allCampBannersItems[i], 0);
      }
      else
        allCampBannersItems[i].style.display = "block";
    }
    
    campBanners.style.display = "block";
    
    currentOpacity = StyleManager.getOpacity(
      allCampBannersItems[currentCampBannerIndex]);
    
    opacityOffset  = 1 - currentOpacity;
    
    Transition.terminate(campBannersTransitionId);
    
    campBannersTransitionId = Transition.start(
      TRANSITION_DURATION, TRANSITION_FUNCTION, false,
      function(position, loopCount)
      {
        StyleManager.setOpacity(
          allCampBannersItems[currentCampBannerIndex],
          currentOpacity + position * opacityOffset);
        
        if (position == 1)
          disableRecommendsContent();
      });
    
    setRecommendsTriggerStateToInitial();
  }
  
  /**
   * ローテーション バナー内の指定されたバナー画像を表示し、その他のバナー画像を非表示にします。
   * @param {number} index 
   */
  function activateCampBanner(index, callback)
  {
    var i,
        prevIndex,
        currentOpacityPrev,
        currentOpacityNext,
        opacityOffset;
    
    stopRotation();
    
    if (isNaN(index) || index < 0 || index >= allCampBannersItems.length)
      index = 0;
    
    if (index == currentCampBannerIndex)
    {
      if (typeof callback == "function")
        callback.call();
      return;
    }
    
    prevIndex = currentCampBannerIndex;
    
    campBanners.style.display = "block";
    
    for (i = 0; i < allCampBannersItems.length; i++)
    {
      if (i != prevIndex && i != index)
      {
        allCampBannersItems[i].style.display = "none";
        StyleManager.setOpacity(allCampBannersItems[i], 0);
      }
      else
        allCampBannersItems[i].style.display = "block";
    }
    
    currentOpacityPrev = StyleManager.getOpacity(
      allCampBannersItems[prevIndex]);
    
    currentOpacityNext = StyleManager.getOpacity(
      allCampBannersItems[index]);
    
    opacityOffset = 1 - currentOpacityNext;
    
    Transition.terminate(campBannersTransitionId);
    
    campBannersTransitionId = Transition.start(
      TRANSITION_DURATION, TRANSITION_FUNCTION, false,
      function(position, loopCount)
      {
        StyleManager.setOpacity(
          allCampBannersItems[index],
          currentOpacityNext + position * opacityOffset);
        
        StyleManager.setOpacity(
          allCampBannersItems[prevIndex],
          currentOpacityPrev - position * currentOpacityPrev);
        
        if (position == 1)
        {
          allCampBannersItems[prevIndex].style.display = "none";
          
          activateCampBannerToggler(index);
          
          disableRecommendsContent();
          
          if (typeof callback == "function")
            callback.call();
        }
      });
    
    currentCampBannerIndex = index;
    
    setRecommendsTriggerStateToInitial();
  }
  
  /**
   * 指定されたインデックスのローテーション バナー切り替えボタンを
   * アクティブに設定し、その他のボタンを非アクティブにします。
   * @param {number} index 
   */
  function activateCampBannerToggler(index)
  {
    for (var i = 0; i < allCampBannersToggler.length; i++)
    {
      if (index == i)
        StyleManager.addClass(allCampBannersToggler[i], CLASS_NAME_CAMP_SELECTED);
      else
        StyleManager.removeClass(allCampBannersToggler[i], CLASS_NAME_CAMP_SELECTED);
    }
  }
  
  /**
   * ローテーション バナーの自動切り替えを停止します。
   */
  function stopRotation()
  {
    if (undefined != rotationIntervalId)
    {
      window.clearTimeout(rotationIntervalId);
      rotationIntervalId = undefined;
    }
  }
  
  /**
   * ローテーション バナーの自動切り替えを開始します。
   */
  function startRotation()
  {
    if (undefined == rotationIntervalId)
    {
      rotationIntervalId = window.setTimeout(
        window_timeout, ROTATION_INTEVAL_MSEC);
    }
  }
  
  /**
   * ローテーション バナーのセットアップを行ないます。
   */
  function initCampaignBanners()
  {
    var i, n,
        items,
        ch,
        a,
        img,
        togglerContainer,
        togglerWrap,
        toggler,
        togglerClone;
    
    allCampBannersItems    = new Array();
    allCampBannersToggler  = new Array();
    currentCampBannerIndex = 0;
    
    togglerContainer = document.createElement("UL");
    togglerContainer.id = ID_CAMP_TOGGLER;
    togglerWrap = document.createElement("LI");
    togglerWrap.appendChild(document.createElement("A"));
    
    for (i = 0, items = DOMOperator.getChildElements(campBanners), n = items.length - 1;
         i <= n;
         i++)
    {
      ch = items[i];
      
      if (i != currentCampBannerIndex)
        StyleManager.setOpacity(ch, 0);
      
      toggler = togglerContainer.appendChild(togglerWrap.cloneNode(true)).firstChild;
      
      EventManager.addEventListener(toggler, "mouseover", campBannerToggler_mouseover);
      EventManager.addEventListener(toggler, "mouseover", _event_stopRotation);
      EventManager.addEventListener(toggler, "mouseout", _event_startRotation);
      EventManager.addEventListener(toggler, "focus",     campBannerToggler_focus);
      
      a = ch.getElementsByTagName("A").item(0);
      img = ch.getElementsByTagName("IMG").item(0);
      
      if (null != a)
      {
        toggler.href = a.href;
        toggler.target = a.target;
      }
      
      if (null != img)
        toggler.appendChild(document.createTextNode(img.alt));
      
      allCampBannersItems.push(ch);
      allCampBannersToggler.push(toggler);
    }
    
    EventManager.addEventListener(campBanners, "mouseover", _event_stopRotation);
    EventManager.addEventListener(campBanners, "mouseout", _event_startRotation);
    
    activateCampBannerToggler(currentCampBannerIndex);
    
    container.insertBefore(togglerContainer, recommends.nextSibling);
  }
  
  /**
   * リフォーム メニューのセットアップを行ないます。
   */
  function initRecommends()
  {
    var i,
        items,
        ch,
        trigger,
        anchor,
        visual,
        links;
    
    allRecommendItems = new Array();
    
    for (i = 0, items = DOMOperator.getChildElements(recommends);
         i < items.length;
         i++)
    {
      ch = items[i];
      
      if (StyleManager.hasClass(ch, CLASS_NAME_RCMD_ITEM))
      {
        trigger = DOMOperator.getElementsByClassName(ch, CLASS_NAME_RCMD_TRIGGER, false);
        visual  = DOMOperator.getElementsByClassName(ch, CLASS_NAME_RCMD_VISUAL,  false);
        links   = DOMOperator.getElementsByClassName(ch, CLASS_NAME_RCMD_LINKS,   false);
        
        if (null == trigger || null == visual || null == links)
          continue;
        
        StyleManager.setOpacity(visual, 0);
        StyleManager.setOpacity(links, 0);
        visual.style.display = "none";
        links.style.display  = "none";
        EventManager.addEventListener(trigger, "mouseover", trigger_mouseover);
        
        anchor = trigger.getElementsByTagName("A").item(0);
        
        if (anchor != null)
          EventManager.addEventListener(anchor, "focus", trigger_mouseover);
        
        allRecommendItems.push(ch);
      }
    }
    
    EventManager.addEventListener(
      recommends, "mouseout", recommends_mouseout);
    
    EventManager.addEventListener(
      recommends, "mouseover", _event_stopRotation);
  }
  
  
  /* ************************************************************************ *
   * Entry Point
   * ************************************************************************ */
  
	document.documentElement.style.overflowX = "hidden";
	
  (function()
  {
    container = document.getElementById(ID_CONTAINER);
    
    if (!(null != container && null != container.nextSibling
      && typeof window.rollover != "undefined"))
    {
      window.setTimeout(arguments.callee, 1);
      return;
    }
    
    recommends = document.getElementById(ID_RECOMMENDS);
    campBanners  = document.getElementById(ID_CAMP_BANNERS);
    
    if (null == recommends || null == campBanners)
		{
			document.documentElement.style.overflowX = "auto";
      return;
		}
    
    TRANSITION_FUNCTION = Transition.easeIn;
    
    initRecommends();
    initCampaignBanners();
    
    EventManager.addStarter(contentLoaded);
    
  })();
  
})();
