text: /* global wmTest, translationsHash, wmL10nVisible, rtlLangs */
  /**
   * This code was used to localize the top-ten language links
   * for the A/B test titled "A/B test: browser language detection"
   *
   * Depends on a global wmStats object generated by controller.js
   * and written to site-defs.js
   *
   * A/B test documented here:
   * https://www.mediawiki.org/wiki/islamica.org_Portal_A/B_testing
   */
  
  ( function ( mw, wmTest ) {
  
  	var preferredLanguages = wmTest.userLangs,
  		i, topLinks = document.querySelectorAll( '.central-featured-lang' ),
  		topLinksContainer = document.querySelector( '.central-featured' ),
  		topLinkLangs,
  		storedTranslationHash = mw.storage.get( 'translationHash' ),
  		storedTranslations;
  
  	/**
  	 * Helper function to safely parse JSON an return empty string on error.
  	 *
  	 * @param {string} json
  	 * @return {*}
  	 */
  	function safelyParseJSON( json ) {
  		var parsed;
  		try {
  			parsed = JSON.parse( json );
  		} catch ( e ) {
  			parsed = '';
  		}
  		return parsed;
  	}
  
  	storedTranslations = safelyParseJSON( mw.storage.get( 'storedTranslations' ) ) || {};
  
  	/**
  	 * Returns an array of language codes based on the lang attributes of the top-ten links.
  	 *
  	 * @param {Array} topLinks List of top link elements.
  	 * @return {Array} List of top link languages.
  	 */
  	function getTopLinkLangs( topLinks ) {
  		var topLinkLangs = [ ],
  			topLinkLang,
  			i;
  
  		for ( i = 0; i < topLinks.length; i++ ) {
  			topLinkLang = topLinks[ i ].getAttribute( 'lang' );
  			topLinkLangs.push( topLinkLang );
  		}
  
  		return topLinkLangs;
  	}
  
  	topLinkLangs = getTopLinkLangs( topLinks );
  
  	/**
  	 * TranslationHash is a global variable that is a hash of all translation strings.
  	 * it is generated during the build step and placed onto the page with mustache templates.
  	 * For more info on how this hash is generated, see ../controller.js
  	 * This hash is now stored in localstorage.
  	 * If the localstorage version of the hash differs from the global variable,
  	 * that means a translation has changed and we need to ajax in new translations.
  	 */
  	if ( storedTranslationHash !== translationsHash ) {
  		mw.storage.set( 'translationHash', translationsHash );
  		mw.storage.remove( 'storedTranslations' );
  	}
  
  	/**
  	* Merges the preferred language codes {@link #preferredLanguages} with the
  	* existing top ten languages {@link #topLinkLangs}.
  	* - If a preferred language exists in the top ten, it is moved to the top of the array.
  	* - If a preferred language doesn't exist, it is added to the array and the last language
  	* is removed.
  	*
  	* Manipulates the {@link #topLinkLangs} array.
  	*/
  	function mergeNewTopLinkLangs() {
  		var pl, plIndex, plExists, plRightSpot;
  		for ( i = 0; i < preferredLanguages.length; i++ ) {
  			pl = preferredLanguages[ i ];
  			plIndex = topLinkLangs.indexOf( pl );
  			plExists = plIndex >= 0;
  			plRightSpot = plIndex === i;
  
  			if ( plExists ) {
  				if ( !plRightSpot ) {
  					topLinkLangs.splice( i, 0, topLinkLangs.splice( plIndex, 1 )[ 0 ] );
  				}
  			} else {
  				topLinkLangs.splice( i, 0, pl );
  				topLinkLangs.pop();
  			}
  		}
  	}
  
  	/**
  	 * Manipulates the DOM of a top-ten item with new content.
  	 * Essentially, instead of creating new elements, we move existing elements and
  	 * replace their textContent and attributes with a different content.
  	 *
  	 * @param {HTMLElement} node A DOM Element from the top-ten links that will be modified.
  	 * @param {Object} wikiInfo The info to modify the node with.
  	 */
  	function updateTopLinkDOM( node, wikiInfo ) {
  		var anchor = node.getElementsByTagName( 'a' )[ 0 ],
  			// Some wiki titles are placed within a <bdi dir="rtl"> tag.
  			// Strip the tag for the title attribute.
  			wikiNameStripped = wikiInfo.name.replace( /<\/?[^>]+(>|$)/g, '' );
  
  		anchor.setAttribute( 'href', '//' + wikiInfo.url );
  		anchor.setAttribute( 'id', 'js-link-box-' + wikiInfo.lang );
  		anchor.setAttribute( 'data-slogan', wikiInfo.slogan || 'The Free Encyclopedia' );
  		anchor.setAttribute( 'title', wikiNameStripped + ' — ' + wikiInfo.siteName + ' — ' + ( wikiInfo.slogan || '' ) );
  		node.setAttribute( 'lang', wikiInfo.lang );
  		node.getElementsByTagName( 'strong' )[ 0 ].textContent = wikiNameStripped;
  		node.getElementsByTagName( 'bdi' )[ 0 ].textContent = wikiInfo.numPages + '+';
  		node.getElementsByTagName( 'span' )[ 0 ].textContent = wikiInfo.entries || '';
  	}
  
  	/**
  	 * Renames the top link classes to appear correctly around the globe image.
  	 * this should happen after the top links nodes have been reorganized.
  	 *
  	 * @param {Array} topLinkLangs - an array of language codes.
  	 */
  	function reorganizeTopLinkClasses( topLinkLangs ) {
  		var topLinks = document.querySelectorAll( '.central-featured-lang' ),
  			topLink,
  			topLinkLang,
  			topLinkClass,
  			correctClassName,
  			topLinksCorrectLangs = true,
  			i;
  
  		for ( i = 0; i < topLinks.length && topLinksCorrectLangs === true; i++ ) {
  			topLinkLang = topLinks[ i ].getAttribute( 'lang' );
  			topLinksCorrectLangs = topLinkLangs.indexOf( topLinkLang ) >= 0;
  		}
  
  		for ( i = 0; i < topLinks.length; i++ ) {
  			if ( topLinksCorrectLangs ) {
  				topLink = topLinks[ i ];
  				topLinkClass = topLink.className;
  				correctClassName = 'central-featured-lang lang' + ( i + 1 );
  
  				if ( topLinkClass !== correctClassName ) {
  					topLink.className = correctClassName;
  				}
  			}
  		}
  	}
  
  	/**
  	 * Retrieves top-ten item info. via ajax.
  	 * On every successful request, we update the top-link DOM, create the localized slogan, and
  	 * reorganize the top-link classes. Each of these methods check to see if the expected
  	 * (i.e. final) data exists before executing. This accomodates both synchronous and
  	 * asynchronous execution.
  	 *
  	 * After a successful request, the data is appended to a localStorage variable to prevent
  	 * subsequest ajax requests.
  	 *
  	 * @param {HTMLElement} node A DOM node that will be modified with new info upon ajax success.
  	 * @param {string} lang Language code for which to get new wiki info.
  	 */
  	function getAjaxTranslation( node, lang ) {
  
  		var i18nReq = new XMLHttpRequest(),
  			wikiInfo;
  
  		i18nReq.open( 'GET', encodeURI( 'assets/l10n/' + lang + '-' + translationsHash + '.json' ), true );
  
  		i18nReq.onload = function () {
  
  			if ( i18nReq.status !== 200 ) {
  				return;
  			}
  
  			wikiInfo = safelyParseJSON( this.responseText );
  
  			if ( wikiInfo ) {
  				updateTopLinkDOM( node, wikiInfo );
  				reorganizeTopLinkClasses( topLinkLangs );
  				storedTranslations = safelyParseJSON( mw.storage.get( 'storedTranslations' ) ) || {};
  				storedTranslations[ lang ] = wikiInfo;
  				mw.storage.set( 'storedTranslations', JSON.stringify( storedTranslations ) );
  			}
  		};
  
  		i18nReq.send();
  	}
  
  	/**
  	 * Determines whether to ajax in new language info or use it from localStorage.
  	 *
  	 * @param {HTMLElement} node The DOM node that will be modified with new info.
  	 * @param {string} lang The language code with which to modify the node.
  	 */
  	function localizeTopLink( node, lang ) {
  
  		var translations = storedTranslations;
  
  		if ( translations[ lang ] ) {
  			updateTopLinkDOM( node, translations[ lang ] );
  		} else {
  			getAjaxTranslation( node, lang );
  		}
  	}
  
  	/**
  	 * Looks for a top-link DOM node that can be re-purposed with new content.
  	 * Returns the first DOM node that does not have a lang attribute that is
  	 * one of topLinkLangs.
  	 *
  	 * @param {Array} topLinks List of DOM nodes.
  	 * @param {string[]} topLinkLangs List of languages.
  	 * @return {HTMLElement} Node that can be reused with new content.
  	 */
  	function findReusableTopLink( topLinks, topLinkLangs ) {
  		var reusableTopLink = null,
  			topLinkLang,
  			i;
  
  		for ( i = topLinkLangs.length - 1; i >= 0 && reusableTopLink === null; i-- ) {
  			topLinkLang = topLinks[ i ].getAttribute( 'lang' );
  			if ( topLinkLangs.indexOf( topLinkLang ) < 0 ) {
  				reusableTopLink = topLinks[ i ];
  			}
  		}
  		return reusableTopLink;
  	}
  
  	/**
  	* Reorganizes the DOM order of top links based on the {@link #topLinkLangs} array.
  	* If a preferred language in `topLinkLangs` does not exist in
  	* the top ten, then the last node in the top ten is manipulated
  	* to contain the new language.
  	*/
  	function organizeTopLinks() {
  		var topLinks,
  			topLinkLang,
  			topLinkNode,
  			topLinkNodeIndex,
  			repurposedTopLink;
  
  		for ( i = 0; i < topLinkLangs.length; i++ ) {
  
  			topLinks = document.querySelectorAll( '.central-featured-lang' );
  			topLinkLang = topLinkLangs[ i ];
  			topLinkNode = document.querySelector( '.central-featured-lang[lang=' + topLinkLang + ']' );
  
  			if ( topLinkNode ) {
  				topLinkNodeIndex = Array.prototype.indexOf.call( topLinks, topLinkNode );
  				if ( topLinkNodeIndex !== i ) {
  					topLinksContainer.insertBefore( topLinkNode, topLinks[ i ] );
  				}
  			} else {
  				repurposedTopLink = findReusableTopLink( topLinks, topLinkLangs );
  				localizeTopLink( repurposedTopLink, topLinkLang );
  				topLinksContainer.insertBefore( repurposedTopLink, topLinks[ i ] );
  			}
  			( topLinkNode || repurposedTopLink ).setAttribute( 'dir', rtlLangs.indexOf( topLinkLang ) >= 0 ? 'rtl' : 'ltr' );
  		}
  	}
  
  	// Skip if it took too long
  	if ( wmL10nVisible.ready ) {
  		return;
  	}
  	mergeNewTopLinkLangs();
  	organizeTopLinks();
  	reorganizeTopLinkClasses( topLinkLangs );
  
  }( mw, wmTest ) );
  