/* 
 * jquery.tips.js
 * マウスオーバーでツールチップを表示させるプラグイン
 * 
 * (c) 2011 kumagaiyusuke.
*/

(function ($) {

  var name_space = 'tips';
  
  $.fn[name_space] = function (contents,options) {


/* !各種設定 */

    var settings = $.extend({
    
      // ツールチップ用に生成される要素のclass名とidの接頭辞
      prefix : 'tips',
      
      // エレメントの重なり順
      zIndex : 'auto',

      // ツールチップの位置
      tipPosition : {
        top : 10,
        left : 10
      },
      
      // 表示するアニメーションの設定
      showTip : {
        duration : 400,
        easing : 'swing'
      },
      
      // 非表示にするアニメーションの設定
      hideTip : {
        duration : 400,
        easing : 'swing'
      },
      
      // カーソルを追いかける速度の設定
      chaseCursor : {
        interval : 33,
        speed : 5
      }

    }, options);


/* !jQuery Object */
    
    var $window = $(window);
    var $document = $(document);
    var $target = $(this);
    var $tip;


/* !プロパティ */
    
    var _currentPosition = { top : 0, left : 0 };
    var _chasePosition   = { top : 0, left : 0 };
    
    var chaseTimer;
    var isActiveLoop = false;
    
    var isFadeOut = false;
    
    var isVisible = false;

    var targetOffset = $target.offset();
    
    var windowSize = {
      width : $window.width(),
      height : $window.height()
    }
    
    var resizeCheck;

    var tipHTML = '<div class="' + settings.prefix + '" id="' + (settings.prefix + $.fn[name_space].count) + '">' + contents + '</div>';


/* !初期化 */

    // ツールチップ用の要素を追加
    $('body').append(tipHTML);
    
    // jQuery Object を保持
    $tip = $('#' + name_space + $.fn[name_space].count);
    
    // スタイルを初期化
    $tip.css({
      position : 'absolute',
      top  : - $document.height(),
      left : - $document.width(),
      zIndex : settings.zIndex,
      opacity : 0
    });

    // ユニークIDのカウントをインクリメント
    $.fn[name_space].count++;


/* !イベント処理 */

    $window.bind('resize', onResizeWindow);
    $target.bind('mousemove', onMouseMoveTarget);
    $document.bind('mouseout', onMouseOutDocument);
    $tip.bind($.fn[name_space].HIDE_COMPLETE, onCompleteHideTip);


/* !イベントハンドラ */
    
    // ウィンドウがリサイズされた時
    function onResizeWindow(evt) {
      $window.unbind('resize', onResizeWindow);
      resizeCheck = setTimeout(checkResize, 200);
    }
    
    // ターゲット上でマウスが動いたとき
    function onMouseMoveTarget(evt) {

      // 非表示の場合
      if (!isVisible) {
          
        // パラメーターをリセット
        isActiveLoop = true;
        isFadeOut = false;
        isVisible = true;
        
        // タイマーが設定されていた場合クリアする
        if (typeof chaseTimer != 'undefined') {
          clearTimeout(chaseTimer);
        }
        
        // タイマーを設定
        chaseTimer = setTimeout(chaseCursor, settings.chaseInterval);
  
        // 表示する位置を設定
        var positionTop  = evt.pageY + settings.tipsPosition.top;
        var positionLeft = evt.pageX + settings.tipsPosition.left;
        
        // ツールチップを表示
        showTip(positionTop, positionLeft);
        
        // 現在の位置をセット
        setCurrentPosition(positionTop, positionLeft);
  
        // イベントを登録
        $document.bind('mousemove', onMouseMoveDocument);
        
      }
      
    }
    
    // ドキュメントからマウスが外れたとき
    function onMouseOutDocument(evt) {
    
      // マウスアウトするきっかけになった要素が取得できない（=ドキュメント外に出た）とき
      if (evt.relatedTarget == null) {
        isVisible = false;
        isFadeOut = true;
        hideTip();
      }
    
    }
    
    // ドキュメント上でマウスが動いたとき
    function onMouseMoveDocument(evt) {
    
      // 移動したい位置を設定
      var chaseTop  = evt.pageY + settings.tipsPosition.top;
      var chaseLeft = evt.pageX + settings.tipsPosition.left;
      
      // マウスが$targetに乗っているか否か
      var isHitX = evt.pageX >= targetOffset.left && evt.pageX < targetOffset.left + $target.width();
      var isHitY = evt.pageY >= targetOffset.top  && evt.pageY < targetOffset.top + $target.height();
      
      // 移動したい位置をセット
      setChasePosition(chaseTop, chaseLeft);
      
      // $targetからマウスが離れたとき
      if (!(isHitX && isHitY) && !isFadeOut) {
        isVisible = false;
        isFadeOut = true;
        hideTip();
      }

    }
    
    // チップが非表示になった時
    function onCompleteHideTip(evt) {

      // ループ処理の停止
      isActiveLoop = false;
      
      // ツールチップを見えない位置まで移動
      $tip.css({
        top  : - $document.height(),
        left : - $document.width(),
        opacity : 0
      });
      
      // タイマーをストップする
      clearTimeout(chaseTimer);
      
      // イベント解除
      $document.unbind('mousemove', onMouseMoveDocument);
    }

    
/* !ゲッターセッター */
    
    // 現在のツールチップの位置
    function getCurrentPosition() {
      return _currentPosition;
    }
    function setCurrentPosition(top, left) {
      _currentPosition.top = top;
      _currentPosition.left = left;
    }
    
    // ツールチップが移動したい位置
    function getChasePosition() {
      return _chasePosition;
    }
    function setChasePosition(top, left) {
      _chasePosition.top = top;
      _chasePosition.left = left;
    }


/* !メソッド */
    
    // ウィンドウがリサイズされているかチェック
    function checkResize() {
      
      var currentWidth  = $window.width();
      var currentHeight = $window.height();
      
      // リサイズが終わったとき
      if (currentWidth == windowSize.width && currentHeight == windowSize.height) {
        targetOffset = $target.offset();
        $window.bind('resize', onResizeWindow);
      }
      // リサイズしてるとき
      else {
        windowSize.width  = currentWidth;
        windowSize.height = currentHeight;
        resizeCheck = setTimeout(checkResize, 200);
      }
    }

    // カーソルを追いかける
    function chaseCursor() {
      
      var currentPosition = getCurrentPosition();
      var chasePosition   = getChasePosition();
      var chaseTop  = currentPosition.top + (chasePosition.top - currentPosition.top) / settings.chaseCursor.speed;
      var chaseLeft = currentPosition.left + (chasePosition.left - currentPosition.left) / settings.chaseCursor.speed;
      
      // ツールチップを移動
      $tip.css({
        top  : chaseTop,
        left : chaseLeft
      });
      
      // 現在の位置をセット
      setCurrentPosition(chaseTop, chaseLeft);
      
      // ループして実行する場合
      if (isActiveLoop) {
        chaseTimer = setTimeout(chaseCursor, settings.chaseCursor.interval);
      }
      
    }
    
    // ツールチップを表示する
    function showTip(top, left) {

      // ツールチップの初期化, 表示
      $tip.stop().css({
        top : top,
        left : left,
        opacity : 0
      }).animate({
        opacity : 1
      },{
        duration : settings.showTip.duration,
        easing : settings.showTip.easing,
        complete : function () {
          // 処理完了のイベントを通知
          $tip.trigger($.fn[name_space].SHOW_COMPLETE);
        }
      });
    }

    // ツールチップを隠す
    function hideTip() {

      $tip.stop().animate({
        opacity : 0
      },{
        duration : settings.hideTip.duration,
        easing : settings.hideTip.easing,
        complete : function () {
          // 処理完了のイベントを通知
          $tip.trigger($.fn[name_space].HIDE_COMPLETE);
        }
      });
    }
    
    return this;

  };


/* !スタティックプロパティ */

  // イベント名の定義
  $.fn[name_space].SHOW_COMPLETE = name_space + 'tips.showComplete';
  $.fn[name_space].HIDE_COMPLETE = name_space + 'tips.hideComplete';

  // ツールチップの数（ユニークIDの生成に使用）
  $.fn[name_space].count = 0;

})(jQuery);

