<commit_before>/**
 * @author <NAME> <<EMAIL>>
 * @licence MIT
 * Minimalistic, workable, understandable html5 history shim.
 * IE8+, FF3+, Safari, Chrome, ...
 *
 *
 * Remarque : for IE8, please use it in conjonction with https://github.com/WebReflection/ie8
 * (Events listener shim for IE8)
 *
 * TODO :
 * 	test in IE8
 * 	work on creatEvent shim
 *
 * 	write test
 *
 * manage basepath
 * 
 * manage title
 *   ==> as all browser ignore it : 
 *   	=> need to manage it through store even for native.
 *   	=> should use hid in data object to retreieve it on popstate
 *   
 * manage internal/external flag
 */
(function() {
	'use strict';
	if (!window.history) // too old !!! ;)
		return;

	// ___________________________________________________________ UTILS
	var _uaMatch = function(ua) {
		ua = ua.toLowerCase();
		var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
			/(webkit)[ \/]([\w.]+)/.exec(ua) ||
			/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
			/(msie) ([\w.]+)/.exec(ua) ||
			ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
		return {
			browser: match[1] || '',
			version: match[2] || '0'
		};
	};

	var userAgent = function() {
		var browser = {},
			matched = _uaMatch(navigator.userAgent);
		if (matched.browser) {
			browser[matched.browser] = true;
			browser.version = matched.version;
		}
		if (browser.chrome)
			browser.webkit = true;
		else if (browser.webkit)
			browser.safari = true;
		return browser;
	};

	var parseUri = function(str) {
		var o = parseUri.options,
			m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
			uri = {},
			i = 14;
		while (i--)
			uri[o.key[i]] = m[i] || "";
		uri[o.q.name] = {};
		uri[o.key[12]].replace(o.q.parser, function($0, $1, $2) {
			if ($1) uri[o.q.name][$1] = $2;
		});
		return uri;
	};

	parseUri.options = {
		strictMode: true,
		key: ["href", "protocol", "host", "userInfo", "user", "password", "hostname", "port", "relative", "pathname", "directory", "file", "search", "hash"],
		q: {
			name: "query",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},
		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
			loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
		}
	};

	var parseURL = function(url) {
		var obj = parseUri(url);
		obj.path = obj.pathname + (obj.search?"?":"") + obj.search;
		return obj;
	};

	// http://stackoverflow.com/questions/9077101/iphone-localstorage-quota-exceeded-err-issue/12976988#12976988
	var sessionStorageSupported = function() {
		try {
			sessionStorage.setItem("test", "test");
			sessionStorage.removeItem("test");
			return true;
		} catch (e) {
			return false;
		}
	};

	// produce hid
	var produceID = function(){
		return +new Date + Math.round(Math.random()*1000000);
	};

	//______________________________________________________________________ History Shim

	var skipHashChange,
		skipInitPop = false,
		fromGo = false,
		agent = userAgent();

	window.history.parseURL = parseURL;
	window.history.config = {
		hashChangeAlone: true,
		setStateEvent:true,
		//setHashEvent:true,
		storageKey: "min-history",
		basePath: "/",
		hid: true
	};

	// some version of safari (and co) have no "state" property in history. force it.
	window.history.forceState = !("state" in window.history);
	if (window.history.forceState)
		window.history.state = null;

	// safari fire pop state on page load. skip it for consistency.
	if (agent.safari && location.relative !== window.history.config.basePath)
		skipInitPop = true;

	// produce href with hid in search part
	var getHrefWithHid = function(id, location) {
		var search = "?"+ (location.search || "") + "&hid=" + id;
		return (location.pathname || '/') 
			+ search
			+ (location.hash ? ("#" + location.hash) : "");
	};

	// remove hid from location parts. store hid in location.
	var cleanHidFromLocation = function(location) {
		if (!location.search)
			return;
		location.search = location.search.replace(/&hid=([^&]+)/gi, function(c, v) {
			location.hid = v;
			return "";
		});
		location.path = location.pathname + (location.search ? ("?" + location.search) : "");
		location.relative = location.path + (location.hash ? ("#" + location.hash) : "");
		location.href = location.protocol + "://" + location.host + location.relative;
	};

	var removeBasePath = function(location){
		if(!window.history.config.basePath)
			return location;
		location.pathname = location.pathname.substring(window.history.config.basePath.length);
		location.path = location.pathname + (location.search ? ("?" + location.search) : "");
		location.relative = location.path + (location.hash ? ("#" + location.hash) : "");
		location.href = location.protocol + "://" + location.host + location.relative;
		return location;
	};

	var fireStateEvent = function(type, state, action) {
		// TODO : manage IE8 document.createEventObject
		var evt = document.createEvent("CustomEvent");
		evt.initEvent(type, true, true);
		evt.state = state || null;
		if(action)
			evt.action = action;
		return window.dispatchEvent(evt);
	};

	window.history.store = {
		states: null,
		index: -1,
		init: function() {
			if (!(window.history.sessionStorageSupported = sessionStorageSupported()))
				console.warn("You are using custom history API and Session Storage is not supported. You'll loose history when leaving page.");
			
			if (window.history.sessionStorageSupported)
				this.states = JSON.parse(sessionStorage.getItem(window.history.config.storageKey)) || [];
			else
				this.states = [];
			if (!Array.isArray(this.states))
				this.states = [];
		},
		current:function(){
			return this.states[this.index];
		},
		push: function(data, title, url) {<commit_msg>ie8 friendly<commit_after>/**
 * @author <NAME> <<EMAIL>>
 * @licence MIT
 * Minimalistic, workable, understandable html5 history shim.
 * IE8+, FF3+, Safari, Chrome, ...
 *
 *
 * Remarque : for IE8, please use it in conjonction with https://github.com/WebReflection/ie8
 * (Events listener polyfill for IE8)
 *
 * TODO :
 * 	test in IE8
 * 	work on creatEvent polyfill
 *
 * 	write test
 *
 * manage basepath
 * 
 * manage title
 *   ==> as all browser ignore it : 
 *   	=> need to manage it through store even for native.
 *   	=> should use hid in data object to retreieve it on popstate
 *   
 * manage internal/external flag
 */
(function() {
	'use strict';
	if (!window.history) // too old !!! ;)
		return;

	// ___________________________________________________________ UTILS
	var _uaMatch = function(ua) {
		ua = ua.toLowerCase();
		var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
			/(webkit)[ \/]([\w.]+)/.exec(ua) ||
			/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
			/(msie) ([\w.]+)/.exec(ua) ||
			ua.indexOf('compatible') < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
		return {
			browser: match[1] || '',
			version: match[2] || '0'
		};
	};

	var userAgent = function() {
		var browser = {},
			matched = _uaMatch(navigator.userAgent);
		if (matched.browser) {
			browser[matched.browser] = true;
			browser.version = matched.version;
		}
		if (browser.chrome)
			browser.webkit = true;
		else if (browser.webkit)
			browser.safari = true;
		return browser;
	};

	var parseUri = function(str) {
		var o = parseUri.options,
			m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
			uri = {},
			i = 14;
		while (i--)
			uri[o.key[i]] = m[i] || "";
		uri[o.q.name] = {};
		uri[o.key[12]].replace(o.q.parser, function($0, $1, $2) {
			if ($1) uri[o.q.name][$1] = $2;
		});
		return uri;
	};

	parseUri.options = {
		strictMode: true,
		key: ["href", "protocol", "host", "userInfo", "user", "password", "hostname", "port", "relative", "pathname", "directory", "file", "search", "hash"],
		q: {
			name: "query",
			parser: /(?:^|&)([^&=]*)=?([^&]*)/g
		},
		parser: {
			strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
			loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
		}
	};

	var parseURL = function(url) {
		var obj = parseUri(url);
		obj.path = obj.pathname + (obj.search?"?":"") + obj.search;
		return obj;
	};

	// http://stackoverflow.com/questions/9077101/iphone-localstorage-quota-exceeded-err-issue/12976988#12976988
	var sessionStorageSupported = function() {
		try {
			sessionStorage.setItem("test", "test");
			sessionStorage.removeItem("test");
			return true;
		} catch (e) {
			return false;
		}
	};

	// produce hid
	var produceID = function(){
		return +new Date + Math.round(Math.random()*1000000);
	};

	//______________________________________________________________________ History Shim

	var skipHashChange,
		skipInitPop = false,
		fromGo = false,
		agent = userAgent();

	window.history.parseURL = parseURL;
	window.history.config = {
		hashChangeAlone: true,
		setStateEvent:true,
		//setHashEvent:true,
		storageKey: "min-history",
		basePath: "/",
		hid: true
	};

	// some version of safari (and co) have no "state" property in history. force it.
	window.history.forceState = !("state" in window.history);
	if (window.history.forceState)
		window.history.state = null;

	// safari fire pop state on page load. skip it for consistency.
	if (agent.safari && location.relative !== window.history.config.basePath)
		skipInitPop = true;

	// produce href with hid in search part
	var getHrefWithHid = function(id, location) {
		var search = "?"+ (location.search || "") + "&hid=" + id;
		return (location.pathname || '/') 
			+ search
			+ (location.hash ? ("#" + location.hash) : "");
	};

	// remove hid from location parts. store hid in location.
	var cleanHidFromLocation = function(location) {
		if (!location.search)
			return;
		location.search = location.search.replace(/&hid=([^&]+)/gi, function(c, v) {
			location.hid = v;
			return "";
		});
		location.path = location.pathname + (location.search ? ("?" + location.search) : "");
		location.relative = location.path + (location.hash ? ("#" + location.hash) : "");
		location.href = location.protocol + "://" + location.host + location.relative;
	};

	var removeBasePath = function(location){
		if(!window.history.config.basePath)
			return location;
		location.pathname = location.pathname.substring(window.history.config.basePath.length);
		location.path = location.pathname + (location.search ? ("?" + location.search) : "");
		location.relative = location.path + (location.hash ? ("#" + location.hash) : "");
		location.href = location.protocol + "://" + location.host + location.relative;
		return location;
	};

	var fireStateEvent = function(type, state, action) {
		var evt = document.createEvent("Event");
		evt.initEvent(type, true, true);
		evt.state = state || null;
		if(action)
			evt.action = action;
		return window.dispatchEvent(evt);
	};

	window.history.store = {
		states: null,
		index: -1,
		init: function() {
			if (!(window.history.sessionStorageSupported = sessionStorageSupported()))
				console.warn("You are using custom history API and Session Storage is not supported. You'll loose history when leaving page.");
			
			if (window.history.sessionStorageSupported)
				this.states = JSON.parse(sessionStorage.getItem(window.history.config.storageKey)) || [];
			else
				this.states = [];
			if (!this.states.forEach)
				this.states = [];
		},
		current:function(){
			return this.states[this.index];
		},
		push: function(data, title, url) {
{'fraction_non_alphanumeric': 0.2249064307587615, 'fraction_numerical': 0.009016672337529771, 'mean_word_length': 3.6342136381553014, 'pattern_counts': {'":': 6, '<': 11, '<?xml version=': 0, '>': 15, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4966707', 'n_tokens_mistral': 4536, 'n_tokens_neox': 4091, 'n_words': 851}