(function($) {
    $.fn.allTipsy = new Array();
    $.fn.deleteAllTipsy = function() {
        for (var i in $.fn.allTipsy) {
            $.fn.allTipsy[i].remove();
            $.fn.allTipsy[i] = null;
        }
        $.fn.allTipsy = new Array();
    };
    
    $.fn.tipsy = function(opts) {

        opts = $.extend({fade: false, gravity: 'n'}, opts || {});
        var tip = null, cancelHide = false;

        this.click(function() {
            if ($.data(this, 'cancel.tipsy')) {
                $.data(this, 'cancel.tipsy', false);
                var self = this;
                setTimeout(function(){ $.fn.close_tipsy(self, opts); }, 100);
                return;
            }
            $.data(this, 'cancel.tipsy', true);

            var tip = $.data(this, 'active.tipsy');
            if (!tip) {
                var closeLink = '<br /><p class="closebtn"><a href="javascript: void(0);" class="close_tipsy" id="close_tipsy_'+ $.fn.allTipsy.length +'">閉じる</a><p>';
                tip = $('<div class="tipsy"><div class="tipsy-inner">' + $(this).attr('title') + closeLink +'</div></div>');
                tip.css({position: 'absolute', zIndex: 100000});
                $(this).attr('title', '');
                $(this).attr('href', 'javascript: void(0)');
                $.data(this, 'active.tipsy', tip);
            }
            
            var pos = $.extend({}, $(this).offset(), {width: this.offsetWidth, height: this.offsetHeight});
            tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
            var actualWidth = tip[0].offsetWidth, actualHeight = tip[0].offsetHeight;
            
            switch (opts.gravity.charAt(0)) {
                case 'n':
                    tip.css({top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-north');
                    break;
                case 's':
                    tip.css({top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2}).addClass('tipsy-south');
                    break;
                case 'e':
                    tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth}).addClass('tipsy-east');
                    break;
                case 'w':
                    tip.css({top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width}).addClass('tipsy-west');
                    break;
            }

            if (opts.fade) {
                tip.css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: 1});
            } else {
                tip.css({visibility: 'visible'});
            }
            var self = this;
            $('#close_tipsy_'+ $.fn.allTipsy.length).click(function() {
                $.data(self, 'cancel.tipsy', false);
                setTimeout(function(){ $.fn.close_tipsy(self,opts); }, 100);
            });
            $.fn.allTipsy.push(tip);

        });
    };
    
    $.fn.close_tipsy = function(self,opts) {
        if ($.data(self, 'cancel.tipsy')) return;
        var tip = $.data(self, 'active.tipsy');

        var tmpArray = new Array();
        for (var i in $.fn.allTipsy) {
            if (tip !== $.fn.allTipsy[i])
                tmpArray.push($.fn.allTipsy[i]);
        }
        $.fn.allTipsy = tmpArray;

        if (opts.fade) {
            tip.stop().fadeOut(function() { $(this).remove(); });
        } else {
            tip.remove();
        }
    };
})(jQuery);
