PageSourceSearch

https://www.ashtangayoga.info/typo3conf/ext/df_tabs/Resources/Public/Scripts/jquery.tabs.js

js ashtangayoga.info collected 2026-10-02 11:12:48 UTC 10,685 bytes, 418 lines download raw bytes

1/**
2 * Initializes the tab widget
3 *
4 * @param {string} menuEntries
5 * @param {string} contentEntries
6 * @param {object} options
7 * @return {void}
8 */
9var TabBar = function(menuEntries, contentEntries, options) {
10	/**
11	 * Available options
12	 *
13	 * startIndex: starting tab index (beginning at 0)
14	 * enableAutoPlay: usage of an auto play mechanism to switch between tabs
15	 * autoPlayInterval: the time gap between the switch between two slides
16	 * enableMouseOver: usage of mouse over in addition the normal click event for changing a tab
17	 * classPrefix: prefix for all assigned classes
18	 * hashName: prefix for the location hash listener
19	 * pollingInterval: location hash polling interval
20	 * animateCallback: animation callback function
21	 *
22	 * Events:
23	 * onBeforeInitialize
24	 * onAfterInitialize
25	 * onTabChange
26	 *
27	 * @cfg {Object}
28	 */
29	this.options = {
30		startIndex: 0,
31		enableAutoPlay: false,
32		autoPlayInterval: 7000,
33		enableMouseOver: false,
34		classPrefix: 'tx-dftabs-',
35		hashName: 'tab',
36		pollingInterval: 1000,
37		animationCallback: null,
38
39		onBeforeInitialize: null,
40		onAfterInitialize: null,
41		onTabChange: null,
42		changeHashOnTabChange: false
43	};
44	$.extend(this.options, options);
45
46	/**
47	 * Tab Entry Array with Fields of Type "Object"
48	 *
49	 * Structure:
50	 * menuItem: related menu element
51	 * contentItem: related content element
52	 *
53	 * @type {Array}
54	 */
55	this.elementMap = [];
56
57	/**
58	 * The Active Tab
59	 *
60	 * @type {int}
61	 */
62	this.previousTab = this.options.startIndex;
63
64	/**
65	 * The AutoPlay Instance
66	 *
67	 * @type {int}
68	 */
69	this.autoPlay = 0;
70
71	/**
72	 * Timed Display Method
73	 *
74	 * @type {int}
75	 */
76	this.timedDisplayFunction = 0;
77
78	for (var i = 0; i < menuEntries.length; ++i) {
79		this.elementMap[i] = {};
80		this.elementMap[i].menuItem = $(menuEntries[i]);
81		this.elementMap[i].contentItem = $(contentEntries[i]);
82	}
83
84	this.finalizeInitialisation();
85};
86
87/**
88 * A tab-bar widget with very basic functionality
89 *
90 * @author Stefan Galinski <[email protected]>
91 */
92TabBar.prototype = {
93	/**
94	 * Finalizes the initialisation
95	 *
96	 * @return {void}
97	 */
98	finalizeInitialisation: function() {
99		this.trigger('beforeInitialize', this);
100
101		this.previousTab = this.options.startIndex;
102		this.parseContentLinks().addEvents().initAutoPlay().addLocationHashPolling();
103
104		this.trigger('afterInitialize', this);
105	},
106
107	/**
108	 * Parses all links and adds a smooth scrolling to the tab if the link
109	 * references to an internal tab on the very same page
110	 *
111	 * @return {TabBar}
112	 */
113	parseContentLinks: function() {
114		$.each(this.elementMap, function(index, element) {
115			var links = element.contentItem.find('a');
116			links.each(function(index, link) {
117				var parts = link.href.split('#');
118				if (parts[1] && parts[1].substr(this.options.hashName.length) === this.options.hashName
119					&& parts[0] === location.href.split('#')[0]) {
120					var hashIndex = parts[1].substr(this.options.hashName.length);
121					$(link).click(this.scrollToTab.bind(this, hashIndex));
122				}
123			}.bind(this));
124		}.bind(this));
125
126		return this;
127	},
128
129	/**
130	 * Scrolls to the menu item of the given tab index
131	 *
132	 * @param {int} tabIndex
133	 * @return {void}
134	 */
135	scrollToTab: function(tabIndex) {
136		$(window).scrollTop(this.elementMap[tabIndex].menuItem.offset().y);
137	},
138
139	/**
140	 * Adds the requested events to the tab menu elements
141	 *
142	 * @return {TabBar}
143	 */
144	addEvents: function() {
145		$.each(this.elementMap, function(index, element) {
146			if (this.options.enableMouseOver) {
147				element.menuItem.mouseenter(this.timedDisplay.bind(this, index));
148				element.contentItem.mouseenter(this.clearTimedDisplay.bind(this));
149			} else {
150				element.menuItem.click(function(event) {
151					event.preventDefault();
152					this.display.call(this, index)
153				}.bind(this));
154			}
155
156			if (this.options.enableAutoPlay) {
157				if (this.options.enableMouseOver) {
158					element.menuItem.mouseenter(this.stopAutoPlay.bind(this));
159					element.menuItem.mouseleave(this.startAutoPlay.bind(this));
160					element.menuItem.click(this.startAutoPlay.bind(this));
161				} else {
162					element.menuItem.click(this.stopAutoPlay.bind(this));
163				}
164			}
165		}.bind(this));
166
167		return this;
168	},
169
170	/**
171	 * Initializes the autoplay mechanism based on the visibility state
172	 *
173	 * Note: If the visibility state isn't available, the autoplay functionality is
174	 * started directly.
175	 *
176	 * @return {TabBar}
177	 */
178	initAutoPlay: function() {
179		var hidden = null,
180			visibilityChange = null;
181		if (typeof document.hidden !== 'undefined') {
182			hidden = 'hidden';
183			visibilityChange = 'visibilitychange';
184		} else if (typeof document.mozHidden !== 'undefined') {
185			hidden = 'mozHidden';
186			visibilityChange = 'mozvisibilitychange';
187		} else if (typeof document.msHidden !== 'undefined') {
188			hidden = 'msHidden';
189			visibilityChange = 'msvisibilitychange';
190		} else if (typeof document.webkitHidden !== 'undefined') {
191			hidden = 'webkitHidden';
192			visibilityChange = 'webkitvisibilitychange';
193		}
194
195		if (visibilityChange) {
196			document.addEventListener(
197				visibilityChange, this.toggleAutoplayBasedOnVisibility.bind(this, [hidden]), false
198			);
199			this.toggleAutoplayBasedOnVisibility(hidden);
200		} else {
201			this.startAutoPlay();
202		}
203
204		return this;
205	},
206
207	/**
208	 * Adds an URL hash evaluation with a polling interval of 1000ms to grant the possibility to
209	 * manually open content elements by changing the location hash inside the URL (e.g. acc123).
210	 *
211	 * @return {void}
212	 */
213	addLocationHashPolling: function() {
214		setTimeout(this.hashHandler.bind(this), 1);
215		setInterval(this.hashHandler.bind(this), 100);
216	},
217
218	/**
219	 * Location Hash Handler
220	 *
221	 * @return {void}
222	 */
223	hashHandler: function() {
224		if (this.locationHash === window.location.hash) {
225			return;
226		}
227
228		var index = -1;
229		this.locationHash = window.location.hash;
230		if (this.locationHash !== '') {
231			var matchExpression = new RegExp(this.options.hashName + '(\\d+)', 'i');
232			var matchResult = matchExpression.exec(this.locationHash);
233			index = parseInt(matchResult ? matchResult[1] : '', 10);
234			if (index >= 0 && index < this.elementMap.length) {
235				this.display(index, false);
236			}
237		} else {
238			if (this.previousTab !== this.options.startIndex) {
239				this.display(this.options.startIndex, false);
240			}
241		}
242	},
243
244	/**
245	 * Adds a small delay before displaying a tab (useful for mouseover)
246	 *
247	 * Note: The delay method is saved in the class property "timedDisplayFunction".
248	 *
249	 * @param {int} nextTabIndex
250	 * @return {TabBar}
251	 */
252	timedDisplay: function(nextTabIndex) {
253		this.clearTimedDisplay();
254		this.timedDisplayFunction = setTimeout(this.display.bind(this, nextTabIndex), 250);
255
256		return this;
257	},
258
259	/**
260	 * Clears the timed display function
261	 *
262	 * @return {TabBar}
263	 */
264	clearTimedDisplay: function() {
265		clearTimeout(this.timedDisplayFunction);
266
267		return this;
268	},
269
270	/**
271	 * Displays the given tab index
272	 *
273	 * @param {int} nextTabIndex
274	 * @param {Boolean} triggeredByAutoPlay
275	 * @return {TabBar}
276	 */
277	display: function(nextTabIndex, triggeredByAutoPlay) {
278		if (triggeredByAutoPlay !== true) {
279			triggeredByAutoPlay = false;
280		}
281
282		nextTabIndex = parseInt(nextTabIndex, 10);
283		var tabIndexInRange = nextTabIndex >= 0 && nextTabIndex < this.elementMap.length;
284		if (isNaN(nextTabIndex) || this.previousTab === nextTabIndex || !tabIndexInRange) {
285			return this;
286		}
287
288		if (this.options.animationCallback) {
289			this.options.animationCallback.call(this, nextTabIndex, triggeredByAutoPlay);
290		} else {
291			this.animate(nextTabIndex);
292		}
293
294		this.trigger('tabChange', this, this.previousTab, nextTabIndex);
295
296		if (this.options.changeHashOnTabChange) {
297			// save current scroll position in order to apply it after hash changed
298			// this prevents the content from jumping around as the tabs change
299			var currentScrollY = window.scrollY;
300			window.location.hash = this.options.hashName + nextTabIndex;
301			window.scrollTo(0, currentScrollY);
302		}
303
304		return this;
305	},
306
307	/**
308	 * Default "animation" of the transition between two tabs. In real it's just
309	 * a toggling of the selected classes for the content and menu items. Define
310	 * your own animation function to get a nice effect.
311	 *
312	 * @param {int} nextTabIndex
313	 * @return {TabBar}
314	 */
315	animate: function(nextTabIndex) {
316		this.toggleContentItemSelectionClasses(nextTabIndex);
317		this.toggleMenuEntrySelectionClasses(nextTabIndex);
318		this.previousTab = nextTabIndex;
319
320		return this;
321	},
322
323	/**
324	 * Toggles the "tabContentSelected" class on the last and current content elements
325	 *
326	 * @param {int} nextTabIndex
327	 * @return {TabBar}
328	 */
329	toggleContentItemSelectionClasses: function(nextTabIndex) {
330		var selectedClass = this.options.classPrefix + 'tabContentSelected';
331		this.elementMap[this.previousTab].contentItem.removeClass(selectedClass);
332		this.elementMap[nextTabIndex].contentItem.addClass(selectedClass);
333
334		return this;
335	},
336
337	/**
338	 * Toggles the "tabMenuEntrySelected" class on the last and current menu entries
339	 *
340	 * @param {int} nextTabIndex
341	 * @return {TabBar}
342	 */
343	toggleMenuEntrySelectionClasses: function(nextTabIndex) {
344		var selectedClass = this.options.classPrefix + 'tabMenuEntrySelected';
345		this.elementMap[nextTabIndex].menuItem.addClass(selectedClass);
346		this.elementMap[this.previousTab].menuItem.removeClass(selectedClass);
347
348		return this;
349	},
350
351	/**
352	 * Toggles the autplay setting based on the visibility state of the page
353	 *
354	 * @param {string} hidden field with the hidden state in the document object
355	 * @return {TabBar}
356	 */
357	toggleAutoplayBasedOnVisibility: function(hidden) {
358		if (!document[hidden]) {
359			this.startAutoPlay();
360		} else {
361			this.stopAutoPlay();
362		}
363
364		return this;
365	},
366
367	/**
368	 * Implements the auto-play mechanism
369	 *
370	 * @return {void}
371	 */
372	autoPlayMechanism: function() {
373		if (this.previousTab < this.elementMap.length - 1) {
374			this.display(this.previousTab + 1, true);
375		} else {
376			this.display(0, true);
377		}
378	},
379
380	/**
381	 * Starts the auto-play mechanism
382	 *
383	 * @return {TabBar}
384	 */
385	startAutoPlay: function() {
386		if (this.options.enableAutoPlay && !this.autoPlay) {
387			this.autoPlay = setInterval(this.autoPlayMechanism.bind(this), this.options.autoPlayInterval);
388		}
389
390		return this;
391	},
392
393	/**
394	 * Stops the auto-play mechanism
395	 *
396	 * @return {TabBar}
397	 */
398	stopAutoPlay: function() {
399		clearInterval(this.autoPlay);
400		this.autoPlay = 0;
401
402		return this;
403	},
404
405	/**
406	 * Triggers an event
407	 *
408	 * @return {void}
409	 */
410	trigger: function(event) {
411		var onFunction = 'on' + event.charAt(0).toUpperCase() + event.slice(1);
412		if (typeof this.options[onFunction] === 'function') {
413			var args = Array.prototype.slice.call(arguments);
414			args.shift();
415			this.options[onFunction].call(this, args[0], args[1], args[2], args[3]);
416		}
417	}
418};

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.