src/sap.ui.core/src/jquery.sap.history.js
function onHashChange(sHash){
			var oRoute, iStep, oParsedHash, iNewHashIndex, sNavType;

			//handle the nonbookmarkable hash
			if (currentHash === undefined) {
				//url with hash opened from bookmark
				oParsedHash = parseHashToObject(sHash);

				if (!oParsedHash || !oParsedHash.bBookmarkable) {
					if (jQuery.isFunction(defaultHandler)) {
						defaultHandler(jQuery.sap.history.NavType.Bookmark);
					}
					return;
				}
			}

			if (sHash.length === 0) {
				if (jQuery.isFunction(defaultHandler)) {
					defaultHandler(jQuery.sap.history.NavType.Back);
				}
			} else {
				//application restored from bookmark with non-empty hash, and later navigates back to the first hash token
				//the defaultHandler should be triggered
				iNewHashIndex = hashHistory.indexOf(sHash);
				if (iNewHashIndex === 0) {
					oParsedHash = parseHashToObject(sHash);
					if (!oParsedHash || !oParsedHash.bBookmarkable) {
						if (jQuery.isFunction(defaultHandler)) {
							defaultHandler(jQuery.sap.history.NavType.Back);
						}
						return;
					}
				}

				//need to handle when iNewHashIndex equals -1.
				//This happens when user navigates out the current application, and later navigates back.
				//In this case, the hashHistory is an empty array.


				if (isVirtualHash(sHash)) {
					//this is a virtual history, should do the skipping calculation
					if (isVirtualHash(currentHash)) {
						//go back to the first one that is not virtual
						iStep = calcStepsToRealHistory(sHash, false);
						window.history.go(iStep);
					} else {
						var sameFamilyRegex = new RegExp(escapeRegExp(currentHash + skipSuffix) + "[0-9]*$");
						if (sameFamilyRegex.test(sHash)) {
							//going forward
							//search forward in history for the first non-virtual hash
							//if there is, change to that one window.history.go
							//if not, stay and return false
							iStep = calcStepsToRealHistory(sHash, true);
							if (iStep) {
								window.history.go(iStep);
							} else {
								window.history.back();
							}

						} else {
							//going backward
							//search backward for the first non-virtual hash and there must be one
							iStep = calcStepsToRealHistory(sHash, false);
							window.history.go(iStep);
						}
					}
				} else {
					if (iNewHashIndex === -1) {
						sNavType = jQuery.sap.history.NavType.Unknown;
						hashHistory.push(sHash);
					} else {
						if (hashHistory.indexOf(currentHash, iNewHashIndex + 1) === -1) {
							sNavType = jQuery.sap.history.NavType.Forward;
						} else {
							sNavType = jQuery.sap.history.NavType.Back;
						}
					}


					oParsedHash = parseHashToObject(sHash);
					if (oParsedHash) {
						oRoute = findRouteByIdentifier(oParsedHash.sIdentifier);

						if (oRoute) {
							oRoute.action.apply(null, [oParsedHash.oStateData, sNavType]);
						}
					} else {
						Log.error("hash format error! The current Hash: " + sHash);
					}

				}
			}
		}
function onHashChange(sHash){
			var oRoute, iStep, oParsedHash, iNewHashIndex, sNavType;

			//handle the nonbookmarkable hash
			if (currentHash === undefined) {
				//url with hash opened from bookmark
				oParsedHash = parseHashToObject(sHash);

				if (!oParsedHash || !oParsedHash.bBookmarkable) {
					if (jQuery.isFunction(defaultHandler)) {
						defaultHandler(jQuery.sap.history.NavType.Bookmark);
					}
					return;
				}
			}

			if (sHash.length === 0) {
				if (jQuery.isFunction(defaultHandler)) {
					defaultHandler(jQuery.sap.history.NavType.Back);
				}
			} else {
				//application restored from bookmark with non-empty hash, and later navigates back to the first hash token
				//the defaultHandler should be triggered
				iNewHashIndex = hashHistory.indexOf(sHash);
				if (iNewHashIndex === 0) {
					oParsedHash = parseHashToObject(sHash);
					if (!oParsedHash || !oParsedHash.bBookmarkable) {
						if (jQuery.isFunction(defaultHandler)) {
							defaultHandler(jQuery.sap.history.NavType.Back);
						}
						return;
					}
				}

				//need to handle when iNewHashIndex equals -1.
				//This happens when user navigates out the current application, and later navigates back.
				//In this case, the hashHistory is an empty array.


				if (isVirtualHash(sHash)) {
					//this is a virtual history, should do the skipping calculation
					if (isVirtualHash(currentHash)) {
						//go back to the first one that is not virtual
						iStep = calcStepsToRealHistory(sHash, false);
						window.history.go(iStep);
					} else {
						var sameFamilyRegex = new RegExp(escapeRegExp(currentHash + skipSuffix) + "[0-9]*$");
						if (sameFamilyRegex.test(sHash)) {
							//going forward
							//search forward in history for the first non-virtual hash
							//if there is, change to that one window.history.go
							//if not, stay and return false
							iStep = calcStepsToRealHistory(sHash, true);
							if (iStep) {
								window.history.go(iStep);
							} else {
								window.history.back();
							}

						} else {
							//going backward
							//search backward for the first non-virtual hash and there must be one
							iStep = calcStepsToRealHistory(sHash, false);
							window.history.go(iStep);
						}
					}
				} else {
					if (iNewHashIndex === -1) {
						sNavType = jQuery.sap.history.NavType.Unknown;
						hashHistory.push(sHash);
					} else {
						if (hashHistory.indexOf(currentHash, iNewHashIndex + 1) === -1) {
							sNavType = jQuery.sap.history.NavType.Forward;
						} else {
							sNavType = jQuery.sap.history.NavType.Back;
						}
					}


					oParsedHash = parseHashToObject(sHash);
					if (oParsedHash) {
						oRoute = findRouteByIdentifier(oParsedHash.sIdentifier);

						if (oRoute) {
							oRoute.action.apply(null, [oParsedHash.oStateData, sNavType]);
						}
					} else {
						Log.error("hash format error! The current Hash: " + sHash);
					}

				}
			}
		}
function onHashChange ( sHash ) { var oRoute , iStep , oParsedHash , iNewHashIndex , sNavType ; //handle the nonbookmarkable hash if ( currentHash === undefined ) { //url with hash opened from bookmark oParsedHash = parseHashToObject ( sHash ) ; if ( ! oParsedHash || ! oParsedHash . bBookmarkable ) { if ( jQuery . isFunction ( defaultHandler ) ) { defaultHandler ( jQuery . sap . history . NavType . Bookmark ) ; } return ; } } if ( sHash . length === 0 ) { if ( jQuery . isFunction ( defaultHandler ) ) { defaultHandler ( jQuery . sap . history . NavType . Back ) ; } } else { //application restored from bookmark with non-empty hash, and later navigates back to the first hash token //the defaultHandler should be triggered iNewHashIndex = hashHistory . indexOf ( sHash ) ; if ( iNewHashIndex === 0 ) { oParsedHash = parseHashToObject ( sHash ) ; if ( ! oParsedHash || ! oParsedHash . bBookmarkable ) { if ( jQuery . isFunction ( defaultHandler ) ) { defaultHandler ( jQuery . sap . history . NavType . Back ) ; } return ; } } //need to handle when iNewHashIndex equals -1. //This happens when user navigates out the current application, and later navigates back. //In this case, the hashHistory is an empty array. if ( isVirtualHash ( sHash ) ) { //this is a virtual history, should do the skipping calculation if ( isVirtualHash ( currentHash ) ) { //go back to the first one that is not virtual iStep = calcStepsToRealHistory ( sHash , false ) ; window . history . go ( iStep ) ; } else { var sameFamilyRegex = new RegExp ( escapeRegExp ( currentHash + skipSuffix ) + "[0-9]*$" ) ; if ( sameFamilyRegex . test ( sHash ) ) { //going forward //search forward in history for the first non-virtual hash //if there is, change to that one window.history.go //if not, stay and return false iStep = calcStepsToRealHistory ( sHash , true ) ; if ( iStep ) { window . history . go ( iStep ) ; } else { window . history . back ( ) ; } } else { //going backward //search backward for the first non-virtual hash and there must be one iStep = calcStepsToRealHistory ( sHash , false ) ; window . history . go ( iStep ) ; } } } else { if ( iNewHashIndex === - 1 ) { sNavType = jQuery . sap . history . NavType . Unknown ; hashHistory . push ( sHash ) ; } else { if ( hashHistory . indexOf ( currentHash , iNewHashIndex + 1 ) === - 1 ) { sNavType = jQuery . sap . history . NavType . Forward ; } else { sNavType = jQuery . sap . history . NavType . Back ; } } oParsedHash = parseHashToObject ( sHash ) ; if ( oParsedHash ) { oRoute = findRouteByIdentifier ( oParsedHash . sIdentifier ) ; if ( oRoute ) { oRoute . action . apply ( null , [ oParsedHash . oStateData , sNavType ] ) ; } } else { Log . error ( "hash format error! The current Hash: " + sHash ) ; } } } }
This is the main function that handles the hash change event.

@private
This is the main function that handles the hash change event .