/* 
 *
 *
 * (c) 2011
 *
 * www.7digital.com
 *
 *
 */

( function ( $ ) {

/* "use strict"; */

var	apiDocumentation,
	checkForSupport,
	cookie,
	disclosureOne,
	disclosureTwo,
	footerBoxes,
	localiseLinks,
	searchBox,
	timeline,
	twitterFeedReader,
	utilities,
	visitorsCountry;	
	
/**************************************

	Feature support utilities 
	
 **************************************/
 
utilities	= {
	
// method to get variables stored in the url
	getArgumentsFromUrl : function () {
		
		var argumentsObject,
			positionOfAssignmnet,
			queryString,
			t,
			valuePairs,
			variableName,
			variableValue;
	
		argumentsObject = {};
		queryString = location.search.substring( 1 );
		valuePairs = queryString.split( "&" );
		
		for ( t = 0; t < valuePairs.length; t = t + 1 ) {
	
			positionOfAssignmnet = valuePairs[ t ].indexOf( "=" );
			
			if ( positionOfAssignmnet === -1 ) {
		
				continue;
			}
		
		variableName	= valuePairs[t].substring( 0, positionOfAssignmnet );
		variableValue 	= valuePairs[t].substring( positionOfAssignmnet + 1 );
		variableValue 	= decodeURIComponent( variableValue );
		argumentsObject[ variableName ] = variableValue;
		
	}
	
	return argumentsObject;
	
	}	
};

checkForSupport = {
	
// method to check support for local storage
	supportsLocalStorage : function() {
		
		try {
			
			return !!localStorage.getItem;
			
		} catch ( e ) {
			
			return false;
			
		}		
	}	
};

/**************************************

	Cookie utilities 
	
 **************************************/
	
cookie = {
	
// method to create a cookie
	createCookie : function( name, value, days ) {
		
		var date,
			expires;
			
		if ( days ) {
			
			date = new Date();
			date.setTime( date.getTime() + ( days * 24 * 60 * 60 * 1000 ) );
			expires = "; expires=" + date.toGMTString();
			
		} else {
			
			expires = "";
			
		}
		
		document.cookie = name + "=" + value + expires + "; path=/";
		
	},
	
// method to read a cookie
	readCookie : function( name ) {
		
		var nameEQ,
			ca,
			i,
			c;
			
		nameEQ	= name + "=";
		ca		= document.cookie.split( ";" );
		
		for ( i = 0; i < ca.length; i = i + 1 ) {
			
			c = ca[ i ];
			
			while ( c.charAt( 0 ) === " " ) {
			
				c = c.substring( 1, c.length );
				
			}
			
			if ( c.indexOf( nameEQ ) === 0 ) {
				
				return c.substring( nameEQ.length, c.length );
				
			}	
		}		
	},
	
// method to erase a cookie
	earaseCookie : function( name) {
		
		this.createCookie( name, "", -1 );
		
	}	
};
	
/**************************************

	Visitor's country 
	
 **************************************/

visitorsCountry = {
	
// method to see if we already have the country information
	checkForStoredCountryInformation : function() {
		
		var supportsLocalStorage,
			country,
			haveCountryInformation;
		
		// if browser supports local storage, we would have saved the country information there
		// otherwise we would have saved it as a cookie
		
		supportsLocalStorage = checkForSupport.supportsLocalStorage();
		
		if ( supportsLocalStorage === true ) {
			
			country = localStorage.country;
			
		} else {
			
			country = cookie.readCookie( "country" );
			
		}
		
		if ( country ) {
			
			//console.log( "after checking, returning true, we do already have the country: " + country );

			return true;
			
		} else {
		
			//console.log( "after checking, returning false, we don't already have the country: " + country );
			
			return false;
			
		}		
	},

// method to get the visitor's country via ip lookup
	getCountry : function() {

		var	country,
			alreadyHaveCountryInformationStored;
			
		alreadyHaveCountryInformationStored = this.checkForStoredCountryInformation();
		
		if ( alreadyHaveCountryInformationStored !== true ) {
			
			$.getJSON( 'http://api.wipmania.com/jsonp?callback=?','', function(json) {
				
				country = json.address.country_code;
				visitorsCountry.storeVisitorsCountry( country ).countryInformationReady();

			});
				
		} else {
			
			this.countryInformationReady();
						
		}
	},
	
// method to store visitor's country 
	storeVisitorsCountry : function ( country ) {
					
		var receivedCountry,
			supportsLocalStorage;
			
		receivedCountry			= country;
		supportsLocalStorage	= checkForSupport.supportsLocalStorage();
		
		// check to see it the browser supports local storage, if so, save the country information there
		// otherwise save it as a cookie
		
		if ( supportsLocalStorage === true ) {
			
			localStorage[ "country" ] = receivedCountry;
			
		} else {
			
			cookie.createCookie( "country", receivedCountry, 5 );
			
		}
		
	// console.log( "finished storing country information" );
	
	return this;
		
	},
	
// method to retrieve visitor's country
	retrieveVisitorsCountry : function() {
		
		var supportsLocalStorage,
			retrievedCountry;
		
		supportsLocalStorage = checkForSupport.supportsLocalStorage();
		
		if ( supportsLocalStorage === true ) {
			
			retrievedCountry = localStorage[ "country" ];
			//console.log( "retrieveVisitorsCountry says: Country read from local storage: " + retrievedCountry );
			return retrievedCountry;
			
		} else {
			
			retrievedCountry = cookie.readCookie( "country" );
			//console.log( "retrieveVisitorsCountry says: Country read from cookie: " + retrievedCountry );
			return retrievedCountry;
			
		}
	},
	
// method to call objects dependent upon country information
	countryInformationReady : function() {
	
		// console.log( "countryInformationReady called" );
		twitterFeedReader.setUp().makeRequest();
		
	}		
};

/**************************************

	Search box 
	
 **************************************/

searchBox = {
	
// dom selection object
	domSelections	:	{
		
		searchInputElements	:	{},
		headerSearchInput	:	{},
		bodySearchInput		:	{}
		
	},
	
// method to set initial state of the search boxes and collect all the things the object will need
	setUp : function() {	
	
		// get stuff we need and save selections as properties of the searchBox object
		this.domSelections.searchInputElements	= $( ".searchInputElement" , "form" );
		this.domSelections.headerSearchInput	= $( ".headerSearchInput" , "form" );
		this.domSelections.bodySearchInput		= $( ".bodySearchInput" , "form" );
		
		return this;
	
	},
	
// method to bind event functions for focus and blur of the search input field
	bindEventFunctions : function() {	
	
		var	indexOfThisInputForFocus,
			indexOfThisInputForBlur;
	
		$( searchBox.domSelections.searchInputElements ).focus( function( event ) {
			
			indexOfThisInputForFocus	= $( searchBox.domSelections.searchInputElements ).index( $( this ) );
			searchBox.removeDefaultText( indexOfThisInputForFocus );
			
		});
		
		$( searchBox.domSelections.searchInputElements ).blur( function( event ) {
			
			indexOfThisInputForBlur	= $( searchBox.domSelections.searchInputElements ).index( $( this ) );
			searchBox.insertDefaultText( indexOfThisInputForBlur );
			
		});
		
		return this;
	
	},	
	
// method to set default text in the search input
	insertDefaultText : function( indexOfInput ) {	
	
		var receivedIndexOfInput;
		receivedIndexOfInput = indexOfInput;
		
		if (	( receivedIndexOfInput === "bothNow" ) || 
				( receivedIndexOfInput === 0 && $( searchBox.domSelections.headerSearchInput ).attr( "value" ) === "" ) ) {
			
				$( searchBox.domSelections.headerSearchInput ).attr( "value" , "Search" );
				
		}
		
		if (	( receivedIndexOfInput === "bothNow" ) ||
				( receivedIndexOfInput === 1 && $( searchBox.domSelections.bodySearchInput ).attr( "value" ) === "" ) ) {
			
				$( searchBox.domSelections.bodySearchInput ).attr( "value" , "Search for something else" );
				
		}
		
		return this;
	
	},
	
// method to remove default text in the search input
	removeDefaultText : function( indexOfInput) {	
	
		var receivedIndexOfInput;
		receivedIndexOfInput = indexOfInput;
		
		if ( receivedIndexOfInput === 0 && $( searchBox.domSelections.headerSearchInput ).attr( "value" ) === "Search" ) {
		
			$( searchBox.domSelections.headerSearchInput ).attr( "value" , "" );
			
		}
		
		if ( receivedIndexOfInput === 1 && $( searchBox.domSelections.bodySearchInput ).attr( "value" ) === "Search for something else" ) {
			
				$( searchBox.domSelections.bodySearchInput ).attr( "value" , "" );
				
		}
		
		return this;
	
	}	
};

/**************************************

	Footer boxes 
	
 **************************************/

footerBoxes	= {
	
// dom selection object
	domSelections	:	{
		
		disclosureContainer		:	{},
		disclosureULs			:	{}
		
	},
	
// method to set initial state of disclosureOne elements and collect all the things the object will need
	setUp : function() {
		
		// get stuff we need and save selections as properties of the footerBoxes object
		this.domSelections.disclosureContainer	= $( ".disclosureFooterBox" , "#footerOneContainer" );
		this.domSelections.disclosureULs		= $( ".disclosureFooterBox ul" , "#footerOneContainer" );
		
		// hide the list of links
		this.domSelections.disclosureULs.hide();
	
		return this;	
		
	},
	
// method to bind event functions
	bindEventFunctions : function() {
		
		var indexOfSectionToShow;
		
		// bind showListOfLinks method to the mouseenter, and hideListOfLinks to the mouseleave events of the containing elements
		$( footerBoxes.domSelections.disclosureContainer ).hover( 
			
			function( event ) {
			
				indexOfSectionToShow = $( footerBoxes.domSelections.disclosureContainer ).index( $( this ) );
				footerBoxes.showListOfLinks( indexOfSectionToShow );
			
			}, 
			
			function( event ) {
			
				footerBoxes.hideListOfLinks();
			
			}
		);
		
	return this;
		
	},
	
// method to show the lists of links
	showListOfLinks : function( indexOfSectionToShow ) {
	
	// show the UL containing the list of links inside the hovered container
	$( this.domSelections.disclosureULs[ indexOfSectionToShow ] ).show();
	
	return this;
		
	},
	
// method to hide lists of links
	hideListOfLinks : function() {
	
	// show the UL containg the list of links
	this.domSelections.disclosureULs.hide();
	
	return this;
		
	}		
};

/**************************************

	Localise links 
	
 **************************************/

localiseLinks = {
	
	setUp : function() {
		
		var facebookLink,
			indexOfSearch,
			newFacebookHref,
			newTwitterHref,
			twitterLink,
			userCountry;
		
		twitterLink		= $( ".twitterButton", "#footerOne" );
		facebookLink 	= $( ".facebookButton", "#footerOne" );
		userCountry		= visitorsCountry.retrieveVisitorsCountry();
		indexOfSearch	= twitterFeedReader.properties.configuration.possibleSuffixes.indexOf( userCountry );
		
		if ( indexOfSearch !== -1 ) {
			
			newFacebookHref = "https://www.facebook.com/7digital" + userCountry;
			newTwitterHref	= "http://twitter.com/#!/7digital_" + userCountry;
			twitterLink.attr( "href", newTwitterHref );
			facebookLink.attr( "href", newFacebookHref );
			
		}
		
		// Netherlands' facebook link doesn't follow pattern, go figure!
		if ( userCountry === "NL" ) {
			
			facebookLink.attr( "href", "https://www.facebook.com/7digitalUS?ref=ts#!/pages/7digital-NL/172811632767284" );
			
		}
	}	
};

/**************************************

	Disclosure one 
	
 **************************************/

disclosureOne	= {
	
	configuration	: {
		
		indexOffset : 1 /* this accounts for the show all/selected button being in the UL. 0 if it's the last link, 1 if it's the first link*/
		
	},
	
	domSelections	: {
		
		arrayOfAllDisclosureOneSections			: null,
		arrayOfDisclosureOneNavigationLinks		: null,
		showAllSelectedButtonLink				: null,
		disclosureOneAllSelectedButtonContainer : null	
		
	},
	
// method to set initial state of disclosureOne elements and collect all the things the object will need
	setUp : function() {
		
		// get the stuff we need
		this.domSelections.arrayOfAllDisclosureOneSections			= $( ".disclosureOneSection" , ".bodyContentThreeColumn" );
		this.domSelections.arrayOfDisclosureOneNavigationLinks		= $( "a" , ".disclosureOneNavigation" );
		this.domSelections.showAllSelectedButtonLink				= $( ".disclosureOneAllSelectedButton" , ".disclosureOneNavigation" );
		this.domSelections.disclosureOneAllSelectedButtonContainer = $( ".disclosureOneAllSelectedButtonContainer" , ".disclosureOneNavigation" );
			
		if ( this.domSelections.arrayOfAllDisclosureOneSections.length ) {
		
			// hide all but first section
			$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections ).hide();
			$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections[ 0 ] ).show();
		
			// add selected class to the first button
			$( disclosureOne.domSelections.arrayOfDisclosureOneNavigationLinks[ disclosureOne.configuration.indexOffset ] ).addClass( "disclosureOneSelectedButton" );
		
			// add disclosureOneShowAllSelected class to the show all/selected button
			$( disclosureOne.domSelections.showAllSelectedButtonLink ).addClass( "disclosureOneShowSelectedIsCurrent" );

			this.bindEventFunctions();
			this.initialListingHandler();
			
		}
	
	return this;	
		
	},
	
// method to bind event functions
	bindEventFunctions : function() {

		var indexOfSelectedSection;
		
		// bind function to onclick of the individual selector button
		$( disclosureOne.domSelections.arrayOfDisclosureOneNavigationLinks ).click( function( event ) {
		
			// prevent default link action
			event.preventDefault();
			
			// bind our method depending on which type of button was clicked { show section corresponding to the 
			//clicked thumbnail button, or the specially classed show selected/all toggle button }
			
			if ( $( this ).hasClass( "disclosureOneAllSelectedButton" ) ) {
				
				disclosureOne.showAllSelectedToggle();
				
			} else {
		
				// we pass the index of the clicked link so showSelectedSection method knows which one to show
				indexOfSelectedSection	= $( disclosureOne.domSelections.arrayOfDisclosureOneNavigationLinks ).index( $( this ) );
				disclosureOne.showSelectedSection( indexOfSelectedSection );
			
			}				
		});
		
	return this;
		
	},
	
// method to show selected section
	showSelectedSection : function( indexOfSelectedSection ) {
		
		// remove disclosureOneSelectedButton class from the element that has it and add it to the clicked link
		$( ".disclosureOneSelectedButton" ).removeClass( "disclosureOneSelectedButton" );
		$( disclosureOne.domSelections.arrayOfDisclosureOneNavigationLinks[ indexOfSelectedSection ] ).addClass( "disclosureOneSelectedButton" );
		
		// hide all the sections then show the one with the index that correspondes to the idex of the clicked link
		$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections ).hide();
		$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections[ indexOfSelectedSection - this.configuration.indexOffset ] ).show();
		
	},
	
// method to toggle between showing all and showing selected
	showAllSelectedToggle : function() {
		
		var selectedButtonIndex;
		
		// check class of the showAllSelectedButton and use it to decide which way to toggle
		if ( $( disclosureOne.domSelections.showAllSelectedButtonLink ).hasClass( "disclosureOneShowSelectedIsCurrent" ) ) {
			
			// only the selected is showing but we want them all to show, so show them all
			$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections ).show();
			
		} else if ( $( disclosureOne.domSelections.showAllSelectedButtonLink ).hasClass( "disclosureOneShowAllIsCurrent" ) ) {
			
			// they're all showing but we only want the selected one to show, so hide all the sections then show the 
			// section that corresponds to the index of the button which is selected
			/*
			$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections ).hide();			
			selectedButtonIndex = $( ".disclosureOneSelectedButton" ).index( ".disclosureOneNavigation a" );		
			$( disclosureOne.domSelections.arrayOfAllDisclosureOneSections[ selectedButtonIndex ] ).show();
			*/
			selectedButtonIndex = $( ".disclosureOneSelectedButton" ).index( ".disclosureOneNavigation a" );
			disclosureOne.showSelectedSection( selectedButtonIndex );
		}
		
		// toggle the state of the show all/selected button
		disclosureOne.showAllSelectedButtonToggleState();
				
	},
	
// method to toggle the state of the showing all/showing selected button
	showAllSelectedButtonToggleState : function() {
		
		// this may seem the wrong way round, but we're testing the current state, which is now wrong, and making it right
		
		if ( $( disclosureOne.domSelections.showAllSelectedButtonLink ).hasClass( "disclosureOneShowSelectedIsCurrent" ) ) {
			
			$( disclosureOne.domSelections.showAllSelectedButtonLink ).removeClass( "disclosureOneShowSelectedIsCurrent" )
				.addClass( "disclosureOneShowAllIsCurrent" )
				.attr( "title" , "Show selected" )
				.html( "Show selected" );
			$( disclosureOne.domSelections.disclosureOneAllSelectedButtonContainer ).addClass( "disclosureOneAllSelectedButtonContainerActive" );
						
		} else if ( $( disclosureOne.domSelections.showAllSelectedButtonLink ).hasClass( "disclosureOneShowAllIsCurrent" ) ) {
			
			$( disclosureOne.domSelections.showAllSelectedButtonLink ).removeClass( "disclosureOneShowAllIsCurrent" )
				.addClass( "disclosureOneShowSelectedIsCurrent" )
				.attr( "title" , "Show all" )
				.html( "Show all" );
			$( disclosureOne.domSelections.disclosureOneAllSelectedButtonContainer ).removeClass( "disclosureOneAllSelectedButtonContainerActive" );
			
		}	
	},
	
// method to check for initial listing
	initialListingHandler : function() {
		
		var	meaningfulChoice,
			sectionNumberAsString,
			sectionNumberAsInteger,
			urlArgumentObject;
		
		urlArgumentObject = utilities.getArgumentsFromUrl();
		
		if ( urlArgumentObject.showSection ) {
			
			sectionNumberAsString 	= urlArgumentObject.showSection;
			sectionNumberAsInteger	= parseInt( sectionNumberAsString, 10 );
			meaningfulChoice		= this.evaluateSectionChoice( sectionNumberAsInteger );
			
			// console.log( "meaningful choice: " + meaningfulChoice );
			
			if ( meaningfulChoice === true ) {
			
				this.showSelectedSection( sectionNumberAsInteger );
				
			}
		}

		return this;
		
	},
	
// check that requested section is a meaningful choice
	evaluateSectionChoice : function( choice ) {
		
		var numberOfSections;
		
		numberOfSections	= this.domSelections.arrayOfAllDisclosureOneSections.length;
		
		if ( choice <= numberOfSections && choice !== 0 ) {
			
			return true;
			
		} else {
		
			return false;
			
		}
	}		
};

/**********************************************
 
	Disclosure two
 
***********************************************/

disclosureTwo = {
	
	configuration : {
		
		textClosed	: "Read more",
		textOpen	: "Read less"
		
	},
	
	domSelections : {
	
		showHideButtons 	: null,
		disclosureSections	: null		
		
	},
	
// method to set up disclosure two object
	setUp : function() {
		
		var defaultButtonText;
		
		defaultButtonText						= this.configuration.textClosed;
		this.domSelections.showHideButtons 		= $( ".disclosureTwoButton", ".disclosureTwoSection" );
		this.domSelections.disclosureSections	= $( ".disclosureTwoSection", "#pageContainer" );
		
		this.domSelections.showHideButtons.each( function() {
			
			$( this ).html( defaultButtonText ).attr( "title", defaultButtonText ).addClass( "closed" );
			
		});
		
		this.bindEventFunctions();
		this.hideDetailedContent();
		this.initialListingHandler();
		
		return this;
		
	},
	
// method to bind event functions
	bindEventFunctions : function() {
		
		$( disclosureTwo.domSelections.showHideButtons ).click( function( event ) {
           
		   event.preventDefault();
		   disclosureTwo.buttonClickHandler( event );
		    
        });
	
	return this;	
		
	},
	
// method to check for initial listing
	initialListingHandler : function() {
		
		var	meaningfulChoice,
			sectionNumberAsString,
			sectionNumberAsInteger,
			urlArgumentObject;
		
		urlArgumentObject = utilities.getArgumentsFromUrl();
		
		if ( urlArgumentObject.showSection ) {
			
			sectionNumberAsString 	= urlArgumentObject.showSection;
			sectionNumberAsInteger	= parseInt( sectionNumberAsString, 10 );
			meaningfulChoice		= this.evaluateSectionChoice( sectionNumberAsInteger );
			
			if ( meaningfulChoice === true ) {
			
				this.showInitialListing( sectionNumberAsInteger );
				
			}
		}

		return this;
		
	},
	
// method to show initial listing 
	showInitialListing : function( sectionNumber ) {
		
		var heightOfSectionElement,
			requestedSectionElement,
			requestedSectionElementTopOffset,
			scrollToValue;
		
		requestedSectionElement 			= this.domSelections.disclosureSections[ sectionNumber - 1 ];
		heightOfSectionElement				= $( requestedSectionElement ).height();
		requestedSectionElementTopOffset	= $( requestedSectionElement ).offset().top;
		scrollToValue						= requestedSectionElementTopOffset - ( heightOfSectionElement / 2 );
		
		$( "html, body" ).animate(
			
			{ 
		
				scrollTop: scrollToValue 
			
			}, "slow" );
		
		// console.log(requestedSectionElement);

	},
	
// check that requested section is a meaningful choice
	evaluateSectionChoice : function( choice ) {
		
		var numberOfSections;
		
		numberOfSections	= this.domSelections.disclosureSections.length;
		
		if ( choice <= numberOfSections && choice !== 0 ) {
			
			return true;
			
		} else {
		
			return false;
			
		}
	},
	
// method to hide detailed Content
	hideDetailedContent : function() {
		
		$( ":not( :nth-child( 1 ) , :nth-child( 2 ) )", ".disclosureTwoSection" ).each( function() {
            
			$( this ).addClass( "displayNone" );
			
        });
	},
	
// method to handle button click
	buttonClickHandler : function( event ) {
		
		var thisButton,
			correspondingSection;
		
		thisButton 				= event.currentTarget;
		correspondingSection	= $( thisButton ).parent();
		
		this.toggleSection( correspondingSection );
		this.toggleButtonText( thisButton );
		this.toggleButtonStyle( thisButton );
		
		return this;
		
	},
	
// method to toggle section
	toggleSection : function( section ) {
		
		var thisButtonText;
		
		thisButtonText = $( ".disclosureTwoButton", section ).html();		
		
		if ( thisButtonText === this.configuration.textOpen ) {
			
			this.collapseSection( section );
			
		} else {
			
			this.expandSection( section );
			
		}		
	},

// method to expand section
	expandSection : function( section ) {
		
		$( ".displayNone", section ).each( function() {
            
			$( this ).removeClass( "displayNone" );
			
        });
		
		return this;
		
	},
	
// method to collapse section
	collapseSection : function( section ) {
		
		$( ":not( :nth-child( 1 ) , :nth-child( 2 ) )", section ).each( function() {
            
			$( this ).addClass( "displayNone" );
			
        });
		
		return this;
		
	},
	
// method to toggle text
	toggleButtonText : function( button ) {
		
		var currentText,
			textOpen;
		
		currentText = $( button ).html();
		textOpen	= disclosureTwo.configuration.textOpen;
		
		if ( currentText === textOpen ) {
			
			$( button ).html( disclosureTwo.configuration.textClosed );
			$( button ).attr( "title", disclosureTwo.configuration.textClosed );
			
		} else {
			
			$( button ).html( textOpen );
			$( button ).attr( "title", textOpen );
			
		}
		
		return this;
		
	},
	
// method to toggle button style
	toggleButtonStyle : function( button ) {
		
		var isOpen;
		
		isOpen = $( button ).hasClass( "open" );
		
		if ( isOpen === true ) {
			
			$( button ).removeClass( "open" );
			$( button ).addClass( "closed" );
			
		} else {
			
			$( button ).removeClass( "closed" );
			$( button ).addClass( "open" );
			
		}
		
		return this;
		
	}
};

/**************************************

	Twitter feed reader 
	
 **************************************/

twitterFeedReader	= {
	
	properties	:	{
		
		displayContainer	: {},
		configuration		: {
		
			baseQueryString		: "http://twitter.com/status/user_timeline/",
			baseAccountName		: "@7digital",
			countrySuffix		: "",
			possibleSuffixes	: "US, DE, FR, NL, ES, CA",
			numberOfTweets		: ".json?count=1&callback=?",
			hashLinkBase		: "http://twitter.com/search?q=%23",
			atLinkBase			: "http://twitter.com/#!/"	

		}	
	},
	
// method to set initial state of twitterFeedReader elements, request the visitor's country from the visitorsCountry object
// and collect all the things the object will need
	setUp : function() {
		
		var countryCode,
			indexOfSearch;

		this.properties.displayContainer	= $( ".twitterFeedReader" , "#footerOne" );
		countryCode							= visitorsCountry.retrieveVisitorsCountry();
		indexOfSearch						= this.properties.configuration.possibleSuffixes.indexOf( countryCode );
		
		if ( indexOfSearch !== -1 ) {
			
			this.properties.configuration.countrySuffix = "_" + countryCode;

			
		} else {
			
			this.properties.configuration.countrySuffix = "";
			
		}
		
		// console.log( "twitterFeedReader.setUp says that countrySuffix is: " + this.properties.configuration.countrySuffix );
		
		return this;
				
	},
	
// method to construct query string
	constructQueryString : function() {
		
		var queryString	=	this.properties.configuration.baseQueryString 
							+ this.properties.configuration.baseAccountName 
							+ this.properties.configuration.countrySuffix
							+ this.properties.configuration.numberOfTweets;

		// console.log( queryString );
		
		return queryString;
		
	},	
	
// method to make the ajax request of twitter and get the json
	makeRequest	: function() {		
		
		var queryString,
			twitterResponse;
		
		queryString		= this.constructQueryString();		
		twitterResponse	= $.getJSON( queryString,
							
					function( data ) {
						
						/*
						// write response from twitter onto the page for testing
						var prettyString = JSON.stringify( data );
						$( "#jsonResponse" ).html( prettyString );
						*/
						
						var	i,
							thisContent,
							rawContent,
							prettyContent,
							tweetsArray,
							contentAsString;

						tweetsArray		= [];
						contentAsString = "";
						
						// create a content string from as many tweets as we requested, 
						for ( i = 0; i < data.length; i = i + 1 ) {
							
							// check for links in tweet
							rawContent		= data[i].text;
							prettyContent	= twitterFeedReader.checkStringForLinks( rawContent );
							thisContent		= "<p>" + prettyContent + "</p>";
							contentAsString = contentAsString + thisContent;
							
						}

						// remove progress bar by removing class from containing element
						$( ".waitingForTwitterResponse" , "#footerOne" ).removeClass( "waitingForTwitterResponse" );						
						
						// display the content
						twitterFeedReader.properties.displayContainer.html( contentAsString );
											
					});	
		},
		
// method to check for links in a string and to mark them up
	checkStringForLinks : function( string ) {
		
		var	prettyString,
			rawString,
			urlMatchExpression,
			hashLinkMatchExpression,
			atLinkMatchExpression,
			urlMatch,
			hashLinkMatch,
			atLinkMatch;
			
		rawString				= string;
		urlMatchExpression		= /http[^\s]+/;
		hashLinkMatchExpression	= /#[^\s]+/;
		atLinkMatchExpression	= /@[^\s]+/;
		urlMatch				= rawString.match( urlMatchExpression );
		hashLinkMatch			= rawString.match( hashLinkMatchExpression );
		atLinkMatch				= rawString.match( atLinkMatchExpression );
		
		// check for standard links in string
		if ( ! urlMatch ) {
			
			prettyString = rawString;
			
		} else {
			
			prettyString = this.markupLinks( rawString, urlMatchExpression );
		
		}
		
		// check for twitter type hash links in string
		if ( ! hashLinkMatch ) {
			
			prettyString = prettyString;
			
		} else {
			
			prettyString = this.markupHashLinks( prettyString, hashLinkMatchExpression );
			
		}
		
		// check for twitter type @ links in string
		if ( ! atLinkMatch ) {
			
			prettyString = prettyString;
			
		} else {
			
			prettyString = this.markupAtLinks( prettyString, atLinkMatchExpression );
			
		}
		
		return prettyString;
		
	},
	
// method to mark up urls
	markupLinks : function( string, matchExpression ) {
				
		var	i,
			markedupLink,
			rawString,
			prettyString,
			wordArray;

		rawString		= string;
		prettyString	= "";
		wordArray		= rawString.split( " " );
		
		for ( i = 0; i < wordArray.length; i = i + 1 ) {			
						
			if ( wordArray[ i ].match( matchExpression ) === null ) {
				
				prettyString = prettyString + " " + wordArray[ i ];
				
			} else {
				
				markedupLink = wordArray[ i ].link( wordArray[ i ] );
				prettyString = prettyString + " " + markedupLink;
				
			}			
		}
			
		return prettyString;
		
	},
	
// method to mark up twitter type hash links
	markupHashLinks : function( string, matchExpression ) {
		
		var i,
			markedupLink,
			thisWordWithoutHash,
			hashStripperMatchExpression,
			rawString,
			prettyString,
			wordArray;
			
		hashStripperMatchExpression = /[^#]+/;
		rawString					= string;
		prettyString				= "";
		wordArray					= rawString.split( " " );
		
		for ( i = 0; i < wordArray.length; i = i + 1 ) {
	
			if ( wordArray[ i ].match( matchExpression ) === null ) {
				
				prettyString = prettyString + " " + wordArray[ i ];	
				
			} else {
				
				thisWordWithoutHash = wordArray[ i ].match( hashStripperMatchExpression );
				markedupLink		= wordArray[ i ].link( this.properties.configuration.hashLinkBase + thisWordWithoutHash );
				prettyString		= prettyString + " " + markedupLink;
				
			}
		}

		return prettyString;
	
	},

// method to mark up twitter type @ links
	markupAtLinks : function( string, matchExpression ) {
		
		var i,
			markedupLink,
			thisWordWithoutAt,
			atStripperMatchExpression,
			rawString,
			prettyString,
			wordArray;

		atStripperMatchExpression	= /[^@]+/;
		rawString					= string;
		prettyString				= "";
		wordArray					= rawString.split( " " );
		
		for ( i = 0; i < wordArray.length; i = i + 1 ) {
	
			if ( wordArray[ i ].match( matchExpression ) === null ) {
				
				prettyString = prettyString + " " + wordArray[ i ];	
				
			} else {
				
				thisWordWithoutAt	= wordArray[ i ].match( atStripperMatchExpression );
				markedupLink		= wordArray[ i ].link( this.properties.configuration.atLinkBase + thisWordWithoutAt );
				prettyString		= prettyString + " " + markedupLink;
				
			}
		}

		return prettyString;
	
	}
};
	
/**************************************

	Timeline 
	
 **************************************/
	
timeline = {
	
	domSelections	:	{
		
		pointerElement		: {},
		pointerDropZone		: {},
		navigationLinks		: {},
		yearContentBlocks	: {}
		
	},
	
	configuration	:	{
		
		indexForShowAll				: 0, /* 0 for top of ol */
		indexForInitialSelection	: 1, /* index of the pointer {eg. 1 is the top year if there is a show all button } */
		indexOffsetForContent		: 1 /* 1 if "show all" button is at the top of the navigation list, otherwise 0 */
		
	},
	
	constants		:	{
		
		numberOfYears				: null,
		heightOfYearMarkerElement	: null
		
	},
	
// method to set initial state and collect all the things we need
	setUp : function() {	
		
		// store selections
		this.domSelections.pointerElement			= $( "#timelineYearPointer" );
		this.domSelections.pointerDropZone			= $( "#timelinePointerContainer" );
		this.domSelections.navigationLinks			= $( "#timelineNavigation ol li a" );
		this.domSelections.yearContentBlocks		= $( ".timelineYearLevel", "#pageContainer" ).children();
		
		//set constants
		this.constants.numberOfYears				= this.domSelections.navigationLinks.length - 1;
		this.constants.heightOfYearMarkerElement	= $( this.domSelections.navigationLinks ).outerHeight( true );		

		// call other setup methods
		this.makeDraggableAndDroppable().disableAndStyleLinks().setInitialSelection();

		return this;
	
	},
	
// method to disable and style navigation links
	disableAndStyleLinks : function() {
		
		$( this.domSelections.navigationLinks ).bind( "click mouseover mouseout focus blur keypress", function( event ) {
			
			event.preventDefault();
			
		}).addClass( "timelineNavigationDisabledLink" );
	
		return this;
		
	},
	
// method to make elements draggable and droppable
	makeDraggableAndDroppable : function() {
		
		$( this.domSelections.pointerElement ).draggable({
			
															axis		:"y",
															scroll		: false,
															grid		: [ 0, timeline.constants.heightOfYearMarkerElement ],
															containment	: timeline.domSelections.pointerDropZone															
															
														});
														
		$( this.domSelections.pointerDropZone ).droppable({ 
		
			drop : function( event, ui ) {
				
				timeline.pointerDropped();
				
			}		
		});
		
		return this;
		
	},

// method to set the initial selection 	
	setInitialSelection : function() {
		
		var indexForInitialSelection;
		indexForInitialSelection = this.configuration.indexForInitialSelection;
		
		this.setPointerPosition( indexForInitialSelection ).showContentSelection( indexForInitialSelection )
			.styleCurrentSelection( indexForInitialSelection );
		
		return this;
		
	},
	
// method to style current selection
	styleCurrentSelection : function( indexOfPointerSelection ) {
		
		// remove current class from the old current link, and add it to the new current link
		$( ".timelineNavigationCurrent", "#timelineNavigation" ).removeClass( "timelineNavigationCurrent" );
		$( this.domSelections.navigationLinks ).eq( indexOfPointerSelection ).addClass( "timelineNavigationCurrent" );
		
		return this;
		
	},

// method to set position of pointer
	setPointerPosition : function( indexOfPointerSelection ) {
		
		var	topValue;
		topValue = ( indexOfPointerSelection * this.constants.heightOfYearMarkerElement );
		
		$( this.domSelections.pointerElement ).css( "top" , topValue );
		
		return this;
		
	},

// method to show content selection
	showContentSelection : function( indexOfPointerSelection ) {
		
		var indexOfSectionToShow;
		indexOfSectionToShow = ( indexOfPointerSelection - this.configuration.indexOffsetForContent );
		
		if ( indexOfPointerSelection === this.configuration.indexForShowAll ) {
			
			// show them all
			$( this.domSelections.yearContentBlocks ).show();
			
		} else {
		
			// hide them all, then show the selected one
			$( this.domSelections.yearContentBlocks ).hide().eq( indexOfSectionToShow ).show();
			
		}
		
		return this;
		
	},

// method to handle pointer drop
	pointerDropped : function() {
		
		var	topValueOfPointerElement,
			indexOfDrop;
		
		 topValueOfPointerElement = parseInt( this.domSelections.pointerElement.css( "top" ) , 10 );
		 
		 if ( timeline.constants.heightOfYearMarkerElement !== 0 ) {
			 
			indexOfDrop = topValueOfPointerElement / timeline.constants.heightOfYearMarkerElement;
			 
		 } else {
			 
			indexOfDrop = 0;
			 
		 }
		
		this.styleCurrentSelection( indexOfDrop ).setPointerPosition( indexOfDrop ).showContentSelection( indexOfDrop );
		
		return this;
		
	}
};

/**************************************

	Api documentation 
	
 **************************************/

apiDocumentation = {
	
	configuration : {
		
		indexForShowAllSelected		: 0, /* 0 if top of list */
		indexForInitialSelection	: 0, /* what to show first, 0 for first section, 1 for second ... */
		indexOffsetForContent		: 1 /* 1 if "show all" button is at the top of the navigation list, otherwise 0 */
		
	},
	
	domSelections : {
		
		navigationContainer		: {},
		showAllSelectedButton	: {},
		navigationLinks			: {},
		contentSections			: {}		
		
	},
	
	constants	:	{
		
		stickyElementOffsetTop : null

	},
	
// method to set initial state and collect all the things we need
	setUp : function() {	

		if ( document.getElementById( "documentationNavigation" ) ) {
			
			// store selections
			this.domSelections.navigationContainer		= $( "#documentationNavigation" );
			this.domSelections.showAllSelectedButton	= $( "#apiNavigationShowAllSelectedButton" );
			this.domSelections.navigationLinks			= $( "#documentationNavigation a" );
			this.domSelections.contentSections			= $( ".apiDocumentationSection", ".bodyContentThreeColumn" );
			this.constants.stickyElementOffsetTop		= $( this.domSelections.navigationContainer ).offset().top;
		
			// style show all/ selected button and current selection button
			$( this.domSelections.showAllSelectedButton ).addClass( "apiNavigationShowingSelected" );
			$( this.domSelections.navigationLinks )
				.eq( this.configuration.indexForInitialSelection + this.configuration.indexOffsetForContent )
				.addClass( "apiDocumentationNavigationCurrent" );

			// call other setup methods
			this.disableAndStyleLinks().bindEventFunctions().setInitialSelection().stickUnstickApiNavigation();
			
		}
		
		return this;
	
	},
	
// method to disable and style navigation links
	disableAndStyleLinks : function() {
		
		$( this.domSelections.navigationLinks ).bind( "click mouseover mouseout focus blur keypress", function( event ) {
			
			event.preventDefault();
			
		}).addClass( "apiDocumentationNavigationDisabledLink" );
	
		return this;
		
	},
	
// method to bind event function
	bindEventFunctions : function() {
		
		$( this.domSelections.navigationLinks ).click( function( event ) {
			
			var indexOfClickedLink;
			indexOfClickedLink = $( apiDocumentation.domSelections.navigationLinks ).index( $( this ) );
			
			apiDocumentation.showContentSelectionHandler( indexOfClickedLink );
			
			// scroll window so navigation is a above section heading
			apiDocumentation.scrollToPosition();
			
		});
		
		return this;		
	},
	
// method to set the initial selection 	
	setInitialSelection : function() {
		
		var indexForInitialSelection;
		indexForInitialSelection = this.configuration.indexForInitialSelection;
		
		apiDocumentation.showSelectionAtIndex( indexForInitialSelection );
		
		return this;
		
	},
	
// method to style current selection
	styleCurrentSelection : function( index ) {
		
		var receivedIndex;
		receivedIndex = index;
		
		$( ".apiDocumentationNavigationCurrent", "#documentationNavigation" )
			.removeClass( "apiDocumentationNavigationCurrent");
		$( this.domSelections.navigationLinks ).eq( receivedIndex + this.configuration.indexOffsetForContent  )
			.addClass( "apiDocumentationNavigationCurrent" );
		
		return this;
		
	},
	
// method to show content selection
	showContentSelectionHandler : function( indexOfClickedLink ) {
		
		var receivedIndex;
		receivedIndex = indexOfClickedLink;
		
		if ( receivedIndex === this.configuration.indexForShowAllSelected ) {
			
			this.toggleAllSelected();
			
		} else {
			
			this.showSelectionAtIndex( receivedIndex - this.configuration.indexOffsetForContent )
				.showAllSelectedButtonToggleStyle( "displayShowAll" );
			
		}
		
		return this;
		
	},
	
// method to show selection at a given index
	showSelectionAtIndex : function( index ) {
		
		var receivedIndex;
		receivedIndex = index;
		
		// hide them all, then show the one we want
		$( this.domSelections.contentSections ).hide().eq( receivedIndex ).show();
		
		// style the selected section in the navigation
		this.styleCurrentSelection( receivedIndex );
		
		return this;
		
	},
	
// method to toggle show all or selected
	toggleAllSelected : function() {
		
		var showAllSelectedButton,
			indexOfSelectedSection;
		
		indexOfSelectedSection	= $( this.domSelections.navigationLinks ).index( $( ".apiDocumentationNavigationCurrent" ) );
		showAllSelectedButton	= $( "#apiNavigationShowAllSelectedButton" );
		
		if ( showAllSelectedButton.hasClass( "apiNavigationShowingAll" ) === true ) {
				
			// show selected
			$( this.domSelections.contentSections ).hide();
			this.showSelectionAtIndex( indexOfSelectedSection - this.configuration.indexOffsetForContent );
				
		} else if ( showAllSelectedButton.hasClass( "apiNavigationShowingSelected" ) === true ) {
				
			// show all
			$( this.domSelections.contentSections ).show();
				
		}
		
		this.showAllSelectedButtonToggleStyle();
			
		return this;
			
	},	

// method to toggle style of show all/selected button
	showAllSelectedButtonToggleStyle : function( overRideToggle ) {
		
		// standard toggle behaviour can be overridden to explicitly set style to one state or the other
		// do this by passing either "displayShowSelected" or "displayShowAll" when you call this method
		
		var showingAll,
			overRideText;
			
		showingAll 		= $( this.domSelections.showAllSelectedButton ).hasClass( "apiNavigationShowingAll" );
		overRideText	= overRideToggle;
		
		if ( 	( overRideText === "displayShowAll" ) || 
				( showingAll === true && overRideText !== "displayShowSelected" ) ) {
			
			$( this.domSelections.showAllSelectedButton ).addClass( "apiNavigationShowingSelected" )
				.removeClass( "apiNavigationShowingAll" )
				/*.html( "Show all" )*/;
			
		} else if (	( overRideText === "displayShowSelected" ) || 
					( showingAll === false && overRideText !== "displayShowAll" ) ) {
			
			$( this.domSelections.showAllSelectedButton ).addClass( "apiNavigationShowingAll" )
				.removeClass( "apiNavigationShowingSelected" )
				/*.html( "Show selected" )*/;
			
		}
		
		return this;
		
	},
	
// method to make the api documentation navigation stick and unstick to the top of the window
	stickUnstickApiNavigation : function() {
		
		var stickyElement,
			stickyElementHeight,
			stickyElementOffsetObject,
			stickyElementOffsetTop,
			elementToInsert,
			flowPlaceHolder;
			
		stickyElement 				= apiDocumentation.domSelections.navigationContainer;
		stickyElementHeight			= $( stickyElement ).outerHeight( true );
		stickyElementOffsetObject	= $( stickyElement ).offset();
		stickyElementOffsetTop		= stickyElementOffsetObject.top;
		elementToInsert				= '<div id="documentationNavigationflowPlaceHolder"></div>';
		
		$( window ).scroll( function ( event ) { 
			
			if ( $( this ).scrollTop() > stickyElementOffsetTop && !$( stickyElement ).hasClass( "stuck" ) ) { 
		
   				$( stickyElement ).addClass( "stuck" ); 
				$( elementToInsert ).insertBefore( stickyElement );
				
				flowPlaceHolder	= $( "#documentationNavigationflowPlaceHolder" );
				
				$( flowPlaceHolder ).height( stickyElementHeight );
				
  			} else if ( $(this).scrollTop() <= stickyElementOffsetTop && $( stickyElement ).hasClass( "stuck" ) ) { 
		
   				$( flowPlaceHolder ).remove();
				$( stickyElement ).removeClass( "stuck" ); 
				
  			}	
		});

	return this;
		
	},
	
// method to scroll to position
	scrollToPosition : function( scrollPosition ) {
		
		// if no value is passed, we use the top of the navigation element, as defined in this object's set up,
		// as the scroll position of the window { + 1 so the navigation is already stuck - it looks better that way }
		
		var receivedScrollPosition;
		
		receivedScrollPosition = scrollPosition;
		
		if ( !receivedScrollPosition ) {
		
			receivedScrollPosition = apiDocumentation.constants.stickyElementOffsetTop + 1;	
			
		}
		
		$( "html, body" ).animate({ scrollTop: receivedScrollPosition }, "fast" );
		
	}	
};

/**************************************

	Ready event

 **************************************/

$( document ).ready( function() {
	
/*** should remove this and any firebug console logs before live deployment ***/
	//try { console.assert(1); } catch(e) { console = { log: function() {}, assert: function() {} }; }
/******************************************************************************/
	
	visitorsCountry.getCountry();
	localiseLinks.setUp();
	searchBox.setUp().bindEventFunctions().insertDefaultText( "bothNow" );
	footerBoxes.setUp().bindEventFunctions();
	disclosureOne.setUp();
	disclosureTwo.setUp();
	timeline.setUp();
	apiDocumentation.setUp();
	
	return true;
							  
});

} ( jQuery ));
