Chú ý: Sau khi lưu trang, có thể bạn sẽ phải xóa bộ nhớ đệm của trình duyệt để xem các thay đổi.

  • Firefox / Safari: Nhấn giữ phím Shift trong khi nhấn Tải lại (Reload), hoặc nhấn tổ hợp Ctrl-F5 hay Ctrl-R (⌘R trên Mac)
  • Google Chrome: Nhấn tổ hợp Ctrl-Shift-R (⇧⌘R trên Mac)
  • Internet Explorer / Edge: Nhấn giữ phím Ctrl trong khi nhấn Làm tươi (Refresh), hoặc nhấn tổ hợp Ctrl-F5
  • Opera: Nhấn tổ hợp Ctrl-F5.
'use strict';

function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }


/**
 * Initialize Tabber
 *
 * @param {HTMLElement} tabber
 */
function initTabber(tabber) {
	var key = tabber.getAttribute('id').substring(7),
	    tabPanels = tabber.querySelectorAll(':scope > .tabber__section > .tabber__panel');

	var container = document.createElement('header'),
	    tabList = document.createElement('nav'),
	    prevButton = document.createElement('div'),
	    nextButton = document.createElement('div');

	var buildTabs = function buildTabs() {
		var fragment = new DocumentFragment();

		[].concat(_toConsumableArray(tabPanels)).forEach(function (tabPanel) {
			var hash = mw.util.escapeIdForAttribute(tabPanel.title) + '-' + key,
			    tab = document.createElement('a');

			tabPanel.setAttribute('id', hash);
			tabPanel.setAttribute('role', 'tabpanel');
			tabPanel.setAttribute('aria-labelledby', 'tab-' + hash);
			tabPanel.setAttribute('aria-hidden', true);

			tab.innerText = tabPanel.title;
			tab.classList.add('tabber__tab');
			tab.setAttribute('title', tabPanel.title);
			tab.setAttribute('role', 'tab');
			tab.setAttribute('href', '#' + hash);
			tab.setAttribute('id', 'tab-' + hash);
			tab.setAttribute('aria-select', false);
			tab.setAttribute('aria-controls', hash);

			fragment.append(tab);
		});

		tabList.append(fragment);

		container.classList.add('tabber__header');
		tabList.classList.add('tabber__tabs');
		tabList.setAttribute('role', 'tablist');
		prevButton.classList.add('tabber__header__prev');
		nextButton.classList.add('tabber__header__next');

		container.append(prevButton, tabList, nextButton);
	};

	buildTabs();
	tabber.prepend(container);

	// Initalize previous and next buttons
	var initButtons = function initButtons() {
		var PREVCLASS = 'tabber__header--prev-visible',
		    NEXTCLASS = 'tabber__header--next-visible';

		/* eslint-disable mediawiki/class-doc */
		var scrollTabs = function scrollTabs(offset) {
			var scrollLeft = tabList.scrollLeft + offset;

			// Scroll to the start
			if (scrollLeft <= 0) {
				tabList.scrollLeft = 0;
				container.classList.remove(PREVCLASS);
				container.classList.add(NEXTCLASS);
			} else {
				tabList.scrollLeft = scrollLeft;
				// Scroll to the end
				if (scrollLeft + tabList.offsetWidth >= tabList.scrollWidth) {
					container.classList.remove(NEXTCLASS);
					container.classList.add(PREVCLASS);
				} else {
					container.classList.add(NEXTCLASS);
					container.classList.add(PREVCLASS);
				}
			}
		};

		var setupButtons = function setupButtons() {
			var isScrollable = tabList.scrollWidth > container.offsetWidth;

			if (isScrollable) {
				(function () {
					var scrollOffset = container.offsetWidth / 2;

					// Just to add the right classes
					scrollTabs(0);
					prevButton.addEventListener('click', function () {
						scrollTabs(-scrollOffset);
					}, false);

					nextButton.addEventListener('click', function () {
						scrollTabs(scrollOffset);
					}, false);
				})();
			} else {
				container.classList.remove(NEXTCLASS);
				container.classList.remove(PREVCLASS);
			}
		};
		/* eslint-enable mediawiki/class-doc */

		setupButtons();

		// Listen for window resize
		window.addEventListener('resize', function () {
			mw.util.debounce(250, setupButtons());
		});
	};

	/**
  * Show panel based on target hash
  *
  * @param {string} targetHash
  */
	function showPanel(targetHash) {
		var ACTIVETABCLASS = 'tabber__tab--active',
		    ACTIVEPANELCLASS = 'tabber__panel--active',
		    targetPanel = document.getElementById(targetHash),
		    targetTab = document.getElementById('tab-' + targetHash),
		    section = targetPanel.parentElement,
		    activePanel = section.querySelector(':scope > .' + ACTIVEPANELCLASS);

		/* eslint-disable mediawiki/class-doc */
		if (activePanel) {
			// Just to be safe since there can be multiple active classes
			// even if there shouldn't be
			var activeTabs = tabList.querySelectorAll('.' + ACTIVETABCLASS);

			if (activeTabs.length > 0) {
				activeTabs.forEach(function (activeTab) {
					activeTab.classList.remove(ACTIVETABCLASS);
					activeTab.setAttribute('aria-selected', false);
				});
			}

			activePanel.classList.remove(ACTIVEPANELCLASS);
			activePanel.setAttribute('aria-hidden', true);
			section.style.height = activePanel.offsetHeight + 'px';
			section.style.height = targetPanel.offsetHeight + 'px';
		} else {
			section.style.height = targetPanel.offsetHeight + 'px';
		}

		// Add active class to the tab
		targetTab.classList.add(ACTIVETABCLASS);
		targetTab.setAttribute('aria-selected', true);
		targetPanel.classList.add(ACTIVEPANELCLASS);
		targetPanel.setAttribute('aria-hidden', false);

		// Scroll to tab
		section.scrollLeft = targetPanel.offsetLeft;
		/* eslint-enable mediawiki/class-doc */
	}

	/**
  * Retrieve target hash and trigger show panel
  * If no targetHash is invalid, use the first panel
  *
  * @param {HTMLElement} tabber
  */
	function switchTab() {
		var targetHash = window.location.hash.substr(1);

		// Switch to the first tab if no targetHash or no tab is detected
		if (!targetHash || !tabList.querySelector('#tab-' + targetHash)) {
			targetHash = tabList.firstElementChild.getAttribute('id').substring(4);
		}

		showPanel(targetHash);
	}

	switchTab();

	// Only run if client is not a touch device
	if (matchMedia('(hover: hover)').matches) {
		initButtons();
	}

	// window.addEventListener( 'hashchange', switchTab, false );

	// Respond to clicks on the nav tabs
	[].concat(_toConsumableArray(tabList.children)).forEach(function (tab) {
		tab.addEventListener('click', function (event) {
			var targetHash = tab.getAttribute('href').substring(1);
			event.preventDefault();
			// Add hash to the end of the URL
			history.pushState(null, null, '#' + targetHash);
			showPanel(targetHash);
		});
	});

	tabber.classList.add('tabber--live');
}

function main() {
	var tabbers = document.querySelectorAll('.tabber');

	if (tabbers) {
		mw.loader.load('ext.tabberNeue.icons');
		tabbers.forEach(function (tabber) {
			initTabber(tabber);
		});
	}
}

main();

( function () {
	'use strict';

	function trySettingTab( indexLayout, hash ) {
		var possiblePanelName = hash.slice( 1 );
		var possiblePanel = possiblePanelName && indexLayout.getTabPanel( possiblePanelName );
		if ( possiblePanel ) {
			indexLayout.setTabPanel( possiblePanelName );
			indexLayout.scrollElementIntoView();
		}
	}

	/**
	 * This function fetches sample code in different programming languages
	 * from the sub-sections of the section "Sample Code" and places
	 * them into an OOUI tabbed window.
	 *
	 * @param {jQuery} $tabbedWindows
	 */
	function makeTabWindow( $tabbedWindows ) {
		var tabs = [];

		$tabbedWindows.each( function () {

			var panelLayout,
				indexLayout = new OO.ui.IndexLayout( {
					expanded: false
				} );

			$( this ).find( 'h3 > .mw-editsection' ).remove();

			$( this ).find( 'h3' ).each( function () {
				var id = $( this ).find( 'span[class=mw-headline]' ).attr( 'id' );

				var $content = $( this ).nextUntil( 'h3' );

				var tabPanel = new OO.ui.TabPanelLayout( id, {
					expanded: false,
					label: $( this ).text(),
					// Attach the original DOM elements directly by reference.
					// This means they move directly in memory instead of getting copied/serialized/parsed as HTML.
					// If we didn't move them, but instead copied and re-parsed HTML, then it would disconnect events
					// and other live references from gadgets and extensions, which breaks sorting features, responsive gallery, etc.
					$content: $content
				} );

				tabPanel.$element.css( 'padding', '0.5em' );

				tabs.push( tabPanel );
			} );

			indexLayout.addTabPanels( tabs );
			tabs = [];

			panelLayout = new OO.ui.PanelLayout( {
				expanded: false,
				framed: true,
				content: [ indexLayout ]
			} );

			$( this ).empty().append( panelLayout.$element );

			// Select and scroll to any initially linked item in the address
			trySettingTab( indexLayout, location.hash );
			// Keep address bar updated with sharable link (also makes forward/backward browser navigation work)
			if ( history.replaceState ) {
				indexLayout.on( 'set', function ( tabPanel ) {
					history.replaceState( null, document.title, '#' + tabPanel.getName() );
				} );
			}
			// Support anchor links on the same page (from table of Contents, or [[#Section]] links) 
			window.addEventListener( 'hashchange', function () {
				trySettingTab( indexLayout, location.hash );
			} );

		} );
	}

	$( function () {
		// Quick check (dependency-free, this is executed for all pages/actions/namespaces)
		var $tabbedWindows,
			supportedNamespaces = [ 'API', 'Help', 'Manual', '' ],
			action = mw.config.get( 'wgAction' ),
			namespace = mw.config.get( 'wgCanonicalNamespace' );

		if (
			action === 'view' &&
			supportedNamespaces.indexOf( namespace ) !== -1
		) {
			// Expensive checks (only on the subset of supported pages)
			// eslint-disable-next-line no-jquery/no-global-selector
			$tabbedWindows = $( '.mw-gadget-tabbedwindow' );
			if ( $tabbedWindows.length > 0 ) {
				// Viewing an API subject page with tabs on it, let's make them nice!
				mw.loader.using( [ 'oojs-ui-widgets' ] ).then( function () {
					makeTabWindow( $tabbedWindows );
				} );
			}
		}
	} );
}() );

mw.loader.load('https://vi.wikibooks.org/w/index.php?title=MediaWiki:Mobile.js/OOUI-ProcessDialog.js&action=raw&ctype=text/javascript');