Best Python code snippet using fMBT_python
history.js
Source:history.js  
1/**2 * History.js Core3 * @author Benjamin Arthur Lupton <contact@balupton.com>4 * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>5 * @license New BSD License <http://creativecommons.org/licenses/BSD/>6 */7(function(window,undefined){8	"use strict";9	// ========================================================================10	// Initialise11	// Localise Globals12	var13		console = window.console||undefined, // Prevent a JSLint complain14		document = window.document, // Make sure we are using the correct document15		navigator = window.navigator, // Make sure we are using the correct navigator16		sessionStorage = window.sessionStorage||false, // sessionStorage17		setTimeout = window.setTimeout,18		clearTimeout = window.clearTimeout,19		setInterval = window.setInterval,20		clearInterval = window.clearInterval,21		JSON = window.JSON,22		alert = window.alert,23		History = window.History = window.History||{}, // Public History Object24		history = window.history; // Old History Object25	// MooTools Compatibility26	JSON.stringify = JSON.stringify||JSON.encode;27	JSON.parse = JSON.parse||JSON.decode;28	// Check Existence29	if ( typeof History.init !== 'undefined' ) {30		throw new Error('History.js Core has already been loaded...');31	}32	// Initialise History33	History.init = function(){34		// Check Load Status of Adapter35		if ( typeof History.Adapter === 'undefined' ) {36			return false;37		}38		// Check Load Status of Core39		if ( typeof History.initCore !== 'undefined' ) {40			History.initCore();41		}42		// Check Load Status of HTML4 Support43		if ( typeof History.initHtml4 !== 'undefined' ) {44			History.initHtml4();45		}46		// Return true47		return true;48	};49	// ========================================================================50	// Initialise Core51	// Initialise Core52	History.initCore = function(){53		// Initialise54		if ( typeof History.initCore.initialized !== 'undefined' ) {55			// Already Loaded56			return false;57		}58		else {59			History.initCore.initialized = true;60		}61		// ====================================================================62		// Options63		/**64		 * History.options65		 * Configurable options66		 */67		History.options = History.options||{};68		/**69		 * History.options.hashChangeInterval70		 * How long should the interval be before hashchange checks71		 */72		History.options.hashChangeInterval = History.options.hashChangeInterval || 100;73		/**74		 * History.options.safariPollInterval75		 * How long should the interval be before safari poll checks76		 */77		History.options.safariPollInterval = History.options.safariPollInterval || 500;78		/**79		 * History.options.doubleCheckInterval80		 * How long should the interval be before we perform a double check81		 */82		History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500;83		/**84		 * History.options.storeInterval85		 * How long should we wait between store calls86		 */87		History.options.storeInterval = History.options.storeInterval || 1000;88		/**89		 * History.options.busyDelay90		 * How long should we wait between busy events91		 */92		History.options.busyDelay = History.options.busyDelay || 250;93		/**94		 * History.options.debug95		 * If true will enable debug messages to be logged96		 */97		History.options.debug = History.options.debug || false;98		/**99		 * History.options.initialTitle100		 * What is the title of the initial state101		 */102		History.options.initialTitle = History.options.initialTitle || document.title;103		// ====================================================================104		// Interval record105		/**106		 * History.intervalList107		 * List of intervals set, to be cleared when document is unloaded.108		 */109		History.intervalList = [];110		/**111		 * History.clearAllIntervals112		 * Clears all setInterval instances.113		 */114		History.clearAllIntervals = function(){115			var i, il = History.intervalList;116			if (typeof il !== "undefined" && il !== null) {117				for (i = 0; i < il.length; i++) {118					clearInterval(il[i]);119				}120				History.intervalList = null;121			}122		};123		// ====================================================================124		// Debug125		/**126		 * History.debug(message,...)127		 * Logs the passed arguments if debug enabled128		 */129		History.debug = function(){130			if ( (History.options.debug||false) ) {131				History.log.apply(History,arguments);132			}133		};134		/**135		 * History.log(message,...)136		 * Logs the passed arguments137		 */138		History.log = function(){139			// Prepare140			var141				consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'),142				textarea = document.getElementById('log'),143				message,144				i,n,145				args,arg146				;147			// Write to Console148			if ( consoleExists ) {149				args = Array.prototype.slice.call(arguments);150				message = args.shift();151				if ( typeof console.debug !== 'undefined' ) {152					console.debug.apply(console,[message,args]);153				}154				else {155					console.log.apply(console,[message,args]);156				}157			}158			else {159				message = ("\n"+arguments[0]+"\n");160			}161			// Write to log162			for ( i=1,n=arguments.length; i<n; ++i ) {163				arg = arguments[i];164				if ( typeof arg === 'object' && typeof JSON !== 'undefined' ) {165					try {166						arg = JSON.stringify(arg);167					}168					catch ( Exception ) {169						// Recursive Object170					}171				}172				message += "\n"+arg+"\n";173			}174			// Textarea175			if ( textarea ) {176				textarea.value += message+"\n-----\n";177				textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;178			}179			// No Textarea, No Console180			else if ( !consoleExists ) {181				alert(message);182			}183			// Return true184			return true;185		};186		// ====================================================================187		// Emulated Status188		/**189		 * History.getInternetExplorerMajorVersion()190		 * Get's the major version of Internet Explorer191		 * @return {integer}192		 * @license Public Domain193		 * @author Benjamin Arthur Lupton <contact@balupton.com>194		 * @author James Padolsey <https://gist.github.com/527683>195		 */196		History.getInternetExplorerMajorVersion = function(){197			var result = History.getInternetExplorerMajorVersion.cached =198					(typeof History.getInternetExplorerMajorVersion.cached !== 'undefined')199				?	History.getInternetExplorerMajorVersion.cached200				:	(function(){201						var v = 3,202								div = document.createElement('div'),203								all = div.getElementsByTagName('i');204						while ( (div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->') && all[0] ) {}205						return (v > 4) ? v : false;206					})()207				;208			return result;209		};210		/**211		 * History.isInternetExplorer()212		 * Are we using Internet Explorer?213		 * @return {boolean}214		 * @license Public Domain215		 * @author Benjamin Arthur Lupton <contact@balupton.com>216		 */217		History.isInternetExplorer = function(){218			var result =219				History.isInternetExplorer.cached =220				(typeof History.isInternetExplorer.cached !== 'undefined')221					?	History.isInternetExplorer.cached222					:	Boolean(History.getInternetExplorerMajorVersion())223				;224			return result;225		};226		/**227		 * History.emulated228		 * Which features require emulating?229		 */230		History.emulated = {231			pushState: !Boolean(232				window.history && window.history.pushState && window.history.replaceState233				&& !(234					(/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */235					|| (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */236				)237			),238			hashChange: Boolean(239				!(('onhashchange' in window) || ('onhashchange' in document))240				||241				(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8)242			)243		};244		/**245		 * History.enabled246		 * Is History enabled?247		 */248		History.enabled = !History.emulated.pushState;249		/**250		 * History.bugs251		 * Which bugs are present252		 */253		History.bugs = {254			/**255			 * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call256			 * https://bugs.webkit.org/show_bug.cgi?id=56249257			 */258			setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),259			/**260			 * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions261			 * https://bugs.webkit.org/show_bug.cgi?id=42940262			 */263			safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)),264			/**265			 * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function)266			 */267			ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8),268			/**269			 * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event270			 */271			hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7)272		};273		/**274		 * History.isEmptyObject(obj)275		 * Checks to see if the Object is Empty276		 * @param {Object} obj277		 * @return {boolean}278		 */279		History.isEmptyObject = function(obj) {280			for ( var name in obj ) {281				return false;282			}283			return true;284		};285		/**286		 * History.cloneObject(obj)287		 * Clones a object and eliminate all references to the original contexts288		 * @param {Object} obj289		 * @return {Object}290		 */291		History.cloneObject = function(obj) {292			var hash,newObj;293			if ( obj ) {294				hash = JSON.stringify(obj);295				newObj = JSON.parse(hash);296			}297			else {298				newObj = {};299			}300			return newObj;301		};302		// ====================================================================303		// URL Helpers304		/**305		 * History.getRootUrl()306		 * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com"307		 * @return {String} rootUrl308		 */309		History.getRootUrl = function(){310			// Create311			var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host);312			if ( document.location.port||false ) {313				rootUrl += ':'+document.location.port;314			}315			rootUrl += '/';316			// Return317			return rootUrl;318		};319		/**320		 * History.getBaseHref()321		 * Fetches the `href` attribute of the `<base href="...">` element if it exists322		 * @return {String} baseHref323		 */324		History.getBaseHref = function(){325			// Create326			var327				baseElements = document.getElementsByTagName('base'),328				baseElement = null,329				baseHref = '';330			// Test for Base Element331			if ( baseElements.length === 1 ) {332				// Prepare for Base Element333				baseElement = baseElements[0];334				baseHref = baseElement.href.replace(/[^\/]+$/,'');335			}336			// Adjust trailing slash337			baseHref = baseHref.replace(/\/+$/,'');338			if ( baseHref ) baseHref += '/';339			// Return340			return baseHref;341		};342		/**343		 * History.getBaseUrl()344		 * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first)345		 * @return {String} baseUrl346		 */347		History.getBaseUrl = function(){348			// Create349			var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl();350			// Return351			return baseUrl;352		};353		/**354		 * History.getPageUrl()355		 * Fetches the URL of the current page356		 * @return {String} pageUrl357		 */358		History.getPageUrl = function(){359			// Fetch360			var361				State = History.getState(false,false),362				stateUrl = (State||{}).url||document.location.href,363				pageUrl;364			// Create365			pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){366				return (/\./).test(part) ? part : part+'/';367			});368			// Return369			return pageUrl;370		};371		/**372		 * History.getBasePageUrl()373		 * Fetches the Url of the directory of the current page374		 * @return {String} basePageUrl375		 */376		History.getBasePageUrl = function(){377			// Create378			var basePageUrl = document.location.href.replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){379				return (/[^\/]$/).test(part) ? '' : part;380			}).replace(/\/+$/,'')+'/';381			// Return382			return basePageUrl;383		};384		/**385		 * History.getFullUrl(url)386		 * Ensures that we have an absolute URL and not a relative URL387		 * @param {string} url388		 * @param {Boolean} allowBaseHref389		 * @return {string} fullUrl390		 */391		History.getFullUrl = function(url,allowBaseHref){392			// Prepare393			var fullUrl = url, firstChar = url.substring(0,1);394			allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref;395			// Check396			if ( /[a-z]+\:\/\//.test(url) ) {397				// Full URL398			}399			else if ( firstChar === '/' ) {400				// Root URL401				fullUrl = History.getRootUrl()+url.replace(/^\/+/,'');402			}403			else if ( firstChar === '#' ) {404				// Anchor URL405				fullUrl = History.getPageUrl().replace(/#.*/,'')+url;406			}407			else if ( firstChar === '?' ) {408				// Query URL409				fullUrl = History.getPageUrl().replace(/[\?#].*/,'')+url;410			}411			else {412				// Relative URL413				if ( allowBaseHref ) {414					fullUrl = History.getBaseUrl()+url.replace(/^(\.\/)+/,'');415				} else {416					fullUrl = History.getBasePageUrl()+url.replace(/^(\.\/)+/,'');417				}418				// We have an if condition above as we do not want hashes419				// which are relative to the baseHref in our URLs420				// as if the baseHref changes, then all our bookmarks421				// would now point to different locations422				// whereas the basePageUrl will always stay the same423			}424			// Return425			return fullUrl.replace(/\#$/,'');426		};427		/**428		 * History.getShortUrl(url)429		 * Ensures that we have a relative URL and not a absolute URL430		 * @param {string} url431		 * @return {string} url432		 */433		History.getShortUrl = function(url){434			// Prepare435			var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl();436			// Trim baseUrl437			if ( History.emulated.pushState ) {438				// We are in a if statement as when pushState is not emulated439				// The actual url these short urls are relative to can change440				// So within the same session, we the url may end up somewhere different441				shortUrl = shortUrl.replace(baseUrl,'');442			}443			// Trim rootUrl444			shortUrl = shortUrl.replace(rootUrl,'/');445			// Ensure we can still detect it as a state446			if ( History.isTraditionalAnchor(shortUrl) ) {447				shortUrl = './'+shortUrl;448			}449			// Clean It450			shortUrl = shortUrl.replace(/^(\.\/)+/g,'./').replace(/\#$/,'');451			// Return452			return shortUrl;453		};454		// ====================================================================455		// State Storage456		/**457		 * History.store458		 * The store for all session specific data459		 */460		History.store = {};461		/**462		 * History.idToState463		 * 1-1: State ID to State Object464		 */465		History.idToState = History.idToState||{};466		/**467		 * History.stateToId468		 * 1-1: State String to State ID469		 */470		History.stateToId = History.stateToId||{};471		/**472		 * History.urlToId473		 * 1-1: State URL to State ID474		 */475		History.urlToId = History.urlToId||{};476		/**477		 * History.storedStates478		 * Store the states in an array479		 */480		History.storedStates = History.storedStates||[];481		/**482		 * History.savedStates483		 * Saved the states in an array484		 */485		History.savedStates = History.savedStates||[];486		/**487		 * History.noramlizeStore()488		 * Noramlize the store by adding necessary values489		 */490		History.normalizeStore = function(){491			History.store.idToState = History.store.idToState||{};492			History.store.urlToId = History.store.urlToId||{};493			History.store.stateToId = History.store.stateToId||{};494		};495		/**496		 * History.getState()497		 * Get an object containing the data, title and url of the current state498		 * @param {Boolean} friendly499		 * @param {Boolean} create500		 * @return {Object} State501		 */502		History.getState = function(friendly,create){503			// Prepare504			if ( typeof friendly === 'undefined' ) { friendly = true; }505			if ( typeof create === 'undefined' ) { create = true; }506			// Fetch507			var State = History.getLastSavedState();508			// Create509			if ( !State && create ) {510				State = History.createStateObject();511			}512			// Adjust513			if ( friendly ) {514				State = History.cloneObject(State);515				State.url = State.cleanUrl||State.url;516			}517			// Return518			return State;519		};520		/**521		 * History.getIdByState(State)522		 * Gets a ID for a State523		 * @param {State} newState524		 * @return {String} id525		 */526		History.getIdByState = function(newState){527			// Fetch ID528			var id = History.extractId(newState.url),529				str;530			531			if ( !id ) {532				// Find ID via State String533				str = History.getStateString(newState);534				if ( typeof History.stateToId[str] !== 'undefined' ) {535					id = History.stateToId[str];536				}537				else if ( typeof History.store.stateToId[str] !== 'undefined' ) {538					id = History.store.stateToId[str];539				}540				else {541					// Generate a new ID542					while ( true ) {543						id = (new Date()).getTime() + String(Math.random()).replace(/\D/g,'');544						if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) {545							break;546						}547					}548					// Apply the new State to the ID549					History.stateToId[str] = id;550					History.idToState[id] = newState;551				}552			}553			// Return ID554			return id;555		};556		/**557		 * History.normalizeState(State)558		 * Expands a State Object559		 * @param {object} State560		 * @return {object}561		 */562		History.normalizeState = function(oldState){563			// Variables564			var newState, dataNotEmpty;565			// Prepare566			if ( !oldState || (typeof oldState !== 'object') ) {567				oldState = {};568			}569			// Check570			if ( typeof oldState.normalized !== 'undefined' ) {571				return oldState;572			}573			// Adjust574			if ( !oldState.data || (typeof oldState.data !== 'object') ) {575				oldState.data = {};576			}577			// ----------------------------------------------------------------578			// Create579			newState = {};580			newState.normalized = true;581			newState.title = oldState.title||'';582			newState.url = History.getFullUrl(History.unescapeString(oldState.url||document.location.href));583			newState.hash = History.getShortUrl(newState.url);584			newState.data = History.cloneObject(oldState.data);585			// Fetch ID586			newState.id = History.getIdByState(newState);587			// ----------------------------------------------------------------588			// Clean the URL589			newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,'');590			newState.url = newState.cleanUrl;591			// Check to see if we have more than just a url592			dataNotEmpty = !History.isEmptyObject(newState.data);593			// Apply594			if ( newState.title || dataNotEmpty ) {595				// Add ID to Hash596				newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,'');597				if ( !/\?/.test(newState.hash) ) {598					newState.hash += '?';599				}600				newState.hash += '&_suid='+newState.id;601			}602			// Create the Hashed URL603			newState.hashedUrl = History.getFullUrl(newState.hash);604			// ----------------------------------------------------------------605			// Update the URL if we have a duplicate606			if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) {607				newState.url = newState.hashedUrl;608			}609			// ----------------------------------------------------------------610			// Return611			return newState;612		};613		/**614		 * History.createStateObject(data,title,url)615		 * Creates a object based on the data, title and url state params616		 * @param {object} data617		 * @param {string} title618		 * @param {string} url619		 * @return {object}620		 */621		History.createStateObject = function(data,title,url){622			// Hashify623			var State = {624				'data': data,625				'title': title,626				'url': url627			};628			// Expand the State629			State = History.normalizeState(State);630			// Return object631			return State;632		};633		/**634		 * History.getStateById(id)635		 * Get a state by it's UID636		 * @param {String} id637		 */638		History.getStateById = function(id){639			// Prepare640			id = String(id);641			// Retrieve642			var State = History.idToState[id] || History.store.idToState[id] || undefined;643			// Return State644			return State;645		};646		/**647		 * Get a State's String648		 * @param {State} passedState649		 */650		History.getStateString = function(passedState){651			// Prepare652			var State, cleanedState, str;653			// Fetch654			State = History.normalizeState(passedState);655			// Clean656			cleanedState = {657				data: State.data,658				title: passedState.title,659				url: passedState.url660			};661			// Fetch662			str = JSON.stringify(cleanedState);663			// Return664			return str;665		};666		/**667		 * Get a State's ID668		 * @param {State} passedState669		 * @return {String} id670		 */671		History.getStateId = function(passedState){672			// Prepare673			var State, id;674			675			// Fetch676			State = History.normalizeState(passedState);677			// Fetch678			id = State.id;679			// Return680			return id;681		};682		/**683		 * History.getHashByState(State)684		 * Creates a Hash for the State Object685		 * @param {State} passedState686		 * @return {String} hash687		 */688		History.getHashByState = function(passedState){689			// Prepare690			var State, hash;691			692			// Fetch693			State = History.normalizeState(passedState);694			// Hash695			hash = State.hash;696			// Return697			return hash;698		};699		/**700		 * History.extractId(url_or_hash)701		 * Get a State ID by it's URL or Hash702		 * @param {string} url_or_hash703		 * @return {string} id704		 */705		History.extractId = function ( url_or_hash ) {706			// Prepare707			var id,parts,url;708			// Extract709			parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash);710			url = parts ? (parts[1]||url_or_hash) : url_or_hash;711			id = parts ? String(parts[2]||'') : '';712			// Return713			return id||false;714		};715		/**716		 * History.isTraditionalAnchor717		 * Checks to see if the url is a traditional anchor or not718		 * @param {String} url_or_hash719		 * @return {Boolean}720		 */721		History.isTraditionalAnchor = function(url_or_hash){722			// Check723			var isTraditional = !(/[\/\?\.]/.test(url_or_hash));724			// Return725			return isTraditional;726		};727		/**728		 * History.extractState729		 * Get a State by it's URL or Hash730		 * @param {String} url_or_hash731		 * @return {State|null}732		 */733		History.extractState = function(url_or_hash,create){734			// Prepare735			var State = null, id, url;736			create = create||false;737			// Fetch SUID738			id = History.extractId(url_or_hash);739			if ( id ) {740				State = History.getStateById(id);741			}742			// Fetch SUID returned no State743			if ( !State ) {744				// Fetch URL745				url = History.getFullUrl(url_or_hash);746				// Check URL747				id = History.getIdByUrl(url)||false;748				if ( id ) {749					State = History.getStateById(id);750				}751				// Create State752				if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) {753					State = History.createStateObject(null,null,url);754				}755			}756			// Return757			return State;758		};759		/**760		 * History.getIdByUrl()761		 * Get a State ID by a State URL762		 */763		History.getIdByUrl = function(url){764			// Fetch765			var id = History.urlToId[url] || History.store.urlToId[url] || undefined;766			// Return767			return id;768		};769		/**770		 * History.getLastSavedState()771		 * Get an object containing the data, title and url of the current state772		 * @return {Object} State773		 */774		History.getLastSavedState = function(){775			return History.savedStates[History.savedStates.length-1]||undefined;776		};777		/**778		 * History.getLastStoredState()779		 * Get an object containing the data, title and url of the current state780		 * @return {Object} State781		 */782		History.getLastStoredState = function(){783			return History.storedStates[History.storedStates.length-1]||undefined;784		};785		/**786		 * History.hasUrlDuplicate787		 * Checks if a Url will have a url conflict788		 * @param {Object} newState789		 * @return {Boolean} hasDuplicate790		 */791		History.hasUrlDuplicate = function(newState) {792			// Prepare793			var hasDuplicate = false,794				oldState;795			// Fetch796			oldState = History.extractState(newState.url);797			// Check798			hasDuplicate = oldState && oldState.id !== newState.id;799			// Return800			return hasDuplicate;801		};802		/**803		 * History.storeState804		 * Store a State805		 * @param {Object} newState806		 * @return {Object} newState807		 */808		History.storeState = function(newState){809			// Store the State810			History.urlToId[newState.url] = newState.id;811			// Push the State812			History.storedStates.push(History.cloneObject(newState));813			// Return newState814			return newState;815		};816		/**817		 * History.isLastSavedState(newState)818		 * Tests to see if the state is the last state819		 * @param {Object} newState820		 * @return {boolean} isLast821		 */822		History.isLastSavedState = function(newState){823			// Prepare824			var isLast = false,825				newId, oldState, oldId;826			// Check827			if ( History.savedStates.length ) {828				newId = newState.id;829				oldState = History.getLastSavedState();830				oldId = oldState.id;831				// Check832				isLast = (newId === oldId);833			}834			// Return835			return isLast;836		};837		/**838		 * History.saveState839		 * Push a State840		 * @param {Object} newState841		 * @return {boolean} changed842		 */843		History.saveState = function(newState){844			// Check Hash845			if ( History.isLastSavedState(newState) ) {846				return false;847			}848			// Push the State849			History.savedStates.push(History.cloneObject(newState));850			// Return true851			return true;852		};853		/**854		 * History.getStateByIndex()855		 * Gets a state by the index856		 * @param {integer} index857		 * @return {Object}858		 */859		History.getStateByIndex = function(index){860			// Prepare861			var State = null;862			// Handle863			if ( typeof index === 'undefined' ) {864				// Get the last inserted865				State = History.savedStates[History.savedStates.length-1];866			}867			else if ( index < 0 ) {868				// Get from the end869				State = History.savedStates[History.savedStates.length+index];870			}871			else {872				// Get from the beginning873				State = History.savedStates[index];874			}875			// Return State876			return State;877		};878		// ====================================================================879		// Hash Helpers880		/**881		 * History.getHash()882		 * Gets the current document hash883		 * @return {string}884		 */885		History.getHash = function(){886			var hash = History.unescapeHash(document.location.hash);887			return hash;888		};889		/**890		 * History.unescapeString()891		 * Unescape a string892		 * @param {String} str893		 * @return {string}894		 */895		History.unescapeString = function(str){896			// Prepare897			var result = str,898				tmp;899			// Unescape hash900			while ( true ) {901				tmp = window.unescape(result);902				if ( tmp === result ) {903					break;904				}905				result = tmp;906			}907			// Return result908			return result;909		};910		/**911		 * History.unescapeHash()912		 * normalize and Unescape a Hash913		 * @param {String} hash914		 * @return {string}915		 */916		History.unescapeHash = function(hash){917			// Prepare918			var result = History.normalizeHash(hash);919			// Unescape hash920			result = History.unescapeString(result);921			// Return result922			return result;923		};924		/**925		 * History.normalizeHash()926		 * normalize a hash across browsers927		 * @return {string}928		 */929		History.normalizeHash = function(hash){930			// Prepare931			var result = hash.replace(/[^#]*#/,'').replace(/#.*/, '');932			// Return result933			return result;934		};935		/**936		 * History.setHash(hash)937		 * Sets the document hash938		 * @param {string} hash939		 * @return {History}940		 */941		History.setHash = function(hash,queue){942			// Prepare943			var adjustedHash, State, pageUrl;944			// Handle Queueing945			if ( queue !== false && History.busy() ) {946				// Wait + Push to Queue947				//History.debug('History.setHash: we must wait', arguments);948				History.pushQueue({949					scope: History,950					callback: History.setHash,951					args: arguments,952					queue: queue953				});954				return false;955			}956			// Log957			//History.debug('History.setHash: called',hash);958			// Prepare959			adjustedHash = History.escapeHash(hash);960			// Make Busy + Continue961			History.busy(true);962			// Check if hash is a state963			State = History.extractState(hash,true);964			if ( State && !History.emulated.pushState ) {965				// Hash is a state so skip the setHash966				//History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments);967				// PushState968				History.pushState(State.data,State.title,State.url,false);969			}970			else if ( document.location.hash !== adjustedHash ) {971				// Hash is a proper hash, so apply it972				// Handle browser bugs973				if ( History.bugs.setHash ) {974					// Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249975					// Fetch the base page976					pageUrl = History.getPageUrl();977					// Safari hash apply978					History.pushState(null,null,pageUrl+'#'+adjustedHash,false);979				}980				else {981					// Normal hash apply982					document.location.hash = adjustedHash;983				}984			}985			// Chain986			return History;987		};988		/**989		 * History.escape()990		 * normalize and Escape a Hash991		 * @return {string}992		 */993		History.escapeHash = function(hash){994			// Prepare995			var result = History.normalizeHash(hash);996			// Escape hash997			result = window.escape(result);998			// IE6 Escape Bug999			if ( !History.bugs.hashEscape ) {1000				// Restore common parts1001				result = result1002					.replace(/\%21/g,'!')1003					.replace(/\%26/g,'&')1004					.replace(/\%3D/g,'=')1005					.replace(/\%3F/g,'?');1006			}1007			// Return result1008			return result;1009		};1010		/**1011		 * History.getHashByUrl(url)1012		 * Extracts the Hash from a URL1013		 * @param {string} url1014		 * @return {string} url1015		 */1016		History.getHashByUrl = function(url){1017			// Extract the hash1018			var hash = String(url)1019				.replace(/([^#]*)#?([^#]*)#?(.*)/, '$2')1020				;1021			// Unescape hash1022			hash = History.unescapeHash(hash);1023			// Return hash1024			return hash;1025		};1026		/**1027		 * History.setTitle(title)1028		 * Applies the title to the document1029		 * @param {State} newState1030		 * @return {Boolean}1031		 */1032		History.setTitle = function(newState){1033			// Prepare1034			var title = newState.title,1035				firstState;1036			// Initial1037			if ( !title ) {1038				firstState = History.getStateByIndex(0);1039				if ( firstState && firstState.url === newState.url ) {1040					title = firstState.title||History.options.initialTitle;1041				}1042			}1043			// Apply1044			try {1045				document.getElementsByTagName('title')[0].innerHTML = title.replace('<','<').replace('>','>').replace(' & ',' & ');1046			}1047			catch ( Exception ) { }1048			document.title = title;1049			// Chain1050			return History;1051		};1052		// ====================================================================1053		// Queueing1054		/**1055		 * History.queues1056		 * The list of queues to use1057		 * First In, First Out1058		 */1059		History.queues = [];1060		/**1061		 * History.busy(value)1062		 * @param {boolean} value [optional]1063		 * @return {boolean} busy1064		 */1065		History.busy = function(value){1066			// Apply1067			if ( typeof value !== 'undefined' ) {1068				//History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length);1069				History.busy.flag = value;1070			}1071			// Default1072			else if ( typeof History.busy.flag === 'undefined' ) {1073				History.busy.flag = false;1074			}1075			// Queue1076			if ( !History.busy.flag ) {1077				// Execute the next item in the queue1078				clearTimeout(History.busy.timeout);1079				var fireNext = function(){1080					var i, queue, item;1081					if ( History.busy.flag ) return;1082					for ( i=History.queues.length-1; i >= 0; --i ) {1083						queue = History.queues[i];1084						if ( queue.length === 0 ) continue;1085						item = queue.shift();1086						History.fireQueueItem(item);1087						History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);1088					}1089				};1090				History.busy.timeout = setTimeout(fireNext,History.options.busyDelay);1091			}1092			// Return1093			return History.busy.flag;1094		};1095		/**1096		 * History.busy.flag1097		 */1098		History.busy.flag = false;1099		/**1100		 * History.fireQueueItem(item)1101		 * Fire a Queue Item1102		 * @param {Object} item1103		 * @return {Mixed} result1104		 */1105		History.fireQueueItem = function(item){1106			return item.callback.apply(item.scope||History,item.args||[]);1107		};1108		/**1109		 * History.pushQueue(callback,args)1110		 * Add an item to the queue1111		 * @param {Object} item [scope,callback,args,queue]1112		 */1113		History.pushQueue = function(item){1114			// Prepare the queue1115			History.queues[item.queue||0] = History.queues[item.queue||0]||[];1116			// Add to the queue1117			History.queues[item.queue||0].push(item);1118			// Chain1119			return History;1120		};1121		/**1122		 * History.queue (item,queue), (func,queue), (func), (item)1123		 * Either firs the item now if not busy, or adds it to the queue1124		 */1125		History.queue = function(item,queue){1126			// Prepare1127			if ( typeof item === 'function' ) {1128				item = {1129					callback: item1130				};1131			}1132			if ( typeof queue !== 'undefined' ) {1133				item.queue = queue;1134			}1135			// Handle1136			if ( History.busy() ) {1137				History.pushQueue(item);1138			} else {1139				History.fireQueueItem(item);1140			}1141			// Chain1142			return History;1143		};1144		/**1145		 * History.clearQueue()1146		 * Clears the Queue1147		 */1148		History.clearQueue = function(){1149			History.busy.flag = false;1150			History.queues = [];1151			return History;1152		};1153		// ====================================================================1154		// IE Bug Fix1155		/**1156		 * History.stateChanged1157		 * States whether or not the state has changed since the last double check was initialised1158		 */1159		History.stateChanged = false;1160		/**1161		 * History.doubleChecker1162		 * Contains the timeout used for the double checks1163		 */1164		History.doubleChecker = false;1165		/**1166		 * History.doubleCheckComplete()1167		 * Complete a double check1168		 * @return {History}1169		 */1170		History.doubleCheckComplete = function(){1171			// Update1172			History.stateChanged = true;1173			// Clear1174			History.doubleCheckClear();1175			// Chain1176			return History;1177		};1178		/**1179		 * History.doubleCheckClear()1180		 * Clear a double check1181		 * @return {History}1182		 */1183		History.doubleCheckClear = function(){1184			// Clear1185			if ( History.doubleChecker ) {1186				clearTimeout(History.doubleChecker);1187				History.doubleChecker = false;1188			}1189			// Chain1190			return History;1191		};1192		/**1193		 * History.doubleCheck()1194		 * Create a double check1195		 * @return {History}1196		 */1197		History.doubleCheck = function(tryAgain){1198			// Reset1199			History.stateChanged = false;1200			History.doubleCheckClear();1201			// Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does)1202			// Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=429401203			if ( History.bugs.ieDoubleCheck ) {1204				// Apply Check1205				History.doubleChecker = setTimeout(1206					function(){1207						History.doubleCheckClear();1208						if ( !History.stateChanged ) {1209							//History.debug('History.doubleCheck: State has not yet changed, trying again', arguments);1210							// Re-Attempt1211							tryAgain();1212						}1213						return true;1214					},1215					History.options.doubleCheckInterval1216				);1217			}1218			// Chain1219			return History;1220		};1221		// ====================================================================1222		// Safari Bug Fix1223		/**1224		 * History.safariStatePoll()1225		 * Poll the current state1226		 * @return {History}1227		 */1228		History.safariStatePoll = function(){1229			// Poll the URL1230			// Get the Last State which has the new URL1231			var1232				urlState = History.extractState(document.location.href),1233				newState;1234			// Check for a difference1235			if ( !History.isLastSavedState(urlState) ) {1236				newState = urlState;1237			}1238			else {1239				return;1240			}1241			// Check if we have a state with that url1242			// If not create it1243			if ( !newState ) {1244				//History.debug('History.safariStatePoll: new');1245				newState = History.createStateObject();1246			}1247			// Apply the New State1248			//History.debug('History.safariStatePoll: trigger');1249			History.Adapter.trigger(window,'popstate');1250			// Chain1251			return History;1252		};1253		// ====================================================================1254		// State Aliases1255		/**1256		 * History.back(queue)1257		 * Send the browser history back one item1258		 * @param {Integer} queue [optional]1259		 */1260		History.back = function(queue){1261			//History.debug('History.back: called', arguments);1262			// Handle Queueing1263			if ( queue !== false && History.busy() ) {1264				// Wait + Push to Queue1265				//History.debug('History.back: we must wait', arguments);1266				History.pushQueue({1267					scope: History,1268					callback: History.back,1269					args: arguments,1270					queue: queue1271				});1272				return false;1273			}1274			// Make Busy + Continue1275			History.busy(true);1276			// Fix certain browser bugs that prevent the state from changing1277			History.doubleCheck(function(){1278				History.back(false);1279			});1280			// Go back1281			history.go(-1);1282			// End back closure1283			return true;1284		};1285		/**1286		 * History.forward(queue)1287		 * Send the browser history forward one item1288		 * @param {Integer} queue [optional]1289		 */1290		History.forward = function(queue){1291			//History.debug('History.forward: called', arguments);1292			// Handle Queueing1293			if ( queue !== false && History.busy() ) {1294				// Wait + Push to Queue1295				//History.debug('History.forward: we must wait', arguments);1296				History.pushQueue({1297					scope: History,1298					callback: History.forward,1299					args: arguments,1300					queue: queue1301				});1302				return false;1303			}1304			// Make Busy + Continue1305			History.busy(true);1306			// Fix certain browser bugs that prevent the state from changing1307			History.doubleCheck(function(){1308				History.forward(false);1309			});1310			// Go forward1311			history.go(1);1312			// End forward closure1313			return true;1314		};1315		/**1316		 * History.go(index,queue)1317		 * Send the browser history back or forward index times1318		 * @param {Integer} queue [optional]1319		 */1320		History.go = function(index,queue){1321			//History.debug('History.go: called', arguments);1322			// Prepare1323			var i;1324			// Handle1325			if ( index > 0 ) {1326				// Forward1327				for ( i=1; i<=index; ++i ) {1328					History.forward(queue);1329				}1330			}1331			else if ( index < 0 ) {1332				// Backward1333				for ( i=-1; i>=index; --i ) {1334					History.back(queue);1335				}1336			}1337			else {1338				throw new Error('History.go: History.go requires a positive or negative integer passed.');1339			}1340			// Chain1341			return History;1342		};1343		// ====================================================================1344		// HTML5 State Support1345		// Non-Native pushState Implementation1346		if ( History.emulated.pushState ) {1347			/*1348			 * Provide Skeleton for HTML4 Browsers1349			 */1350			// Prepare1351			var emptyFunction = function(){};1352			History.pushState = History.pushState||emptyFunction;1353			History.replaceState = History.replaceState||emptyFunction;1354		} // History.emulated.pushState1355		// Native pushState Implementation1356		else {1357			/*1358			 * Use native HTML5 History API Implementation1359			 */1360			/**1361			 * History.onPopState(event,extra)1362			 * Refresh the Current State1363			 */1364			History.onPopState = function(event,extra){1365				// Prepare1366				var stateId = false, newState = false, currentHash, currentState;1367				// Reset the double check1368				History.doubleCheckComplete();1369				// Check for a Hash, and handle apporiatly1370				currentHash	= History.getHash();1371				if ( currentHash ) {1372					// Expand Hash1373					currentState = History.extractState(currentHash||document.location.href,true);1374					if ( currentState ) {1375						// We were able to parse it, it must be a State!1376						// Let's forward to replaceState1377						//History.debug('History.onPopState: state anchor', currentHash, currentState);1378						History.replaceState(currentState.data, currentState.title, currentState.url, false);1379					}1380					else {1381						// Traditional Anchor1382						//History.debug('History.onPopState: traditional anchor', currentHash);1383						History.Adapter.trigger(window,'anchorchange');1384						History.busy(false);1385					}1386					// We don't care for hashes1387					History.expectedStateId = false;1388					return false;1389				}1390				// Ensure1391				stateId = History.Adapter.extractEventData('state',event,extra) || false;1392				// Fetch State1393				if ( stateId ) {1394					// Vanilla: Back/forward button was used1395					newState = History.getStateById(stateId);1396				}1397				else if ( History.expectedStateId ) {1398					// Vanilla: A new state was pushed, and popstate was called manually1399					newState = History.getStateById(History.expectedStateId);1400				}1401				else {1402					// Initial State1403					newState = History.extractState(document.location.href);1404				}1405				// The State did not exist in our store1406				if ( !newState ) {1407					// Regenerate the State1408					newState = History.createStateObject(null,null,document.location.href);1409				}1410				// Clean1411				History.expectedStateId = false;1412				// Check if we are the same state1413				if ( History.isLastSavedState(newState) ) {1414					// There has been no change (just the page's hash has finally propagated)1415					//History.debug('History.onPopState: no change', newState, History.savedStates);1416					History.busy(false);1417					return false;1418				}1419				// Store the State1420				History.storeState(newState);1421				History.saveState(newState);1422				// Force update of the title1423				History.setTitle(newState);1424				// Fire Our Event1425				History.Adapter.trigger(window,'statechange');1426				History.busy(false);1427				// Return true1428				return true;1429			};1430			History.Adapter.bind(window,'popstate',History.onPopState);1431			/**1432			 * History.pushState(data,title,url)1433			 * Add a new State to the history object, become it, and trigger onpopstate1434			 * We have to trigger for HTML4 compatibility1435			 * @param {object} data1436			 * @param {string} title1437			 * @param {string} url1438			 * @return {true}1439			 */1440			History.pushState = function(data,title,url,queue){1441				//History.debug('History.pushState: called', arguments);1442				// Check the State1443				if ( History.getHashByUrl(url) && History.emulated.pushState ) {1444					throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');1445				}1446				// Handle Queueing1447				if ( queue !== false && History.busy() ) {1448					// Wait + Push to Queue1449					//History.debug('History.pushState: we must wait', arguments);1450					History.pushQueue({1451						scope: History,1452						callback: History.pushState,1453						args: arguments,1454						queue: queue1455					});1456					return false;1457				}1458				// Make Busy + Continue1459				History.busy(true);1460				// Create the newState1461				var newState = History.createStateObject(data,title,url);1462				// Check it1463				if ( History.isLastSavedState(newState) ) {1464					// Won't be a change1465					History.busy(false);1466				}1467				else {1468					// Store the newState1469					History.storeState(newState);1470					History.expectedStateId = newState.id;1471					// Push the newState1472					history.pushState(newState.id,newState.title,newState.url);1473					// Fire HTML5 Event1474					History.Adapter.trigger(window,'popstate');1475				}1476				// End pushState closure1477				return true;1478			};1479			/**1480			 * History.replaceState(data,title,url)1481			 * Replace the State and trigger onpopstate1482			 * We have to trigger for HTML4 compatibility1483			 * @param {object} data1484			 * @param {string} title1485			 * @param {string} url1486			 * @return {true}1487			 */1488			History.replaceState = function(data,title,url,queue){1489				//History.debug('History.replaceState: called', arguments);1490				// Check the State1491				if ( History.getHashByUrl(url) && History.emulated.pushState ) {1492					throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');1493				}1494				// Handle Queueing1495				if ( queue !== false && History.busy() ) {1496					// Wait + Push to Queue1497					//History.debug('History.replaceState: we must wait', arguments);1498					History.pushQueue({1499						scope: History,1500						callback: History.replaceState,1501						args: arguments,1502						queue: queue1503					});1504					return false;1505				}1506				// Make Busy + Continue1507				History.busy(true);1508				// Create the newState1509				var newState = History.createStateObject(data,title,url);1510				// Check it1511				if ( History.isLastSavedState(newState) ) {1512					// Won't be a change1513					History.busy(false);1514				}1515				else {1516					// Store the newState1517					History.storeState(newState);1518					History.expectedStateId = newState.id;1519					// Push the newState1520					history.replaceState(newState.id,newState.title,newState.url);1521					// Fire HTML5 Event1522					History.Adapter.trigger(window,'popstate');1523				}1524				// End replaceState closure1525				return true;1526			};1527		} // !History.emulated.pushState1528		// ====================================================================1529		// Initialise1530		/**1531		 * Load the Store1532		 */1533		if ( sessionStorage ) {1534			// Fetch1535			try {1536				History.store = JSON.parse(sessionStorage.getItem('History.store'))||{};1537			}1538			catch ( err ) {1539				History.store = {};1540			}1541			// Normalize1542			History.normalizeStore();1543		}1544		else {1545			// Default Load1546			History.store = {};1547			History.normalizeStore();1548		}1549		/**1550		 * Clear Intervals on exit to prevent memory leaks1551		 */1552		History.Adapter.bind(window,"beforeunload",History.clearAllIntervals);1553		History.Adapter.bind(window,"unload",History.clearAllIntervals);1554		/**1555		 * Create the initial State1556		 */1557		History.saveState(History.storeState(History.extractState(document.location.href,true)));1558		/**1559		 * Bind for Saving Store1560		 */1561		if ( sessionStorage ) {1562			// When the page is closed1563			History.onUnload = function(){1564				// Prepare1565				var	currentStore, item;1566				// Fetch1567				try {1568					currentStore = JSON.parse(sessionStorage.getItem('History.store'))||{};1569				}1570				catch ( err ) {1571					currentStore = {};1572				}1573				// Ensure1574				currentStore.idToState = currentStore.idToState || {};1575				currentStore.urlToId = currentStore.urlToId || {};1576				currentStore.stateToId = currentStore.stateToId || {};1577				// Sync1578				for ( item in History.idToState ) {1579					if ( !History.idToState.hasOwnProperty(item) ) {1580						continue;1581					}1582					currentStore.idToState[item] = History.idToState[item];1583				}1584				for ( item in History.urlToId ) {1585					if ( !History.urlToId.hasOwnProperty(item) ) {1586						continue;1587					}1588					currentStore.urlToId[item] = History.urlToId[item];1589				}1590				for ( item in History.stateToId ) {1591					if ( !History.stateToId.hasOwnProperty(item) ) {1592						continue;1593					}1594					currentStore.stateToId[item] = History.stateToId[item];1595				}1596				// Update1597				History.store = currentStore;1598				History.normalizeStore();1599				// Store1600				sessionStorage.setItem('History.store',JSON.stringify(currentStore));1601			};1602			// For Internet Explorer1603			History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval));1604			1605			// For Other Browsers1606			History.Adapter.bind(window,'beforeunload',History.onUnload);1607			History.Adapter.bind(window,'unload',History.onUnload);1608			1609			// Both are enabled for consistency1610		}1611		// Non-Native pushState Implementation1612		if ( !History.emulated.pushState ) {1613			// Be aware, the following is only for native pushState implementations1614			// If you are wanting to include something for all browsers1615			// Then include it above this if block1616			/**1617			 * Setup Safari Fix1618			 */1619			if ( History.bugs.safariPoll ) {1620				History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval));1621			}1622			/**1623			 * Ensure Cross Browser Compatibility1624			 */1625			if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) {1626				/**1627				 * Fix Safari HashChange Issue1628				 */1629				// Setup Alias1630				History.Adapter.bind(window,'hashchange',function(){1631					History.Adapter.trigger(window,'popstate');1632				});1633				// Initialise Alias1634				if ( History.getHash() ) {1635					History.Adapter.onDomLoad(function(){1636						History.Adapter.trigger(window,'hashchange');1637					});1638				}1639			}1640		} // !History.emulated.pushState1641	}; // History.initCore1642	// Try and Initialise History1643	History.init();...history.html4.js
Source:history.html4.js  
1/**2 * History.js HTML4 Support3 * Depends on the HTML5 Support4 * @author Benjamin Arthur Lupton <contact@balupton.com>5 * @copyright 2010-2011 Benjamin Arthur Lupton <contact@balupton.com>6 * @license New BSD License <http://creativecommons.org/licenses/BSD/>7 */8(function(window,undefined){9	"use strict";10	// ========================================================================11	// Initialise12	// Localise Globals13	var14		document = window.document, // Make sure we are using the correct document15		setTimeout = window.setTimeout||setTimeout,16		clearTimeout = window.clearTimeout||clearTimeout,17		setInterval = window.setInterval||setInterval,18		History = window.History = window.History||{}; // Public History Object19	// Check Existence20	if ( typeof History.initHtml4 !== 'undefined' ) {21		throw new Error('History.js HTML4 Support has already been loaded...');22	}23	// ========================================================================24	// Initialise HTML4 Support25	// Initialise HTML4 Support26	History.initHtml4 = function(){27		// Initialise28		if ( typeof History.initHtml4.initialized !== 'undefined' ) {29			// Already Loaded30			return false;31		}32		else {33			History.initHtml4.initialized = true;34		}35		// ====================================================================36		// Properties37		/**38		 * History.enabled39		 * Is History enabled?40		 */41		History.enabled = true;42		// ====================================================================43		// Hash Storage44		/**45		 * History.savedHashes46		 * Store the hashes in an array47		 */48		History.savedHashes = [];49		/**50		 * History.isLastHash(newHash)51		 * Checks if the hash is the last hash52		 * @param {string} newHash53		 * @return {boolean} true54		 */55		History.isLastHash = function(newHash){56			// Prepare57			var oldHash = History.getHashByIndex(),58				isLast;59			// Check60			isLast = newHash === oldHash;61			// Return isLast62			return isLast;63		};64		/**65		 * History.saveHash(newHash)66		 * Push a Hash67		 * @param {string} newHash68		 * @return {boolean} true69		 */70		History.saveHash = function(newHash){71			// Check Hash72			if ( History.isLastHash(newHash) ) {73				return false;74			}75			// Push the Hash76			History.savedHashes.push(newHash);77			// Return true78			return true;79		};80		/**81		 * History.getHashByIndex()82		 * Gets a hash by the index83		 * @param {integer} index84		 * @return {string}85		 */86		History.getHashByIndex = function(index){87			// Prepare88			var hash = null;89			// Handle90			if ( typeof index === 'undefined' ) {91				// Get the last inserted92				hash = History.savedHashes[History.savedHashes.length-1];93			}94			else if ( index < 0 ) {95				// Get from the end96				hash = History.savedHashes[History.savedHashes.length+index];97			}98			else {99				// Get from the beginning100				hash = History.savedHashes[index];101			}102			// Return hash103			return hash;104		};105		// ====================================================================106		// Discarded States107		/**108		 * History.discardedHashes109		 * A hashed array of discarded hashes110		 */111		History.discardedHashes = {};112		/**113		 * History.discardedStates114		 * A hashed array of discarded states115		 */116		History.discardedStates = {};117		/**118		 * History.discardState(State)119		 * Discards the state by ignoring it through History120		 * @param {object} State121		 * @return {true}122		 */123		History.discardState = function(discardedState,forwardState,backState){124			//History.debug('History.discardState', arguments);125			// Prepare126			var discardedStateHash = History.getHashByState(discardedState),127				discardObject;128			// Create Discard Object129			discardObject = {130				'discardedState': discardedState,131				'backState': backState,132				'forwardState': forwardState133			};134			// Add to DiscardedStates135			History.discardedStates[discardedStateHash] = discardObject;136			// Return true137			return true;138		};139		/**140		 * History.discardHash(hash)141		 * Discards the hash by ignoring it through History142		 * @param {string} hash143		 * @return {true}144		 */145		History.discardHash = function(discardedHash,forwardState,backState){146			//History.debug('History.discardState', arguments);147			// Create Discard Object148			var discardObject = {149				'discardedHash': discardedHash,150				'backState': backState,151				'forwardState': forwardState152			};153			// Add to discardedHash154			History.discardedHashes[discardedHash] = discardObject;155			// Return true156			return true;157		};158		/**159		 * History.discardState(State)160		 * Checks to see if the state is discarded161		 * @param {object} State162		 * @return {bool}163		 */164		History.discardedState = function(State){165			// Prepare166			var StateHash = History.getHashByState(State),167				discarded;168			// Check169			discarded = History.discardedStates[StateHash]||false;170			// Return true171			return discarded;172		};173		/**174		 * History.discardedHash(hash)175		 * Checks to see if the state is discarded176		 * @param {string} State177		 * @return {bool}178		 */179		History.discardedHash = function(hash){180			// Check181			var discarded = History.discardedHashes[hash]||false;182			// Return true183			return discarded;184		};185		/**186		 * History.recycleState(State)187		 * Allows a discarded state to be used again188		 * @param {object} data189		 * @param {string} title190		 * @param {string} url191		 * @return {true}192		 */193		History.recycleState = function(State){194			//History.debug('History.recycleState', arguments);195			// Prepare196			var StateHash = History.getHashByState(State);197			// Remove from DiscardedStates198			if ( History.discardedState(State) ) {199				delete History.discardedStates[StateHash];200			}201			// Return true202			return true;203		};204		// ====================================================================205		// HTML4 HashChange Support206		if ( History.emulated.hashChange ) {207			/*208			 * We must emulate the HTML4 HashChange Support by manually checking for hash changes209			 */210			/**211			 * History.hashChangeInit()212			 * Init the HashChange Emulation213			 */214			History.hashChangeInit = function(){215				// Define our Checker Function216				History.checkerFunction = null;217				// Define some variables that will help in our checker function218				var lastDocumentHash = '',219					iframeId, iframe,220					lastIframeHash, checkerRunning;221				// Handle depending on the browser222				if ( History.isInternetExplorer() ) {223					// IE6 and IE7224					// We need to use an iframe to emulate the back and forward buttons225					// Create iFrame226					iframeId = 'historyjs-iframe';227					iframe = document.createElement('iframe');228					// Adjust iFarme229					iframe.setAttribute('id', iframeId);230					iframe.style.display = 'none';231					// Append iFrame232					document.body.appendChild(iframe);233					// Create initial history entry234					iframe.contentWindow.document.open();235					iframe.contentWindow.document.close();236					// Define some variables that will help in our checker function237					lastIframeHash = '';238					checkerRunning = false;239					// Define the checker function240					History.checkerFunction = function(){241						// Check Running242						if ( checkerRunning ) {243							return false;244						}245						// Update Running246						checkerRunning = true;247						// Fetch248						var documentHash = History.getHash()||'',249							iframeHash = History.unescapeHash(iframe.contentWindow.document.location.hash)||'';250						// The Document Hash has changed (application caused)251						if ( documentHash !== lastDocumentHash ) {252							// Equalise253							lastDocumentHash = documentHash;254							// Create a history entry in the iframe255							if ( iframeHash !== documentHash ) {256								//History.debug('hashchange.checker: iframe hash change', 'documentHash (new):', documentHash, 'iframeHash (old):', iframeHash);257								// Equalise258								lastIframeHash = iframeHash = documentHash;259								// Create History Entry260								iframe.contentWindow.document.open();261								iframe.contentWindow.document.close();262								// Update the iframe's hash263								iframe.contentWindow.document.location.hash = History.escapeHash(documentHash);264							}265							// Trigger Hashchange Event266							History.Adapter.trigger(window,'hashchange');267						}268						// The iFrame Hash has changed (back button caused)269						else if ( iframeHash !== lastIframeHash ) {270							//History.debug('hashchange.checker: iframe hash out of sync', 'iframeHash (new):', iframeHash, 'documentHash (old):', documentHash);271							// Equalise272							lastIframeHash = iframeHash;273							// Update the Hash274							History.setHash(iframeHash,false);275						}276						// Reset Running277						checkerRunning = false;278						// Return true279						return true;280					};281				}282				else {283					// We are not IE284					// Firefox 1 or 2, Opera285					// Define the checker function286					History.checkerFunction = function(){287						// Prepare288						var documentHash = History.getHash();289						// The Document Hash has changed (application caused)290						if ( documentHash !== lastDocumentHash ) {291							// Equalise292							lastDocumentHash = documentHash;293							// Trigger Hashchange Event294							History.Adapter.trigger(window,'hashchange');295						}296						// Return true297						return true;298					};299				}300				// Apply the checker function301				History.intervalList.push(setInterval(History.checkerFunction, History.options.hashChangeInterval));302				// Done303				return true;304			}; // History.hashChangeInit305			// Bind hashChangeInit306			History.Adapter.onDomLoad(History.hashChangeInit);307		} // History.emulated.hashChange308		// ====================================================================309		// HTML5 State Support310		// Non-Native pushState Implementation311		if ( History.emulated.pushState ) {312			/*313			 * We must emulate the HTML5 State Management by using HTML4 HashChange314			 */315			/**316			 * History.onHashChange(event)317			 * Trigger HTML5's window.onpopstate via HTML4 HashChange Support318			 */319			History.onHashChange = function(event){320				//History.debug('History.onHashChange', arguments);321				// Prepare322				var currentUrl = ((event && event.newURL) || document.location.href),323					currentHash = History.getHashByUrl(currentUrl),324					currentState = null,325					currentStateHash = null,326					currentStateHashExits = null,327					discardObject;328				// Check if we are the same state329				if ( History.isLastHash(currentHash) ) {330					// There has been no change (just the page's hash has finally propagated)331					//History.debug('History.onHashChange: no change');332					History.busy(false);333					return false;334				}335				// Reset the double check336				History.doubleCheckComplete();337				// Store our location for use in detecting back/forward direction338				History.saveHash(currentHash);339				// Expand Hash340				if ( currentHash && History.isTraditionalAnchor(currentHash) ) {341					//History.debug('History.onHashChange: traditional anchor', currentHash);342					// Traditional Anchor Hash343					History.Adapter.trigger(window,'anchorchange');344					History.busy(false);345					return false;346				}347				// Create State348				currentState = History.extractState(History.getFullUrl(currentHash||document.location.href,false),true);349				// Check if we are the same state350				if ( History.isLastSavedState(currentState) ) {351					//History.debug('History.onHashChange: no change');352					// There has been no change (just the page's hash has finally propagated)353					History.busy(false);354					return false;355				}356				// Create the state Hash357				currentStateHash = History.getHashByState(currentState);358				// Check if we are DiscardedState359				discardObject = History.discardedState(currentState);360				if ( discardObject ) {361					// Ignore this state as it has been discarded and go back to the state before it362					if ( History.getHashByIndex(-2) === History.getHashByState(discardObject.forwardState) ) {363						// We are going backwards364						//History.debug('History.onHashChange: go backwards');365						History.back(false);366					} else {367						// We are going forwards368						//History.debug('History.onHashChange: go forwards');369						History.forward(false);370					}371					return false;372				}373				// Push the new HTML5 State374				//History.debug('History.onHashChange: success hashchange');375				History.pushState(currentState.data,currentState.title,currentState.url,false);376				// End onHashChange closure377				return true;378			};379			History.Adapter.bind(window,'hashchange',History.onHashChange);380			/**381			 * History.pushState(data,title,url)382			 * Add a new State to the history object, become it, and trigger onpopstate383			 * We have to trigger for HTML4 compatibility384			 * @param {object} data385			 * @param {string} title386			 * @param {string} url387			 * @return {true}388			 */389			History.pushState = function(data,title,url,queue){390				//History.debug('History.pushState: called', arguments);391				// Check the State392				if ( History.getHashByUrl(url) ) {393					throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');394				}395				// Handle Queueing396				if ( queue !== false && History.busy() ) {397					// Wait + Push to Queue398					//History.debug('History.pushState: we must wait', arguments);399					History.pushQueue({400						scope: History,401						callback: History.pushState,402						args: arguments,403						queue: queue404					});405					return false;406				}407				// Make Busy408				History.busy(true);409				// Fetch the State Object410				var newState = History.createStateObject(data,title,url),411					newStateHash = History.getHashByState(newState),412					oldState = History.getState(false),413					oldStateHash = History.getHashByState(oldState),414					html4Hash = History.getHash();415				// Store the newState416				History.storeState(newState);417				History.expectedStateId = newState.id;418				// Recycle the State419				History.recycleState(newState);420				// Force update of the title421				History.setTitle(newState);422				// Check if we are the same State423				if ( newStateHash === oldStateHash ) {424					//History.debug('History.pushState: no change', newStateHash);425					History.busy(false);426					return false;427				}428				// Update HTML4 Hash429				if ( newStateHash !== html4Hash && newStateHash !== History.getShortUrl(document.location.href) ) {430					//History.debug('History.pushState: update hash', newStateHash, html4Hash);431					History.setHash(newStateHash,false);432					return false;433				}434				// Update HTML5 State435				History.saveState(newState);436				// Fire HTML5 Event437				//History.debug('History.pushState: trigger popstate');438				History.Adapter.trigger(window,'statechange');439				History.busy(false);440				// End pushState closure441				return true;442			};443			/**444			 * History.replaceState(data,title,url)445			 * Replace the State and trigger onpopstate446			 * We have to trigger for HTML4 compatibility447			 * @param {object} data448			 * @param {string} title449			 * @param {string} url450			 * @return {true}451			 */452			History.replaceState = function(data,title,url,queue){453				//History.debug('History.replaceState: called', arguments);454				// Check the State455				if ( History.getHashByUrl(url) ) {456					throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).');457				}458				// Handle Queueing459				if ( queue !== false && History.busy() ) {460					// Wait + Push to Queue461					//History.debug('History.replaceState: we must wait', arguments);462					History.pushQueue({463						scope: History,464						callback: History.replaceState,465						args: arguments,466						queue: queue467					});468					return false;469				}470				// Make Busy471				History.busy(true);472				// Fetch the State Objects473				var newState        = History.createStateObject(data,title,url),474					oldState        = History.getState(false),475					previousState   = History.getStateByIndex(-2);476				// Discard Old State477				History.discardState(oldState,newState,previousState);478				// Alias to PushState479				History.pushState(newState.data,newState.title,newState.url,false);480				// End replaceState closure481				return true;482			};483		} // History.emulated.pushState484		// ====================================================================485		// Initialise486		// Non-Native pushState Implementation487		if ( History.emulated.pushState ) {488			/**489			 * Ensure initial state is handled correctly490			 */491			if ( History.getHash() && !History.emulated.hashChange ) {492				History.Adapter.onDomLoad(function(){493					History.Adapter.trigger(window,'hashchange');494				});495			}496		} // History.emulated.pushState497	}; // History.initHtml4498	// Try and Initialise History499	if ( typeof History.init !== 'undefined' ) {500		History.init();501	}...history-html5-coverage.js
Source:history-html5-coverage.js  
1/*2YUI 3.7.3 (build 5687)3Copyright 2012 Yahoo! Inc. All rights reserved.4Licensed under the BSD License.5http://yuilibrary.com/license/6*/7if (typeof _yuitest_coverage == "undefined"){8    _yuitest_coverage = {};9    _yuitest_coverline = function(src, line){10        var coverage = _yuitest_coverage[src];11        if (!coverage.lines[line]){12            coverage.calledLines++;13        }14        coverage.lines[line]++;15    };16    _yuitest_coverfunc = function(src, name, line){17        var coverage = _yuitest_coverage[src],18            funcId = name + ":" + line;19        if (!coverage.functions[funcId]){20            coverage.calledFunctions++;21        }22        coverage.functions[funcId]++;23    };24}25_yuitest_coverage["build/history-html5/history-html5.js"] = {26    lines: {},27    functions: {},28    coveredLines: 0,29    calledLines: 0,30    coveredFunctions: 0,31    calledFunctions: 0,32    path: "build/history-html5/history-html5.js",33    code: []34};35_yuitest_coverage["build/history-html5/history-html5.js"].code=["YUI.add('history-html5', function (Y, NAME) {","","/**"," * Provides browser history management using the HTML5 history API."," *"," * @module history"," * @submodule history-html5"," * @since 3.2.0"," */","","/**"," * <p>"," * Provides browser history management using the HTML5 history API."," * </p>"," *"," * <p>"," * When calling the <code>add()</code>, <code>addValue()</code>,"," * <code>replace()</code>, or <code>replaceValue()</code> methods on"," * <code>HistoryHTML5</code>, the following additional options are supported:"," * </p>"," *"," * <dl>"," *   <dt><strong>title (String)</strong></dt>"," *   <dd>"," *     Title to use for the new history entry. Browsers will typically display"," *     this title to the user in the detailed history window or in a dropdown"," *     menu attached to the back/forward buttons. If not specified, the title"," *     of the current document will be used."," *   </dd>"," *"," *   <dt><strong>url (String)</strong></dt>"," *   <dd>"," *     URL to display to the user for the new history entry. This URL will be"," *     visible in the browser's address bar and will be the bookmarked URL if"," *     the user bookmarks the page. It may be a relative path (\"foo/bar\"), an"," *     absolute path (\"/foo/bar\"), or a full URL (\"http://example.com/foo/bar\")."," *     If you specify a full URL, the origin <i>must</i> be the same as the"," *     origin of the current page, or an error will occur. If no URL is"," *     specified, the current URL will not be changed."," *   </dd>"," * </dl>"," *"," * @class HistoryHTML5"," * @extends HistoryBase"," * @constructor"," * @param {Object} config (optional) Configuration object."," */","","var HistoryBase     = Y.HistoryBase,","    Lang            = Y.Lang,","    win             = Y.config.win,","    useHistoryHTML5 = Y.config.useHistoryHTML5,","","    SRC_POPSTATE    = 'popstate',","    SRC_REPLACE     = HistoryBase.SRC_REPLACE;","","function HistoryHTML5() {","    HistoryHTML5.superclass.constructor.apply(this, arguments);","}","","Y.extend(HistoryHTML5, HistoryBase, {","    // -- Initialization -------------------------------------------------------","    _init: function (config) {","        var bookmarkedState = win.history.state;","","        // Treat empty state objects as `null` so they're not processed further.","        if (Y.Object.isEmpty(bookmarkedState)) {","            bookmarkedState = null;","        }","","        config || (config = {});","","        // If both the initial state and the bookmarked state are objects, merge","        // them (bookmarked state wins).","        if (config.initialState","                && Lang.type(config.initialState) === 'object'","                && Lang.type(bookmarkedState) === 'object') {","","            this._initialState = Y.merge(config.initialState, bookmarkedState);","        } else {","            // Otherwise, the bookmarked state always wins if there is one. If","            // there isn't a bookmarked state, history-base will take care of","            // falling back to config.initialState or null.","            this._initialState = bookmarkedState;","        }","","        Y.on('popstate', this._onPopState, win, this);","","        HistoryHTML5.superclass._init.apply(this, arguments);","    },","","    // -- Protected Methods ----------------------------------------------------","","    /**","     * Overrides HistoryBase's <code>_storeState()</code> and pushes or replaces","     * a history entry using the HTML5 history API when necessary.","     *","     * @method _storeState","     * @param {String} src Source of the changes.","     * @param {Object} newState New state to store.","     * @param {Object} options Zero or more options.","     * @protected","     */","    _storeState: function (src, newState, options) {","        if (src !== SRC_POPSTATE) {","            win.history[src === SRC_REPLACE ? 'replaceState' : 'pushState'](","                newState,","                options.title || Y.config.doc.title || '',","                options.url || null","            );","        }","","        HistoryHTML5.superclass._storeState.apply(this, arguments);","    },","","    // -- Protected Event Handlers ---------------------------------------------","","    /**","     * Handler for popstate events.","     *","     * @method _onPopState","     * @param {Event} e","     * @protected","     */","    _onPopState: function (e) {","        this._resolveChanges(SRC_POPSTATE, e._event.state || null);","    }","}, {","    // -- Public Static Properties ---------------------------------------------","    NAME: 'historyhtml5',","","    /**","     * Constant used to identify state changes originating from","     * <code>popstate</code> events.","     *","     * @property SRC_POPSTATE","     * @type String","     * @static","     * @final","     */","    SRC_POPSTATE: SRC_POPSTATE","});","","if (!Y.Node.DOM_EVENTS.popstate) {","    Y.Node.DOM_EVENTS.popstate = 1;","}","","Y.HistoryHTML5 = HistoryHTML5;","","/**"," * <p>"," * If <code>true</code>, the <code>Y.History</code> alias will always point to"," * <code>Y.HistoryHTML5</code> when the history-html5 module is loaded, even if"," * the current browser doesn't support HTML5 history."," * </p>"," *"," * <p>"," * If <code>false</code>, the <code>Y.History</code> alias will always point to"," * <code>Y.HistoryHash</code> when the history-hash module is loaded, even if"," * the current browser supports HTML5 history."," * </p>"," *"," * <p>"," * If neither <code>true</code> nor <code>false</code>, the"," * <code>Y.History</code> alias will point to the best available history adapter"," * that the browser supports. This is the default behavior."," * </p>"," *"," * @property useHistoryHTML5"," * @type boolean"," * @for config"," * @since 3.2.0"," */","","// HistoryHTML5 will always win over HistoryHash unless useHistoryHTML5 is false","// or HTML5 history is not supported.","if (useHistoryHTML5 === true || (useHistoryHTML5 !== false &&","        HistoryBase.html5)) {","    Y.History = HistoryHTML5;","}","","","}, '3.7.3', {\"optional\": [\"json\"], \"requires\": [\"event-base\", \"history-base\", \"node-base\"]});"];36_yuitest_coverage["build/history-html5/history-html5.js"].lines = {"1":0,"49":0,"57":0,"58":0,"61":0,"64":0,"67":0,"68":0,"71":0,"75":0,"79":0,"84":0,"87":0,"89":0,"105":0,"106":0,"113":0,"126":0,"144":0,"145":0,"148":0,"177":0,"179":0};37_yuitest_coverage["build/history-html5/history-html5.js"].functions = {"HistoryHTML5:57":0,"_init:63":0,"_storeState:104":0,"_onPopState:125":0,"(anonymous 1):1":0};38_yuitest_coverage["build/history-html5/history-html5.js"].coveredLines = 23;39_yuitest_coverage["build/history-html5/history-html5.js"].coveredFunctions = 5;40_yuitest_coverline("build/history-html5/history-html5.js", 1);41YUI.add('history-html5', function (Y, NAME) {42/**43 * Provides browser history management using the HTML5 history API.44 *45 * @module history46 * @submodule history-html547 * @since 3.2.048 */49/**50 * <p>51 * Provides browser history management using the HTML5 history API.52 * </p>53 *54 * <p>55 * When calling the <code>add()</code>, <code>addValue()</code>,56 * <code>replace()</code>, or <code>replaceValue()</code> methods on57 * <code>HistoryHTML5</code>, the following additional options are supported:58 * </p>59 *60 * <dl>61 *   <dt><strong>title (String)</strong></dt>62 *   <dd>63 *     Title to use for the new history entry. Browsers will typically display64 *     this title to the user in the detailed history window or in a dropdown65 *     menu attached to the back/forward buttons. If not specified, the title66 *     of the current document will be used.67 *   </dd>68 *69 *   <dt><strong>url (String)</strong></dt>70 *   <dd>71 *     URL to display to the user for the new history entry. This URL will be72 *     visible in the browser's address bar and will be the bookmarked URL if73 *     the user bookmarks the page. It may be a relative path ("foo/bar"), an74 *     absolute path ("/foo/bar"), or a full URL ("http://example.com/foo/bar").75 *     If you specify a full URL, the origin <i>must</i> be the same as the76 *     origin of the current page, or an error will occur. If no URL is77 *     specified, the current URL will not be changed.78 *   </dd>79 * </dl>80 *81 * @class HistoryHTML582 * @extends HistoryBase83 * @constructor84 * @param {Object} config (optional) Configuration object.85 */86_yuitest_coverfunc("build/history-html5/history-html5.js", "(anonymous 1)", 1);87_yuitest_coverline("build/history-html5/history-html5.js", 49);88var HistoryBase     = Y.HistoryBase,89    Lang            = Y.Lang,90    win             = Y.config.win,91    useHistoryHTML5 = Y.config.useHistoryHTML5,92    SRC_POPSTATE    = 'popstate',93    SRC_REPLACE     = HistoryBase.SRC_REPLACE;94_yuitest_coverline("build/history-html5/history-html5.js", 57);95function HistoryHTML5() {96    _yuitest_coverfunc("build/history-html5/history-html5.js", "HistoryHTML5", 57);97_yuitest_coverline("build/history-html5/history-html5.js", 58);98HistoryHTML5.superclass.constructor.apply(this, arguments);99}100_yuitest_coverline("build/history-html5/history-html5.js", 61);101Y.extend(HistoryHTML5, HistoryBase, {102    // -- Initialization -------------------------------------------------------103    _init: function (config) {104        _yuitest_coverfunc("build/history-html5/history-html5.js", "_init", 63);105_yuitest_coverline("build/history-html5/history-html5.js", 64);106var bookmarkedState = win.history.state;107        // Treat empty state objects as `null` so they're not processed further.108        _yuitest_coverline("build/history-html5/history-html5.js", 67);109if (Y.Object.isEmpty(bookmarkedState)) {110            _yuitest_coverline("build/history-html5/history-html5.js", 68);111bookmarkedState = null;112        }113        _yuitest_coverline("build/history-html5/history-html5.js", 71);114config || (config = {});115        // If both the initial state and the bookmarked state are objects, merge116        // them (bookmarked state wins).117        _yuitest_coverline("build/history-html5/history-html5.js", 75);118if (config.initialState119                && Lang.type(config.initialState) === 'object'120                && Lang.type(bookmarkedState) === 'object') {121            _yuitest_coverline("build/history-html5/history-html5.js", 79);122this._initialState = Y.merge(config.initialState, bookmarkedState);123        } else {124            // Otherwise, the bookmarked state always wins if there is one. If125            // there isn't a bookmarked state, history-base will take care of126            // falling back to config.initialState or null.127            _yuitest_coverline("build/history-html5/history-html5.js", 84);128this._initialState = bookmarkedState;129        }130        _yuitest_coverline("build/history-html5/history-html5.js", 87);131Y.on('popstate', this._onPopState, win, this);132        _yuitest_coverline("build/history-html5/history-html5.js", 89);133HistoryHTML5.superclass._init.apply(this, arguments);134    },135    // -- Protected Methods ----------------------------------------------------136    /**137     * Overrides HistoryBase's <code>_storeState()</code> and pushes or replaces138     * a history entry using the HTML5 history API when necessary.139     *140     * @method _storeState141     * @param {String} src Source of the changes.142     * @param {Object} newState New state to store.143     * @param {Object} options Zero or more options.144     * @protected145     */146    _storeState: function (src, newState, options) {147        _yuitest_coverfunc("build/history-html5/history-html5.js", "_storeState", 104);148_yuitest_coverline("build/history-html5/history-html5.js", 105);149if (src !== SRC_POPSTATE) {150            _yuitest_coverline("build/history-html5/history-html5.js", 106);151win.history[src === SRC_REPLACE ? 'replaceState' : 'pushState'](152                newState,153                options.title || Y.config.doc.title || '',154                options.url || null155            );156        }157        _yuitest_coverline("build/history-html5/history-html5.js", 113);158HistoryHTML5.superclass._storeState.apply(this, arguments);159    },160    // -- Protected Event Handlers ---------------------------------------------161    /**162     * Handler for popstate events.163     *164     * @method _onPopState165     * @param {Event} e166     * @protected167     */168    _onPopState: function (e) {169        _yuitest_coverfunc("build/history-html5/history-html5.js", "_onPopState", 125);170_yuitest_coverline("build/history-html5/history-html5.js", 126);171this._resolveChanges(SRC_POPSTATE, e._event.state || null);172    }173}, {174    // -- Public Static Properties ---------------------------------------------175    NAME: 'historyhtml5',176    /**177     * Constant used to identify state changes originating from178     * <code>popstate</code> events.179     *180     * @property SRC_POPSTATE181     * @type String182     * @static183     * @final184     */185    SRC_POPSTATE: SRC_POPSTATE186});187_yuitest_coverline("build/history-html5/history-html5.js", 144);188if (!Y.Node.DOM_EVENTS.popstate) {189    _yuitest_coverline("build/history-html5/history-html5.js", 145);190Y.Node.DOM_EVENTS.popstate = 1;191}192_yuitest_coverline("build/history-html5/history-html5.js", 148);193Y.HistoryHTML5 = HistoryHTML5;194/**195 * <p>196 * If <code>true</code>, the <code>Y.History</code> alias will always point to197 * <code>Y.HistoryHTML5</code> when the history-html5 module is loaded, even if198 * the current browser doesn't support HTML5 history.199 * </p>200 *201 * <p>202 * If <code>false</code>, the <code>Y.History</code> alias will always point to203 * <code>Y.HistoryHash</code> when the history-hash module is loaded, even if204 * the current browser supports HTML5 history.205 * </p>206 *207 * <p>208 * If neither <code>true</code> nor <code>false</code>, the209 * <code>Y.History</code> alias will point to the best available history adapter210 * that the browser supports. This is the default behavior.211 * </p>212 *213 * @property useHistoryHTML5214 * @type boolean215 * @for config216 * @since 3.2.0217 */218// HistoryHTML5 will always win over HistoryHash unless useHistoryHTML5 is false219// or HTML5 history is not supported.220_yuitest_coverline("build/history-html5/history-html5.js", 177);221if (useHistoryHTML5 === true || (useHistoryHTML5 !== false &&222        HistoryBase.html5)) {223    _yuitest_coverline("build/history-html5/history-html5.js", 179);224Y.History = HistoryHTML5;225}...history.py
Source:history.py  
...52        53    history_length=property(get_history_length,set_history_length)54    history_cursor=property(get_history_cursor,set_history_cursor)5556    def clear_history(self):57        '''Clear readline history.'''58        self.history[:] = []59        self.history_cursor = 06061    def read_history_file(self, filename=None): 62        '''Load a readline history file.'''63        if filename is None:64            filename=self.history_filename65        try:66            for line in open(filename, 'r'):67                self.add_history(lineobj.ReadLineTextBuffer(line.rstrip()))68        except IOError:69            self.history = []70            self.history_cursor = 07172    def write_history_file(self, filename=None): 73        '''Save a readline history file.'''74        if filename is None:75            filename=self.history_filename76        fp = open(filename, 'wb')77        for line in self.history[-self.history_length:]:78            fp.write(line.get_line_text())79            fp.write('\n')80        fp.close()818283    def add_history(self, line):84        '''Append a line to the history buffer, as if it was the last line typed.'''85        if not line.get_line_text():86            pass87        elif len(self.history) > 0 and self.history[-1].get_line_text() == line.get_line_text():88            pass89        else:90            self.history.append(line)91        self.history_cursor = len(self.history)9293    def previous_history(self,current): # (C-p)94        '''Move back through the history list, fetching the previous command. '''95        if self.history_cursor==len(self.history):96            self.history.append(current.copy()) #do not use add_history since we do not want to increment cursor97            98        if self.history_cursor > 0:99            self.history_cursor -= 1100            current.set_line(self.history[self.history_cursor].get_line_text())101            current.point=lineobj.EndOfLine102103    def next_history(self,current): # (C-n)104        '''Move forward through the history list, fetching the next command. '''105        if self.history_cursor < len(self.history)-1:106            self.history_cursor += 1107            current.set_line(self.history[self.history_cursor].get_line_text())108109    def beginning_of_history(self): # (M-<)110        '''Move to the first line in the history.'''111        self.history_cursor = 0112        if len(self.history) > 0:113            self.l_buffer = self.history[0]114115    def end_of_history(self,current): # (M->)116        '''Move to the end of the input history, i.e., the line currently117        being entered.'''118        self.history_cursor=len(self.history)119        current.set_line(self.history[-1].get_line_text())120121    def reverse_search_history(self,searchfor,startpos=None):122        if startpos is None:123            startpos=self.history_cursor124        res=[(idx,line)  for idx,line in enumerate(self.history[startpos:0:-1]) if line.startswith(searchfor)]125        if res:126            self.history_cursor-=res[0][0]127            return res[0][1].get_line_text()128        return ""129        130    def forward_search_history(self,searchfor,startpos=None):131        if startpos is None:132            startpos=self.history_cursor133        res=[(idx,line) for idx,line in enumerate(self.history[startpos:]) if line.startswith(searchfor)]134        if res:135            self.history_cursor+=res[0][0]136            return res[0][1].get_line_text()137        return ""138139    def _non_i_search(self, direction, current):140        c = pyreadline.rl.console141        line = current.get_line_text()142        query = ''143        while 1:144            c.pos(*pyreadline.rl.prompt_end_pos)145            scroll = c.write_scrolling(":%s" % query)146            pyreadline.rl._update_prompt_pos(scroll)147            pyreadline.rl._clear_after()148149            event = c.getkeypress()150            log_sock(str(event),"history")151            152            if event.keyinfo.keyname == 'backspace':153                if len(query) > 0:154                    query = query[:-1]155                else:156                    break157            elif event.char in string.letters + string.digits + string.punctuation + ' ':158                query += event.char159            elif event.keyinfo.keyname == 'return':160                break161            else:162                pyreadline.rl._bell()163        log_sock(query,"history")164        res=""165        if query:166            if direction==-1:167                res=self.reverse_search_history(query)168                169            else:170                res=self.forward_search_history(query)171            log_sock(res,"history")172        return lineobj.ReadLineTextBuffer(res,point=0)173        174    def non_incremental_reverse_search_history(self,current): # (M-p)175        '''Search backward starting at the current line and moving up176        through the history as necessary using a non-incremental search for177        a string supplied by the user.'''178        return self._non_i_search(-1,current)179180    def non_incremental_forward_search_history(self,current): # (M-n)181        '''Search forward starting at the current line and moving down182        through the the history as necessary using a non-incremental search183        for a string supplied by the user.'''184        return self._non_i_search(1,current)185186    def _search(self, direction, partial):187        try:188            if (self.lastcommand != self.history_search_forward and189                    self.lastcommand != self.history_search_backward):190                self.query = ''.join(partial[0:partial.point].get_line_text())191            hcstart=max(self.history_cursor,0) 192            log_sock("hcstart %s"%hcstart,"history")193            hc = self.history_cursor + direction194            while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):195                h = self.history[hc]196                if not self.query:197                    self.history_cursor = hc198                    result=lineobj.ReadLineTextBuffer(h,point=len(h.get_line_text()))199                    return result200                elif h.get_line_text().startswith(self.query) and h != partial.get_line_text():201                    self.history_cursor = hc202                    result=lineobj.ReadLineTextBuffer(h,point=partial.point)203                    return result204                hc += direction205            else:206                if len(self.history)==0:207                    pass 208                elif hc>=len(self.history) and not self.query:209                    self.history_cursor=len(self.history)210                    return lineobj.ReadLineTextBuffer("",point=0)211                elif self.history[max(min(hcstart,len(self.history)-1),0)].get_line_text().startswith(self.query) and self.query:212                    return lineobj.ReadLineTextBuffer(self.history[max(min(hcstart,len(self.history)-1),0)],point=partial.point)213                else:                214                    return lineobj.ReadLineTextBuffer(partial,point=partial.point)215                return lineobj.ReadLineTextBuffer(self.query,point=min(len(self.query),partial.point))216        except IndexError:217            log_sock("hcstart:%s %s"%(hcstart,len(self.history)),"history")218            raise219220    def history_search_forward(self,partial): # ()221        '''Search forward through the history for the string of characters222        between the start of the current line and the point. This is a223        non-incremental search. By default, this command is unbound.'''224        q= self._search(1,partial)225        return q226227    def history_search_backward(self,partial): # ()228        '''Search backward through the history for the string of characters229        between the start of the current line and the point. This is a230        non-incremental search. By default, this command is unbound.'''231        232        q= self._search(-1,partial)233        return q234235236        237if __name__=="__main__":238    q=LineHistory()239    RL=lineobj.ReadLineTextBuffer240    q.add_history(RL("aaaa"))241    q.add_history(RL("aaba"))242    q.add_history(RL("aaca"))243    q.add_history(RL("akca"))244    q.add_history(RL("bbb"))
...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
