1/** 2 * Simple tooltip jQuery plugin. 3 * @author Monkeysan 4 * @version 1.0.0 5 */ 6;(function($){ 7 'use strict'; 8 9 /** 10 * Specifies whether the necessary global events has been registered. 11 * @private {boolean} 12 */ 13 var _singleEventsBinded = false; 14 15 /** 16 * Contains time out id for resizing window mechanism. 17 * @private {Number} 18 */ 19 var _resizeWindowTimeOutId = null; 20 21 /** 22 * Contains jQuery object of the window object. 23 * @private {jQuery} 24 */ 25 var _$w = $(window); 26 27 /** 28 * Contains jQuery object of the document.body element. 29 * @private {jQuery} 30 */ 31 var _$body = $('body'); 32 33 /** 34 * Contains private methods. 35 * @private {Object} 36 */ 37 var _privates = {}; 38 39 /** 40 * Contains all initialized tooltips on the page. 41 * @private {Array} 42 */ 43 _privates.tooltips = []; 44 45 /** 46 * Contains amount of all tooltips on the page. 47 * @private {Number} 48 */ 49 _privates.count = 0; 50 51 /** 52 * Specifies whether the browser supports css animation. 53 * @private {boolean} 54 */ 55 _privates.CSSAnimationSupported = _isAnimationSupported(); 56 57 /** 58 * Creates a tooltip element. 59 * @private 60 */ 61 _privates.prepareTooltip = function () { 62 var _self = this, 63 tooltipsClasses = ['tooltip', 'tooltip-' + _self.config.tooltipPosition, 'tooltip-skin-' + _self.config.skin]; 64 65 this.uniqueId = _privates.getUniqueId(); 66 67 if( this.needsCSSAnimation ) tooltipsClasses.push('tooltip-hidden'); 68 this.$element.attr('aria-describedby', _self.uniqueId); 69 70 this.$tooltip = $('<span></span>', { 71 id: _self.uniqueId, 72 class: _privates.getPrefixedClass.call(_self, tooltipsClasses), 73 text: _self.tooltipText, 74 'aria-hidden': 'true', 75 role: 'tooltip' 76 }).css( 'width', _self.config.width ).appendTo( _$body ); 77 78 if( this.config.width == 'auto' ) this.$tooltip.css('white-space', 'nowrap'); 79 80 if( this.needsCSSAnimation ) { 81 this.$tooltip.addClass('animated').on("webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend", function(e) { 82 var $this = $(this); 83 84 if( $this.hasClass(_self.config.animationOut) ) { 85 $this 86 .addClass(_privates.getPrefixedClass('tooltip-hidden')) 87 .removeClass(_self.config.animationOut); 88 _self.$element.trigger('hide.MonkeysanTooltip'); 89 } 90 e.stopPropagation(); 91 }); 92 } 93 else { 94 this.$tooltip.hide(); 95 } 96 97 _privates.tooltips.push(this); 98 }; 99 100 /** 101 * Registers necessary events on the element. 102 * @private 103 */ 104 _privates.bindEvents = function() { 105 var _self = this; 106 107 this.$element.on('mouseenter.MonkeysanTooltip', function(e) { 108 if( !_self._prepared ) { 109 _privates.prepareTooltip.call(_self); 110 _self._prepared = true; 111 } 112 113 _self.show(); 114 }).on('mouseleave.MonkeysanTooltip', function(e) { 115 if( !_self._prepared ) return; 116 _self.hide(); 117 }); 118 }; 119 120 /** 121 * Returns prefixed class. 122 * @param {string} className An unprefixed class. 123 * @param {boolean} dotted A dot before the class. 124 * @return {string} 125 */ 126 _privates.getPrefixedClass = function(className, dotted) { 127 if($.isArray(className)) { 128 return $.map(className, function(element){ 129 return (dotted ? '.' : '') + MonkeysanTooltip._cssPrefix + element; 130 }).join(' '); 131 } 132 return (dotted ? '.' : '') + MonkeysanTooltip._cssPrefix + className; 133 }; 134 135 /** 136 * Returns unique id for the new tooltip element. 137 * @private 138 * @return {string} 139 */ 140 _privates.getUniqueId = function() { 141 var idName = _privates.getPrefixedClass("tooltip-" + _privates.count); 142 143 for(var i = 0; i < _privates.tooltips.length; i++) { 144 if( _privates.tooltips[i].uniqueId === idName ) { 145 _privates.count++; 146 return _privates.getUniqueId(); 147 } 148 } 149 150 return idName; 151 }; 152 153 /** 154 * Sets up the correct position of the tooltip. 155 * @private 156 */ 157 _privates.setUpCorrectPosition = function() { 158 if( !this.$tooltip.length ) return; 159 this.$tooltip.removeClass( _privates.getPrefixedClass( 'tooltip-position-reversed' ) ); 160 _privates['setUpCorrectPosition' + this.config.tooltipPosition.toUpperCase()].call(this); 161 }; 162 163 /** 164 * Sets up the correct coords for the tooltip aligned by top. 165 * @param {boolean} reverse Specifies whether reverse the position of the tooltip. 166 * @private 167 */ 168 _privates.setUpCorrectPositionTOP = function(reverse) { 169 var _self = this, 170 elCoords = _self.$element.offset(); 171 172 this.$tooltip.css({ 173 'bottom': 'auto', 174 'top': elCoords.top - _self.$tooltip.outerHeight(), 175 'left': elCoords.left + _self.$element.outerWidth() / 2, 176 'margin-left': _self.$tooltip.outerWidth() / -2 177 }); 178 179 if(!reverse) _privates.correctPositionByXYAxis.call(this); 180 }; 181 182 /** 183 * Sets up the correct coords for the tooltip aligned by bottom. 184 * @param {boolean} reverse Specifies whether reverse the position of the tooltip. 185 * @private 186 */ 187 _privates.setUpCorrectPositionBOTTOM = function(reverse) { 188 var _self = this, 189 elCoords = _self.$element.offset(); 190 191 this.$tooltip.css({ 192 'bottom': 'auto', 193 'top': elCoords.top + _self.$element.outerHeight(), 194 'left': elCoords.left + _self.$element.outerWidth() / 2, 195 'margin-left': _self.$tooltip.outerWidth() / -2 196 }); 197 198 if(!reverse) _privates.correctPositionByXYAxis.call(this); 199 }; 200 201 /** 202 * Sets up the correct coords for the tooltip aligned by right. 203 * @private 204 */ 205 _privates.setUpCorrectPositionRIGHT = function() { 206 var _self = this, 207 elCoords = _self.$element.offset(); 208 209 this.$tooltip.css({ 210 'bottom': 'auto', 211 'top': elCoords.top + _self.$element.outerHeight() / 2, 212 'left': elCoords.left + _self.$element.outerWidth(), 213 'right': 'auto', 214 'margin-top': _self.$tooltip.outerHeight() / - 2 215 }); 216 217 _privates.correctPositionByXYAxis.call(this); 218 }; 219 220 /** 221 * Sets up the correct coords for the tooltip aligned by left. 222 * @private 223 */ 224 _privates.setUpCorrectPositionLEFT = function() { 225 var _self = this, 226 elCoords = _self.$element.offset(); 227 228 this.$tooltip.css({ 229 'bottom': 'auto', 230 'top': elCoords.top + _self.$element.outerHeight() / 2, 231 'left': elCoords.left - _self.$tooltip.outerWidth(), 232 'right': 'auto', 233 'margin-top': _self.$tooltip.outerHeight() / - 2 234 }); 235 236 _privates.correctPositionByXYAxis.call(this); 237 }; 238 239 /** 240 * Corrects tooltip position in the edge cases by x and y axis. 241 * @private 242 */ 243 _privates.correctPositionByXYAxis = function() { 244 var tooltipCoords = this.$tooltip.offset(), 245 bodyCoords = _$body.offset(); 246 247 // x axis 248 if( ( tooltipCoords.left + this.$tooltip.outerWidth() > _$body.outerWidth() + bodyCoords.left ) && this.config.tooltipPosition === 'right' ) { 249 _privates.setUpCorrectPositionLEFT.call(this, true); 250 this.$tooltip.addClass( _privates.getPrefixedClass( 'tooltip-position-reversed' ) ); 251 252 tooltipCoords = this.$tooltip.offset(); 253 254 if( tooltipCoords.left < 0 ) { 255 this.$tooltip.css( 'left', parseInt(this.$tooltip.css('left'), 10) + Math.abs(tooltipCoords.left) ); 256 } 257 } 258 else if(tooltipCoords.left < 0 && this.config.tooltipPosition === 'left') { 259 _privates.setUpCorrectPositionRIGHT.call(this, true); 260 this.$tooltip.addClass( _privates.getPrefixedClass( 'tooltip-position-reversed' ) ); 261 262 tooltipCoords = this.$tooltip.offset(); 263 264 if( tooltipCoords.left + this.$tooltip.outerWidth() > _$body.outerWidth() + bodyCoords.left ) { 265 this.$tooltip.css('left', parseInt(this.$tooltip.css('left'), 10) - ( (tooltipCoords.left + this.$tooltip.outerWidth()) - ( _$body.outerWidth() + bodyCoords.left) )); 266 } 267 } 268 269 // y axis 270 if( tooltipCoords.top < _$w.scrollTop() && this.config.tooltipPosition === 'top' ) { 271 _privates.setUpCorrectPositionBOTTOM.call(this, true); 272 this.$tooltip.addClass( _privates.getPrefixedClass( 'tooltip-position-reversed' ) ); 273 } 274 else if( ( (tooltipCoords.top + this.$tooltip.outerHeight()) > _$w.scrollTop() + _$w.height() ) && this.config.tooltipPosition === 'bottom' ) { 275 _privates.setUpCorrectPositionTOP.call(this, true); 276 this.$tooltip.addClass( _privates.getPrefixedClass( 'tooltip-position-reversed' ) ); 277 } 278 279 if( this.config.tooltipPosition === 'top' || this.config.tooltipPosition === 'bottom' ) { 280 if( tooltipCoords.left + this.$tooltip.outerWidth() > _$body.outerWidth() + bodyCoords.left ) { 281 this.$tooltip.css('left', parseInt(this.$tooltip.css('left'), 10) - ( (tooltipCoords.left + this.$tooltip.outerWidth()) - ( _$body.outerWidth() + bodyCoords.left) )); 282 } 283 else if( tooltipCoords.left < 0 ) { 284 this.$tooltip.css( 'left', parseInt(this.$tooltip.css('left'), 10) + Math.abs(tooltipCoords.left) ); 285 } 286 } 287 288 }; 289 290 /** 291 * Class that describes the tooltip entity. 292 * @param {jQuery} $element A jQuery element that have a tooltip. 293 * @param {Object} config A configuration object. 294 * @constructor 295 */ 296 function MonkeysanTooltip($element, config) { 297 this.$element = $element; 298 this.config = config; 299 this._prepared = false;
300 _privates.bindEvents.call(this); 301 }; 302 303 /** 304 * Contains default configuration of the tooltip. 305 * @public {Object} 306 */ 307 MonkeysanTooltip.defaults = { 308 animationIn: false, 309 animationOut: false, 310 tooltipPosition: 'top', 311 skin: 'default', 312 width: 'auto', 313 jQueryAnimationEasing: 'linear', 314 jQueryAnimationDuration: 500 315 }; 316 317 /** 318 * Contains a css prefix for all necessary tooltip classes. 319 * In case you change this property you should replace all 320 * prefixes in the css file. 321 * @private {string} 322 */ 323 MonkeysanTooltip._cssPrefix = 'monkeysan-'; 324 325 /** 326 * Contains a delay between show and hide events. 327 * @private {Number} 328 */ 329 MonkeysanTooltip._timeOutDelay = 100; 330 331 Object.defineProperties(MonkeysanTooltip.prototype, { 332 tooltipText: { 333 get: function() { 334 return this.$element.data('tooltip'); 335 }, 336 set: function(value) { 337 this.$element.data('tooltip', value); 338 } 339 }, 340 needsCSSAnimation: { 341 get: function() { 342 return _privates.CSSAnimationSupported && this.config.animationIn && this.config.animationOut; 343 } 344 }, 345 isVisible: { 346 get: function() { 347 return this.needsCSSAnimation ? !this.$tooltip.hasClass( _privates.getPrefixedClass('tooltip-hidden') ) : this.$tooltip.is(':visible'); 348 } 349 } 350 }); 351 352 /** 353 * Displays the tooltip. 354 * @return {MonkeysanTooltip} 355 */ 356 MonkeysanTooltip.prototype.show = function() { 357 var _self = this; 358 359 if(this._hideTimeOutId) clearTimeout(this._hideTimeOutId); 360 361 _privates.setUpCorrectPosition.call(this); 362 363 if( this.needsCSSAnimation ) { 364 this.$tooltip 365 .removeClass( this.config.animationOut + " " + _privates.getPrefixedClass('tooltip-hidden')) 366 .addClass( this.config.animationIn ) 367 .attr('aria-hidden', 'false'); 368 369 this.$element.trigger('show.MonkeysanTooltip'); 370 } 371 else { 372 this.$tooltip.stop().fadeIn({ 373 easing: _self.config.jQueryAnimationEasing, 374 duration: _self.config.jQueryAnimationDuration, 375 compltete: function() { 376 _self.$element.trigger('show.MonkeysanTooltip'); 377 } 378 }); 379 } 380 return this; 381 }; 382 383 /** 384 * Hides the tooltip. 385 * @return {MonkeysanTooltip} 386 */ 387 MonkeysanTooltip.prototype.hide = function() { 388 var _self = this; 389 390 this._hideTimeOutId = setTimeout(function(){ 391 if( _self.needsCSSAnimation ) { 392 _self.$tooltip 393 .removeClass( _self.config.animationIn ) 394 .addClass( _self.config.animationOut ) 395 .attr('aria-hidden', 'true'); 396 } 397 else { 398 _self.$tooltip.stop().fadeOut({ 399 easing: _self.config.jQueryAnimationEasing, 400 duration: _self.config.jQueryAnimationDuration, 401 compltete: function() { 402 _self.$element.trigger('hide.MonkeysanTooltip'); 403 } 404 }); 405 } 406 }, MonkeysanTooltip._timeOutDelay); 407 408 return this; 409 }; 410 411 /** 412 * Registers MonkeysanTooltip jQuery method. 413 * @param {Object} config 414 * @return {jQuery} 415 */ 416 $.fn.MonkeysanTooltip = function(config) { 417 config = $.extend(true, {}, MonkeysanTooltip.defaults, $.isPlainObject(config) ? config : {}); 418 419 if(!_singleEventsBinded) { 420 _$w.on('scroll.MonkeysanTooltip', function(e) { 421 if( !_privates.tooltips.length ) return; 422 423 $.each(_privates.tooltips, function(index, object){ 424 if(object.isVisible){ 425 _privates.setUpCorrectPosition.call(object); 426 } 427 }); 428 }).on('resize.MonkeysanTooltip', function(e) { 429 if( !_privates.tooltips.length ) return; 430 if( _resizeWindowTimeOutId ) clearTimeout( _resizeWindowTimeOutId ); 431 432 _resizeWindowTimeOutId = setTimeout(function(){ 433 $.each(_privates.tooltips, function(index, object){ 434 _privates.setUpCorrectPosition.call(object); 435 }); 436 }, MonkeysanTooltip._timeOutDelay); 437 }); 438 } 439 440 return this.each(function(index, element){ 441 var $element = $(element); 442 config = $.extend(true, {}, config, $element.data('tooltip-config')); 443 444 if(!$element.data('MonkeysanTooltip')) { 445 $element.data('MonkeysanTooltip', new MonkeysanTooltip($element, config)); 446 } 447 }); 448 }; 449 450 /** 451 * Helper function for checking if the browser supports css animations. 452 * @private 453 * @return {boolean} 454 */ 455 function _isAnimationSupported() { 456 var DOMPrefixes = ['Webkit', 'Moz', 'O', 'ms', 'Khtml'], 457 elem = document.createElement('div'); 458 459 if(elem.style.animationName !== undefined) return true; 460 461 for(var i = 0; i < DOMPrefixes.length; i++) { 462 if( elem.style[DOMPrefixes[i] + 'AnimationName'] !== undefined ) return true; 463 } 464 465 return false;
466 }; 467 468})(window.jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.