PageSourceSearch

https://www.ekjcp.org/journal/js/jquery.poshytip.js

js ekjcp.org collected 2026-10-06 10:59:21 UTC 17,538 bytes, 470 lines download raw bytes

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.