/media/com_bootstrap/js/bootstrap-tooltip.js
JavaScript | 275 lines | 193 code | 59 blank | 23 comment | 38 complexity | a0015785d9e69af7cbfb238e9a0c28cf MD5 | raw file
1/* =========================================================== 2 * bootstrap-tooltip.js v2.1.1 3 * http://twitter.github.com/bootstrap/javascript.html#tooltips 4 * Inspired by the original jQuery.tipsy by Jason Frame 5 * =========================================================== 6 * Copyright 2012 Twitter, Inc. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * ========================================================== */ 20 21 22!function ($) { 23 24 "use strict"; // jshint ;_; 25 26 27 /* TOOLTIP PUBLIC CLASS DEFINITION 28 * =============================== */ 29 30 var Tooltip = function (element, options) { 31 this.init('tooltip', element, options) 32 } 33 34 Tooltip.prototype = { 35 36 constructor: Tooltip 37 38 , init: function (type, element, options) { 39 var eventIn 40 , eventOut 41 42 this.type = type 43 this.$element = $(element) 44 this.options = this.getOptions(options) 45 this.enabled = true 46 47 if (this.options.trigger == 'click') { 48 this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 49 } else if (this.options.trigger != 'manual') { 50 eventIn = this.options.trigger == 'hover' ? 'mouseenter' : 'focus' 51 eventOut = this.options.trigger == 'hover' ? 'mouseleave' : 'blur' 52 this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 53 this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) 54 } 55 56 this.options.selector ? 57 (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 58 this.fixTitle() 59 } 60 61 , getOptions: function (options) { 62 options = $.extend({}, $.fn[this.type].defaults, options, this.$element.data()) 63 64 if (options.delay && typeof options.delay == 'number') { 65 options.delay = { 66 show: options.delay 67 , hide: options.delay 68 } 69 } 70 71 return options 72 } 73 74 , enter: function (e) { 75 var self = $(e.currentTarget)[this.type](this._options).data(this.type) 76 77 if (!self.options.delay || !self.options.delay.show) return self.show() 78 79 clearTimeout(this.timeout) 80 self.hoverState = 'in' 81 this.timeout = setTimeout(function() { 82 if (self.hoverState == 'in') self.show() 83 }, self.options.delay.show) 84 } 85 86 , leave: function (e) { 87 var self = $(e.currentTarget)[this.type](this._options).data(this.type) 88 89 if (this.timeout) clearTimeout(this.timeout) 90 if (!self.options.delay || !self.options.delay.hide) return self.hide() 91 92 self.hoverState = 'out' 93 this.timeout = setTimeout(function() { 94 if (self.hoverState == 'out') self.hide() 95 }, self.options.delay.hide) 96 } 97 98 , show: function () { 99 var $tip 100 , inside 101 , pos 102 , actualWidth 103 , actualHeight 104 , placement 105 , tp 106 107 if (this.hasContent() && this.enabled) { 108 $tip = this.tip() 109 this.setContent() 110 111 if (this.options.animation) { 112 $tip.addClass('fade') 113 } 114 115 placement = typeof this.options.placement == 'function' ? 116 this.options.placement.call(this, $tip[0], this.$element[0]) : 117 this.options.placement 118 119 inside = /in/.test(placement) 120 121 $tip 122 .remove() 123 .css({ top: 0, left: 0, display: 'block' }) 124 .appendTo(inside ? this.$element : document.body) 125 126 pos = this.getPosition(inside) 127 128 actualWidth = $tip[0].offsetWidth 129 actualHeight = $tip[0].offsetHeight 130 131 switch (inside ? placement.split(' ')[1] : placement) { 132 case 'bottom': 133 tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} 134 break 135 case 'top': 136 tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} 137 break 138 case 'left': 139 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} 140 break 141 case 'right': 142 tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} 143 break 144 } 145 146 $tip 147 .css(tp) 148 .addClass(placement) 149 .addClass('in') 150 } 151 } 152 153 , setContent: function () { 154 var $tip = this.tip() 155 , title = this.getTitle() 156 157 $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 158 $tip.removeClass('fade in top bottom left right') 159 } 160 161 , hide: function () { 162 var that = this 163 , $tip = this.tip() 164 165 $tip.removeClass('in') 166 167 function removeWithAnimation() { 168 var timeout = setTimeout(function () { 169 $tip.off($.support.transition.end).remove() 170 }, 500) 171 172 $tip.one($.support.transition.end, function () { 173 clearTimeout(timeout) 174 $tip.remove() 175 }) 176 } 177 178 $.support.transition && this.$tip.hasClass('fade') ? 179 removeWithAnimation() : 180 $tip.remove() 181 182 return this 183 } 184 185 , fixTitle: function () { 186 var $e = this.$element 187 if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { 188 $e.attr('data-original-title', $e.attr('title') || '').removeAttr('title') 189 } 190 } 191 192 , hasContent: function () { 193 return this.getTitle() 194 } 195 196 , getPosition: function (inside) { 197 return $.extend({}, (inside ? {top: 0, left: 0} : this.$element.offset()), { 198 width: this.$element[0].offsetWidth 199 , height: this.$element[0].offsetHeight 200 }) 201 } 202 203 , getTitle: function () { 204 var title 205 , $e = this.$element 206 , o = this.options 207 208 title = $e.attr('data-original-title') 209 || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 210 211 return title 212 } 213 214 , tip: function () { 215 return this.$tip = this.$tip || $(this.options.template) 216 } 217 218 , validate: function () { 219 if (!this.$element[0].parentNode) { 220 this.hide() 221 this.$element = null 222 this.options = null 223 } 224 } 225 226 , enable: function () { 227 this.enabled = true 228 } 229 230 , disable: function () { 231 this.enabled = false 232 } 233 234 , toggleEnabled: function () { 235 this.enabled = !this.enabled 236 } 237 238 , toggle: function () { 239 this[this.tip().hasClass('in') ? 'hide' : 'show']() 240 } 241 242 , destroy: function () { 243 this.hide().$element.off('.' + this.type).removeData(this.type) 244 } 245 246 } 247 248 249 /* TOOLTIP PLUGIN DEFINITION 250 * ========================= */ 251 252 $.fn.tooltip = function ( option ) { 253 return this.each(function () { 254 var $this = $(this) 255 , data = $this.data('tooltip') 256 , options = typeof option == 'object' && option 257 if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) 258 if (typeof option == 'string') data[option]() 259 }) 260 } 261 262 $.fn.tooltip.Constructor = Tooltip 263 264 $.fn.tooltip.defaults = { 265 animation: true 266 , placement: 'top' 267 , selector: false 268 , template: '<div class="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>' 269 , trigger: 'hover' 270 , title: '' 271 , delay: 0 272 , html: true 273 } 274 275}(window.jQuery);