1/* 2 * Poshy Tip jQuery plugin v1.1 3 * http://vadikom.com/tools/poshy-tip-jquery-plugin-for-stylish-tooltips/ 4 * Copyright 2010-2011, Vasil Dinkov, http://vadikom.com/ 5 */ 6 7(function($) { 8 9 var tips = [], 10 reBgImage = /^url\(["']?([^"'\)]*)["']?\);?$/i, 11 rePNG = /\.png$/i, 12 ie6 = $.browser.msie && $.browser.version == 6; 13 14 // make sure the tips' position is updated on resize 15 function handleWindowResize() { 16 $.each(tips, function() { 17 this.refresh(true); 18 }); 19 } 20 $(window).resize(handleWindowResize); 21 22 $.Poshytip = function(elm, options) { 23 this.$elm = $(elm); 24 this.opts = $.extend({}, $.fn.poshytip.defaults, options); 25 this.$tip = $(['<div class="',this.opts.className,'">', 26 '<div class="tip-inner tip-bg-image"></div>', 27 '<div class="tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left"></div>', 28 '</div>'].join('')).appendTo(document.body); 29 this.$arrow = this.$tip.find('div.tip-arrow'); 30 this.$inner = this.$tip.find('div.tip-inner'); 31 this.disabled = false; 32 this.content = null; 33 this.init(); 34 }; 35 36 $.Poshytip.prototype = { 37 init: function() { 38 tips.push(this); 39 40 // save the original title and a reference to the Poshytip object 41 var title = this.$elm.attr('title'); 42 this.$elm.data('title.poshytip', title !== undefined ? title : null) 43 .data('poshytip', this); 44 45 // hook element events 46 if (this.opts.showOn != 'none') { 47 this.$elm.bind({ 48 'mouseenter.poshytip': $.proxy(this.mouseenter, this), 49 'mouseleave.poshytip': $.proxy(this.mouseleave, this) 50 }); 51 switch (this.opts.showOn) { 52 case 'hover': 53 if (this.opts.alignTo == 'cursor') 54 this.$elm.bind('mousemove.poshytip', $.proxy(this.mousemove, this)); 55 if (this.opts.allowTipHover) 56 this.$tip.hover($.proxy(this.clearTimeouts, this), $.proxy(this.mouseleave, this)); 57 break; 58 case 'focus': 59 this.$elm.bind({ 60 'focus.poshytip': $.proxy(this.show, this), 61 'blur.poshytip': $.proxy(this.hide, this) 62 }); 63 break; 64 } 65 } 66 }, 67 mouseenter: function(e) { 68 if (this.disabled) 69 return true; 70 71 this.$elm.attr('title', ''); 72 if (this.opts.showOn == 'focus') 73 return true; 74 75 this.clearTimeouts(); 76 this.showTimeout = setTimeout($.proxy(this.show, this), this.opts.showTimeout); 77 }, 78 mouseleave: function(e) { 79 if (this.disabled || this.asyncAnimating && (this.$tip[0] === e.relatedTarget || jQuery.contains(this.$tip[0], e.relatedTarget))) 80 return true; 81 82 var title = this.$elm.data('title.poshytip'); 83 if (title !== null) 84 this.$elm.attr('title', title); 85 if (this.opts.showOn == 'focus') 86 return true; 87 88 this.clearTimeouts(); 89 this.hideTimeout = setTimeout($.proxy(this.hide, this), this.opts.hideTimeout); 90 }, 91 mousemove: function(e) { 92 if (this.disabled) 93 return true; 94 95 this.eventX = e.pageX; 96 this.eventY = e.pageY; 97 if (this.opts.followCursor && this.$tip.data('active')) { 98 this.calcPos(); 99 this.$tip.css({left: this.pos.l, top: this.pos.t}); 100 if (this.pos.arrow) 101 this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow; 102 } 103 }, 104 show: function() { 105 if (this.disabled || this.$tip.data('active')) 106 return; 107 108 this.reset(); 109 this.update(); 110 this.display(); 111 if (this.opts.timeOnScreen) 112 setTimeout($.proxy(this.hide, this), this.opts.timeOnScreen); 113 }, 114 hide: function() { 115 if (this.disabled || !this.$tip.data('active')) 116 return; 117 118 this.display(true); 119 }, 120 reset: function() { 121 this.$tip.queue([]).detach().css('visibility', 'hidden').data('active', false); 122 this.$inner.find('*').poshytip('hide'); 123 if (this.opts.fade) 124 this.$tip.css('opacity', this.opacity); 125 this.$arrow[0].className = 'tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left'; 126 this.asyncAnimating = false; 127 }, 128 update: function(content, dontOverwriteOption) { 129 if (this.disabled) 130 return; 131 132 var async = content !== undefined; 133 if (async) { 134 if (!dontOverwriteOption) 135 this.opts.content = content; 136 if (!this.$tip.data('active')) 137 return; 138 } else { 139 content = this.opts.content; 140 } 141 142 // update content only if it has been changed since last time 143 var self = this, 144 newContent = typeof content == 'function' ? 145 content.call(this.$elm[0], function(newContent) { 146 self.update(newContent); 147 }) : 148 content == '[title]' ? this.$elm.data('title.poshytip') : content; 149 if (this.content !== newContent) { 150 this.$inner.empty().append(newContent); 151 this.content = newContent; 152 } 153 154 this.refresh(async); 155 }, 156 refresh: function(async) { 157 if (this.disabled) 158 return; 159
vendor: 1,780 bytes, lines 160-193
160 if (async) { 161 if (!this.$tip.data('active')) 162 return; 163 // save current position as we will need to animate 164 var currPos = {left: this.$tip.css('left'), top: this.$tip.css('top')}; 165 } 166 167 // reset position to avoid text wrapping, etc. 168 this.$tip.css({left: 0, top: 0}).appendTo(document.body); 169 170 // save default opacity 171 if (this.opacity === undefined) 172 this.opacity = this.$tip.css('opacity'); 173 174 // check for images - this code is here (i.e. executed each time we show the tip and not on init) due to some browser inconsistencies 175 var bgImage = this.$tip.css('background-image').match(reBgImage), 176 arrow = this.$arrow.css('background-image').match(reBgImage); 177 178 if (bgImage) { 179 var bgImagePNG = rePNG.test(bgImage[1]); 180 // fallback to background-color/padding/border in IE6 if a PNG is used 181 if (ie6 && bgImagePNG) { 182 this.$tip.css('background-image', 'none'); 183 this.$inner.css({margin: 0, border: 0, padding: 0}); 184 bgImage = bgImagePNG = false; 185 } else { 186 this.$tip.prepend('<table border="0" cellpadding="0" cellspacing="0"><tr><td class="tip-top tip-bg-image" colspan="2"><span></span></td><td class="tip-right tip-bg-image" rowspan="2"><span></span></td></tr><tr><td class="tip-left tip-bg-image" rowspan="2"><span></span></td><td></td></tr><tr><td class="tip-bottom tip-bg-image" colspan="2"><span></span></td></tr></table>') 187 .css({border: 0, padding: 0, 'background-image': 'none', 'background-color': 'transparent'}) 188 .find('.tip-bg-image').css('background-image', 'url("' + bgImage[1] +'")').end() 189 .find('td').eq(3).append(this.$inner); 190 } 191 // disable fade effect in IE due to Alpha filter + translucent PNG issue 192 if (bgImagePNG && !$.support.opacity) 193 this.opts.fade = false;
194 } 195 // IE arrow fixes 196 if (arrow && !$.support.opacity) { 197 // disable arrow in IE6 if using a PNG 198 if (ie6 && rePNG.test(arrow[1])) { 199 arrow = false; 200 this.$arrow.css('background-image', 'none'); 201 } 202 // disable fade effect in IE due to Alpha filter + translucent PNG issue 203 this.opts.fade = false; 204 } 205 206 var $table = this.$tip.find('table'); 207 if (ie6) { 208 // fix min/max-width in IE6 209 this.$tip[0].style.width = ''; 210 $table.width('auto').find('td').eq(3).width('auto'); 211 var tipW = this.$tip.width(), 212 minW = parseInt(this.$tip.css('min-width')), 213 maxW = parseInt(this.$tip.css('max-width')); 214 if (!isNaN(minW) && tipW < minW) 215 tipW = minW; 216 else if (!isNaN(maxW) && tipW > maxW) 217 tipW = maxW; 218 this.$tip.add($table).width(tipW).eq(0).find('td').eq(3).width('100%'); 219 } else if ($table[0]) { 220 // fix the table width if we are using a background image 221 // IE9, FF4 use float numbers for width/height so use getComputedStyle for them to avoid text wrapping 222 // for details look at: http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/ 223 $table.width('auto').find('td').eq(3).width('auto').end().end().width(document.defaultView && document.defaultView.getComputedStyle && parseFloat(document.defaultView.getComputedStyle(this.$tip[0], null).width) || this.$tip.width()).find('td').eq(3).width('100%'); 224 } 225 this.tipOuterW = this.$tip.outerWidth(); 226 this.tipOuterH = this.$tip.outerHeight(); 227 228 this.calcPos(); 229 230 // position and show the arrow image 231 if (arrow && this.pos.arrow) { 232 this.$arrow[0].className = 'tip-arrow tip-arrow-' + this.pos.arrow; 233 this.$arrow.css('visibility', 'inherit'); 234 } 235 236 if (async) { 237 this.asyncAnimating = true; 238 var self = this; 239 this.$tip.css(currPos).animate({left: this.pos.l, top: this.pos.t}, 200, function() { self.asyncAnimating = false; }); 240 } else { 241 this.$tip.css({left: this.pos.l, top: this.pos.t}); 242 } 243 }, 244 display: function(hide) { 245 var active = this.$tip.data('active'); 246 if (active && !hide || !active && hide) 247 return; 248 249 this.$tip.stop(); 250 if ((this.opts.slide && this.pos.arrow || this.opts.fade) && (hide && this.opts.hideAniDuration || !hide && this.opts.showAniDuration)) { 251 var from = {}, to = {}; 252 // this.pos.arrow is only undefined when alignX == alignY == 'center' and we don't need to slide in that rare case 253 if (this.opts.slide && this.pos.arrow) { 254 var prop, arr; 255 if (this.pos.arrow == 'bottom' || this.pos.arrow == 'top') { 256 prop = 'top'; 257 arr = 'bottom'; 258 } else { 259 prop = 'left'; 260 arr = 'right'; 261 } 262 var val = parseInt(this.$tip.css(prop)); 263 from[prop] = val + (hide ? 0 : (this.pos.arrow == arr ? -this.opts.slideOffset : this.opts.slideOffset)); 264 to[prop] = val + (hide ? (this.pos.arrow == arr ? this.opts.slideOffset : -this.opts.slideOffset) : 0) + 'px'; 265 } 266 if (this.opts.fade) { 267 from.opacity = hide ? this.$tip.css('opacity') : 0; 268 to.opacity = hide ? 0 : this.opacity; 269 } 270 this.$tip.css(from).animate(to, this.opts[hide ? 'hideAniDuration' : 'showAniDuration']); 271 } 272 hide ? this.$tip.queue($.proxy(this.reset, this)) : this.$tip.css('visibility', 'inherit'); 273 this.$tip.data('active', !active); 274 }, 275 disable: function() { 276 this.reset(); 277 this.disabled = true; 278 }, 279 enable: function() { 280 this.disabled = false; 281 }, 282 destroy: function() { 283 this.reset(); 284 this.$tip.remove(); 285 delete this.$tip; 286 this.content = null; 287 this.$elm.unbind('.poshytip').removeData('title.poshytip').removeData('poshytip'); 288 tips.splice($.inArray(this, tips), 1); 289 }, 290 clearTimeouts: function() { 291 if (this.showTimeout) { 292 clearTimeout(this.showTimeout); 293 this.showTimeout = 0; 294 } 295 if (this.hideTimeout) { 296 clearTimeout(this.hideTimeout); 297 this.hideTimeout = 0; 298 } 299 }, 300 calcPos: function() { 301 var pos = {l: 0, t: 0, arrow: ''}, 302 $win = $(window), 303 win = { 304 l: $win.scrollLeft(), 305 t: $win.scrollTop(), 306 w: $win.width(), 307 h: $win.height() 308 }, xL, xC, xR, yT, yC, yB; 309 if (this.opts.alignTo == 'cursor') { 310 xL = xC = xR = this.eventX; 311 yT = yC = yB = this.eventY; 312 } else { // this.opts.alignTo == 'target' 313 var elmOffset = this.$elm.offset(), 314 elm = { 315 l: elmOffset.left, 316 t: elmOffset.top, 317 w: this.$elm.outerWidth(), 318 h: this.$elm.outerHeight() 319 }; 320 xL = elm.l + (this.opts.alignX != 'inner-right' ? 0 : elm.w); // left edge 321 xC = xL + Math.floor(elm.w / 2); // h center 322 xR = xL + (this.opts.alignX != 'inner-left' ? elm.w : 0); // right edge 323 yT = elm.t + (this.opts.alignY != 'inner-bottom' ? 0 : elm.h); // top edge 324 yC = yT + Math.floor(elm.h / 2); // v center 325 yB = yT + (this.opts.alignY != 'inner-top' ? elm.h : 0); // bottom edge 326 } 327 328 // keep in viewport and calc arrow position 329 switch (this.opts.alignX) { 330 case 'right': 331 case 'inner-left': 332 pos.l = xR + this.opts.offsetX; 333 if (pos.l + this.tipOuterW > win.l + win.w) 334 pos.l = win.l + win.w - this.tipOuterW; 335 if (this.opts.alignX == 'right' || this.opts.alignY == 'center') 336 pos.arrow = 'left'; 337 break; 338 case 'center': 339 pos.l = xC - Math.floor(this.tipOuterW / 2); 340 if (pos.l + this.tipOuterW > win.l + win.w) 341 pos.l = win.l + win.w - this.tipOuterW; 342 else if (pos.l < win.l) 343 pos.l = win.l; 344 break; 345 default: // 'left' || 'inner-right' 346 pos.l = xL - this.tipOuterW - this.opts.offsetX; 347 if (pos.l < win.l) 348 pos.l = win.l; 349 if (this.opts.alignX == 'left' || this.opts.alignY == 'center') 350 pos.arrow = 'right'; 351 } 352 switch (this.opts.alignY) { 353 case 'bottom': 354 case 'inner-top': 355 pos.t = yB + this.opts.offsetY; 356 // 'left' and 'right' need priority for 'target' 357 if (!pos.arrow || this.opts.alignTo == 'cursor') 358 pos.arrow = 'top'; 359 if (pos.t + this.tipOuterH > win.t + win.h) { 360 pos.t = yT - this.tipOuterH - this.opts.offsetY; 361 if (pos.arrow == 'top') 362 pos.arrow = 'bottom'; 363 } 364 break; 365 case 'center': 366 pos.t = yC - Math.floor(this.tipOuterH / 2); 367 if (pos.t + this.tipOuterH > win.t + win.h) 368 pos.t = win.t + win.h - this.tipOuterH; 369 else if (pos.t < win.t) 370 pos.t = win.t; 371 break; 372 default: // 'top' || 'inner-bottom' 373 pos.t = yT - this.tipOuterH - this.opts.offsetY; 374 // 'left' and 'right' need priority for 'target' 375 if (!pos.arrow || this.opts.alignTo == 'cursor') 376 pos.arrow = 'bottom'; 377 if (pos.t < win.t) { 378 pos.t = yB + this.opts.offsetY; 379 if (pos.arrow == 'bottom') 380 pos.arrow = 'top'; 381 } 382 } 383 this.pos = pos; 384 } 385 }; 386 387 $.fn.poshytip = function(options) { 388 if (typeof options == 'string') { 389 var args = arguments, 390 method = options; 391 Array.prototype.shift.call(args); 392 // unhook live events if 'destroy' is called 393 if (method == 'destroy') 394 this.die('mouseenter.poshytip').die('focus.poshytip'); 395 return this.each(function() { 396 var poshytip = $(this).data('poshytip'); 397 if (poshytip && poshytip[method])
398 poshytip[method].apply(poshytip, args); 399 }); 400 } 401 402 var opts = $.extend({}, $.fn.poshytip.defaults, options); 403 404 // generate CSS for this tip class if not already generated 405 if (!$('#poshytip-css-' + opts.className)[0]) 406 $(['<style id="poshytip-css-',opts.className,'" type="text/css">', 407 'div.',opts.className,'{visibility:hidden;position:absolute;top:0;left:0;}', 408 'div.',opts.className,' table, div.',opts.className,' td{margin:0;font-family:inherit;font-size:inherit;font-weight:inherit;font-style:inherit;font-variant:inherit;}', 409 'div.',opts.className,' td.tip-bg-image span{display:block;font:1px/1px sans-serif;height:',opts.bgImageFrameSize,'px;width:',opts.bgImageFrameSize,'px;overflow:hidden;}', 410 'div.',opts.className,' td.tip-right{background-position:100% 0;}', 411 'div.',opts.className,' td.tip-bottom{background-position:100% 100%;}', 412 'div.',opts.className,' td.tip-left{background-position:0 100%;}', 413 'div.',opts.className,' div.tip-inner{background-position:-',opts.bgImageFrameSize,'px -',opts.bgImageFrameSize,'px;}', 414 'div.',opts.className,' div.tip-arrow{visibility:hidden;position:absolute;overflow:hidden;font:1px/1px sans-serif;}', 415 '</style>'].join('')).appendTo('head'); 416 417 // check if we need to hook live events 418 if (opts.liveEvents && opts.showOn != 'none') { 419 var deadOpts = $.extend({}, opts, { liveEvents: false }); 420 switch (opts.showOn) { 421 case 'hover': 422 this.live('mouseenter.poshytip', function() { 423 var $this = $(this); 424 if (!$this.data('poshytip')) 425 $this.poshytip(deadOpts).poshytip('mouseenter'); 426 }); 427 break; 428 case 'focus': 429 this.live('focus.poshytip', function() { 430 var $this = $(this); 431 if (!$this.data('poshytip')) 432 $this.poshytip(deadOpts).poshytip('show'); 433 }); 434 break; 435 } 436 return this; 437 } 438 439 return this.each(function() { 440 new $.Poshytip(this, opts); 441 }); 442 } 443 444 // default settings 445 $.fn.poshytip.defaults = { 446 content: '[title]', // content to display ('[title]', 'string', element, function(updateCallback){...}, jQuery) 447 className: 'tip-yellow', // class for the tips 448 bgImageFrameSize: 10, // size in pixels for the background-image (if set in CSS) frame around the inner content of the tip 449 showTimeout: 300, // timeout before showing the tip (in milliseconds 1000 == 1 second) 450 hideTimeout: 100, // timeout before hiding the tip 451 timeOnScreen: 0, // timeout before automatically hiding the tip after showing it (set to > 0 in order to activate) 452 showOn: 'hover', // handler for showing the tip ('hover', 'focus', 'none') - use 'none' to trigger it manually 453 liveEvents: false, // use live events 454 alignTo: 'cursor', // align/position the tip relative to ('cursor', 'target') 455 alignX: 'right', // horizontal alignment for the tip relative to the mouse cursor or the target element 456 // ('right', 'center', 'left', 'inner-left', 'inner-right') - 'inner-*' matter if alignTo:'target' 457 alignY: 'top', // vertical alignment for the tip relative to the mouse cursor or the target element 458 // ('bottom', 'center', 'top', 'inner-bottom', 'inner-top') - 'inner-*' matter if alignTo:'target' 459 offsetX: -22, // offset X pixels from the default position - doesn't matter if alignX:'center' 460 offsetY: 18, // offset Y pixels from the default position - doesn't matter if alignY:'center' 461 allowTipHover: true, // allow hovering the tip without hiding it onmouseout of the target - matters only if showOn:'hover' 462 followCursor: false, // if the tip should follow the cursor - matters only if showOn:'hover' and alignTo:'cursor' 463 fade: true, // use fade animation 464 slide: true, // use slide animation 465 slideOffset: 8, // slide animation offset 466 showAniDuration: 200, // show animation duration - set to 0 if you don't want show animation 467 hideAniDuration: 200 // hide animation duration - set to 0 if you don't want hide animation 468 }; 469 470})(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.