diff --git a/LICENSE-GPL b/LICENSE-GPL
deleted file mode 100644
index 65c5ca8..0000000
--- a/LICENSE-GPL
+++ /dev/null
@@ -1,165 +0,0 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
diff --git a/LICENSE-MIT b/LICENSE-MIT
index 737e952..4ca5750 100644
--- a/LICENSE-MIT
+++ b/LICENSE-MIT
@@ -1,6 +1,6 @@
MIT License (MIT)
-Copyright (c) 2010-2013 Dmitrii Pakhtinov
+Copyright (c) 2010-2018 Dmitrii Pakhtinov
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
diff --git a/README.md b/README.md
index d4eb55e..258c630 100644
--- a/README.md
+++ b/README.md
@@ -3,344 +3,378 @@ ENGLISH
This Javascript library provides an emulation of HTML5 History API for older browsers.
-The library operates according to W3C specification, adding no new or incompatible methods. The library can be used exactly as described, for example, in Dive Into HTML5 book (http://diveintohtml5.info/history.html) or in the History API Specification (http://www.w3.org/TR/html5/history.html#the-history-interface).
+The library operates according to W3C specification, adding no new or incompatible methods. The library can be used exactly as described, for example, in Dive Into HTML5 book (http://diveintohtml5.info/history.html) or in the History API Specification (http://www.w3.org/TR/html5/browsers.html#the-history-interface).
-Example of using the library in the pure JS context:
-
-```html
-
-
-
-
-
-
-
- My Link
- Other Link
-
-
+To enable support for HTML5-History-API polyfill in your library, you need to add one line of code:
+```js
+var location = window.history.location || window.location;
```
-Example of using the library along with JQuery:
+code of library looks like this:
+```js
+(function(){
+ // To enable support for HTML5-History-API polyfill in your library
+ var location = window.history.location || window.location;
+
+ // you library code here
+ // ....
+ // ....
+ // ....
+})();
+```
+### AMD Support:
```html
-
-
-
-
-
-
- My Link
- Other Link
-
+
+
+
+
+
+
```
-Example of using popstate (pure JS):
-
-```javascript
-window[ window.addEventListener ? 'addEventListener' : 'attachEvent' ]( 'popstate', function( event ) {
-
- // receiving location from the window.history object
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
-
-}, false);
+### Example of using the library in the pure JS context:
+```html
+
+
+
+
+
+
+
+ My Link
+ Other Link
+
+
```
-Example of using popstate with JQuery:
-
-```javascript
-$( window ).bind( 'popstate', function( event ) {
-
- // receiving location from the window.history object
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
-});
+### Example of using the library along with JQuery:
+```html
+
+
+
+
+
+
+
+
+ My Link
+ Other Link
+
+
```
-Advanced library configuration:
+### Advanced library configuration:
history.js?basepath=/pathtosite/ - the base path to the site; defaults to the root "/".
history.js?redirect=true - enable link translation.
- history.js?type=/ - substitute the string after the anchor; by default, nothing is substituted.
+ history.js?type=/ - substitute the string after the anchor; by default "/".
You can also combine options:
history.js?type=/&redirect=true&basepath=/pathtosite/ - the order of options does not matter.
Or execute special method in JavaScript:
-
- history.redirect( /* type = */ '/', /* basepath = */ '/pathtosite/' );
-
-Demo Site: http://history.spb-piksel.ru/
+```js
+history.redirect(/* type = */ '/', /* basepath = */ '/pathtosite/');
+```
+Demo Site: http://history.spb-piksel.ru/ or http://devote.github.io/demos/history/
Follow me on Twitter: https://twitter.com/DimaPakhtinov
-
-------------------------------------------------------------------------------------------------------------
-РУССКИЙ
+РУССКИЙ (Russian)
=============================================================================================================
Библиотека эмулирует HTML5 History API в старых браузерах.
-Библиотека которая не добавляет ненужные методы заставляя их изучать, а оперирует по спецификации w3c, по интерфейсу History.
+Библиотека, которая не добавляет ненужные методы, заставляя их изучать, а оперирует по спецификации w3c, по интерфейсу History.
Для примера могу привести короткий код как с ней работать.
-По принципу мы работаем с HTML5 History API так как описано например тут http://htmlbook.ru/html5/history или по спецификации http://www.w3.org/TR/html5/history.html#the-history-interface
+По принципу мы работаем с HTML5 History API так как описано, например, тут http://htmlbook.ru/html5/history или по спецификации http://www.w3.org/TR/html5/history.html#the-history-interface
-То-есть коротенький пример:
-
-на чистом JS:
-
-```html
-
-
-
-
-
-
-
- My Link
- Other Link
-
-
+Для включения поддержки плагина HTML5-History-API polyfill в своих библиотеках, добавьте строку кода:
+```js
+var location = window.history.location || window.location;
```
-А тепер показываю пример в связке с jQuery:
+код будет выглядеть примерно так:
+```js
+(function(){
+ // Включает поддержку плагина HTML5-History-API polyfill
+ var location = window.history.location || window.location;
+
+ // код вашей библиотеки
+ // ....
+ // ....
+ // ....
+})();
+```
+### Поддержка AMD:
```html
-
-
-
-
-
-
- My Link
- Other Link
-
+
+
+
+
+
+
```
-Использование события popstate при обычном чистом JS:
-
-```javascript
-window[ window.addEventListener ? 'addEventListener' : 'attachEvent' ]( 'popstate', function( event ) {
-
- // получение location из объекта window.history
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
-
-}, false);
+### Коротенький пример на чистом JS:
+```html
+
+
+
+
+
+
+
+ My Link
+ Other Link
+
+
```
-Использование события popstate в связке jQuery:
+### А теперь показываю пример в связке с jQuery:
-```javascript
-$( window ).bind( 'popstate', function( event ) {
-
- // получение location из объекта window.history
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
-});
+```html
+
+
+
+
+
+
+
+
+ My Link
+ Other Link
+
+
```
-Вы можете использовать дополнительные параметры конфигурации библиотеки:
+### Вы можете использовать дополнительные параметры конфигурации библиотеки:
history.js?basepath=/pathtosite/ - базовый путь к сайту, по умолчанию имеет значение корня "/".
history.js?redirect=true - включить преобразование ссылок.
- history.js?type=/ - подставлять подстроку после якоря, по умолчанию ничего не подставляет.
+ history.js?type=/ - подставлять подстроку после якоря, по умолчанию имеет символ "/".
Также вы можете комбинировать опции:
history.js?type=/&redirect=true&basepath=/pathtosite/ - порядок опций не имеет значение.
Или выполнить специальный метод в JavaScript:
-
- history.redirect( /* type = */ '/', /* basepath = */ '/pathtosite/' );
-
-Демо-сайт: http://history.spb-piksel.ru/
+```js
+history.redirect(/* type = */ '/', /* basepath = */ '/pathtosite/');
+```
+Демо-сайт: http://history.spb-piksel.ru/ или http://devote.github.io/demos/history/
Я в Twitter: https://twitter.com/DimaPakhtinov
diff --git a/bower.json b/bower.json
index 9fda30d..865be98 100644
--- a/bower.json
+++ b/bower.json
@@ -1,8 +1,8 @@
{
- "name": "history",
+ "name": "html5-history-api",
"repo": "devote/HTML5-History-API",
"description": "HTML5 History API expansion for browsers not supporting pushState, replaceState",
- "version": "4.0.5",
+ "version": "4.2.10",
"keywords": ["history", "pushState", "replaceState"],
"main": "history.js",
"scripts": ["history.js"],
@@ -19,8 +19,6 @@
"url": "http://spb-piksel.ru"
},
"licenses": [{
- "type": "GPL3"
- }, {
"type": "MIT"
}]
-}
\ No newline at end of file
+}
diff --git a/component.json b/component.json
new file mode 100644
index 0000000..ef1add6
--- /dev/null
+++ b/component.json
@@ -0,0 +1,23 @@
+{
+ "name": "html5-history-api",
+ "version": "4.2.10",
+ "description": "HTML5 History API expansion for browsers not supporting pushState, replaceState",
+ "main": "history.js",
+ "scripts": ["history.js"],
+ "repository": "devote/HTML5-History-API",
+ "keywords": [
+ "javascript",
+ "html5 history api",
+ "hashchange",
+ "popstate",
+ "pushstate",
+ "replacestate",
+ "hashes",
+ "hashbang",
+ "history",
+ "html5",
+ "devote"
+ ],
+ "author": "Dmitrii Pakhtinov",
+ "license": "MIT"
+}
diff --git a/history.iegte8.js b/history.iegte8.js
deleted file mode 100644
index d82fa25..0000000
--- a/history.iegte8.js
+++ /dev/null
@@ -1,945 +0,0 @@
-/*
- * History API JavaScript Library v4.0.5
- *
- * Support: IE8+, FF3+, Opera 9+, Safari, Chrome and other
- *
- * Copyright 2011-2013, Dmitrii Pakhtinov ( spb.piksel@gmail.com )
- *
- * http://spb-piksel.ru/
- *
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * Update: 20.08.13 21:16
- */
-(function(window) {
- // Prevent the code from running if there is no window.history object
- if (!window.history) return;
- // symlink to document
- var document = window.document;
- // HTML element
- var documentElement = document.documentElement;
- // symlink to sessionStorage
- var sessionStorage = null;
- // symlink to constructor of Object
- var Object = window['Object'];
- // symlink to JSON Object
- var JSON = window['JSON'];
- // symlink to instance object of 'Location'
- var windowLocation = window.location;
- // symlink to instance object of 'History'
- var windowHistory = window.history;
- // new instance of 'History'. The default is a reference to the original object instance
- var historyObject = windowHistory;
- // symlink to method 'history.pushState'
- var historyPushState = windowHistory.pushState;
- // symlink to method 'history.replaceState'
- var historyReplaceState = windowHistory.replaceState;
- // if the browser supports HTML5-History-API
- var isSupportHistoryAPI = !!historyPushState;
- // verifies the presence of an object 'state' in interface 'History'
- var isSupportStateObjectInHistory = 'state' in windowHistory;
- // symlink to method 'Object.defineProperty'
- var defineProperty = Object.defineProperty;
- // new instance of 'Location', for IE8 will use the element HTMLAnchorElement, instead of pure object
- var locationObject = redefineProperty({}, 't') ? {} : document.createElement('a');
- // prefix for the names of events
- var eventNamePrefix = '';
- // String that will contain the name of the method
- var addEventListenerName = window.addEventListener ? 'addEventListener' : (eventNamePrefix = 'on') && 'attachEvent';
- // String that will contain the name of the method
- var removeEventListenerName = window.removeEventListener ? 'removeEventListener' : 'detachEvent';
- // String that will contain the name of the method
- var dispatchEventName = window.dispatchEvent ? 'dispatchEvent' : 'fireEvent';
- // reference native methods for the events
- var addEvent = window[addEventListenerName];
- var removeEvent = window[removeEventListenerName];
- var dispatch = window[dispatchEventName];
- // default settings
- var settings = {"basepath": '/', "redirect": 0, "type": '/'};
- // key for the sessionStorage
- var sessionStorageKey = '__historyAPI__';
- // Anchor Element for parseURL function
- var anchorElement = document.createElement('a');
- // last URL before change to new URL
- var lastURL = windowLocation.href;
- // Control URL, need to fix the bug in Opera
- var checkUrlForPopState = '';
- // trigger event 'onpopstate' on page load
- var isFireInitialState = false;
- // store a list of 'state' objects in the current session
- var stateStorage = {};
- // in this object will be stored custom handlers
- var eventsList = {};
-
- /**
- * Properties that will be replaced in the global
- * object 'window', to prevent conflicts
- *
- * @type {Object}
- */
- var eventsDescriptors = {
- "onhashchange": null,
- "onpopstate": null
- };
-
- /**
- * Fix for Chrome in iOS
- * See https://github.com/devote/HTML5-History-API/issues/29
- */
- var fastFixChrome = function(method, args) {
- var isNeedFix = window.history !== windowHistory;
- if (isNeedFix) {
- window.history = windowHistory;
- }
- method.apply(windowHistory, args);
- if (isNeedFix) {
- window.history = historyObject;
- }
- };
-
- /**
- * Properties that will be replaced/added to object
- * 'window.history', includes the object 'history.location',
- * for a complete the work with the URL address
- *
- * @type {Object}
- */
- var historyDescriptors = {
- /**
- * @namespace history
- * @param {String} [type]
- * @param {String} [basepath]
- */
- "redirect": function(type, basepath) {
- settings["basepath"] = basepath = basepath == null ? settings["basepath"] : basepath;
- settings["type"] = type = type == null ? settings["type"] : type;
- if (window.top == window.self) {
- var relative = parseURL(null, false, true)._relative;
- var path = windowLocation.pathname + windowLocation.search;
- if (isSupportHistoryAPI) {
- path = path.replace(/([^\/])$/, '$1/');
- if (relative != basepath && (new RegExp("^" + basepath + "$", "i")).test(path)) {
- windowLocation.replace(relative);
- }
- } else if (path != basepath) {
- path = path.replace(/([^\/])\?/, '$1/?');
- if ((new RegExp("^" + basepath, "i")).test(path)) {
- windowLocation.replace(basepath + '#' + path.
- replace(new RegExp("^" + basepath, "i"), type) + windowLocation.hash);
- }
- }
- }
- },
- /**
- * The method adds a state object entry
- * to the history.
- *
- * @namespace history
- * @param {Object} state
- * @param {string} title
- * @param {string} [url]
- */
- pushState: function(state, title, url) {
- historyPushState && fastFixChrome(historyPushState, arguments);
- changeState(state, url);
- },
- /**
- * The method updates the state object,
- * title, and optionally the URL of the
- * current entry in the history.
- *
- * @namespace history
- * @param {Object} state
- * @param {string} title
- * @param {string} [url]
- */
- replaceState: function(state, title, url) {
- delete stateStorage[windowLocation.href];
- historyReplaceState && fastFixChrome(historyReplaceState, arguments);
- changeState(state, url, true);
- },
- /**
- * Object 'history.location' is similar to the
- * object 'window.location', except that in
- * HTML4 browsers it will behave a bit differently
- *
- * @namespace history
- */
- "location": {
- set: function(value) {
- window.location = value;
- },
- get: function() {
- return isSupportHistoryAPI ? windowLocation : locationObject;
- }
- },
- /**
- * A state object is an object representing
- * a user interface state.
- *
- * @namespace history
- */
- "state": {
- get: function() {
- return stateStorage[windowLocation.href] || null;
- }
- }
- };
-
- /**
- * Properties for object 'history.location'.
- * Object 'history.location' is similar to the
- * object 'window.location', except that in
- * HTML4 browsers it will behave a bit differently
- *
- * @type {Object}
- */
- var locationDescriptors = {
- /**
- * Navigates to the given page.
- *
- * @namespace history.location
- */
- assign: function(url) {
- if (('' + url).indexOf('#') === 0) {
- changeState(null, url);
- } else {
- windowLocation.assign(url);
- }
- },
- /**
- * Reloads the current page.
- *
- * @namespace history.location
- */
- reload: function() {
- windowLocation.reload();
- },
- /**
- * Removes the current page from
- * the session history and navigates
- * to the given page.
- *
- * @namespace history.location
- */
- replace: function(url) {
- if (('' + url).indexOf('#') === 0) {
- changeState(null, url, true);
- } else {
- windowLocation.replace(url);
- }
- },
- /**
- * Returns the current page's location.
- *
- * @namespace history.location
- */
- toString: function() {
- return this.href;
- },
- /**
- * Returns the current page's location.
- * Can be set, to navigate to another page.
- *
- * @namespace history.location
- */
- "href": {
- get: function() {
- return parseURL()._href;
- }
- },
- /**
- * Returns the current page's protocol.
- *
- * @namespace history.location
- */
- "protocol": null,
- /**
- * Returns the current page's host and port number.
- *
- * @namespace history.location
- */
- "host": null,
- /**
- * Returns the current page's host.
- *
- * @namespace history.location
- */
- "hostname": null,
- /**
- * Returns the current page's port number.
- *
- * @namespace history.location
- */
- "port": null,
- /**
- * Returns the current page's path only.
- *
- * @namespace history.location
- */
- "pathname": {
- get: function() {
- return parseURL()._pathname;
- }
- },
- /**
- * Returns the current page's search
- * string, beginning with the character
- * '?' and to the symbol '#'
- *
- * @namespace history.location
- */
- "search": {
- get: function() {
- return parseURL()._search;
- }
- },
- /**
- * Returns the current page's hash
- * string, beginning with the character
- * '#' and to the end line
- *
- * @namespace history.location
- */
- "hash": {
- set: function(value) {
- changeState(null, ('' + value).replace(/^(#|)/, '#'), false, lastURL);
- },
- get: function() {
- return parseURL()._hash;
- }
- }
- };
-
- /**
- * Just empty function
- *
- * @return void
- */
- function emptyFunction() {
- // dummy
- }
-
- /**
- * Prepares a parts of the current or specified reference for later use in the library
- *
- * @param {string} [href]
- * @param {boolean} [isWindowLocation]
- * @param {boolean} [isNotAPI]
- * @return {Object}
- */
- function parseURL(href, isWindowLocation, isNotAPI) {
- var re = /(?:([\w0-9]+:))?(?:\/\/(?:[^@]*@)?([^\/:\?#]+)(?::([0-9]+))?)?([^\?#]*)(?:(\?[^#]+)|\?)?(?:(#.*))?/;
- if (href && !isWindowLocation) {
- var current = parseURL(), _pathname = current._pathname, _protocol = current._protocol;
- // convert relative link to the absolute
- href = /^(?:[\w0-9]+\:)?\/\//.test(href) ? href.indexOf("/") === 0
- ? _protocol + href : href : _protocol + "//" + current._host + (
- href.indexOf("/") === 0 ? href : href.indexOf("?") === 0
- ? _pathname + href : href.indexOf("#") === 0
- ? _pathname + current._search + href : _pathname.replace(/[^\/]+$/g, '') + href
- );
- } else {
- href = isWindowLocation ? href : windowLocation.href;
- // if current browser not support History-API
- if (!isSupportHistoryAPI || isNotAPI) {
- // get hash fragment
- href = href.replace(/^[^#]*/, '') || "#";
- // form the absolute link from the hash
- href = windowLocation.protocol + '//' + windowLocation.host + settings['basepath']
- + href.replace(new RegExp("^#[\/]?(?:" + settings["type"] + ")?"), "");
- }
- }
- // that would get rid of the links of the form: /../../
- anchorElement.href = href;
- // decompose the link in parts
- var result = re.exec(anchorElement.href);
- // host name with the port number
- var host = result[2] + (result[3] ? ':' + result[3] : '');
- // folder
- var pathname = result[4] || '/';
- // the query string
- var search = result[5] || '';
- // hash
- var hash = result[6] === '#' ? '' : (result[6] || '');
- // relative link, no protocol, no host
- var relative = pathname + search + hash;
- // special links for set to hash-link, if browser not support History API
- var nohash = pathname.replace(new RegExp("^" + settings["basepath"], "i"), settings["type"]) + search;
- // result
- return {
- _href: result[1] + '//' + host + relative,
- _protocol: result[1],
- _host: host,
- _hostname: result[2],
- _port: result[3] || '',
- _pathname: pathname,
- _search: search,
- _hash: hash,
- _relative: relative,
- _nohash: nohash,
- _special: nohash + hash
- }
- }
-
- /**
- * Initializing storage for the custom state's object
- */
- function storageInitialize() {
- var storage = '';
- if (sessionStorage) {
- // get cache from the storage in browser
- storage += sessionStorage.getItem(sessionStorageKey);
- } else {
- var cookie = document.cookie.split(sessionStorageKey + "=");
- if (cookie.length > 1) {
- storage += (cookie.pop().split(";").shift() || 'null');
- }
- }
- try {
- stateStorage = JSON.parse(storage) || {};
- } catch(_e_) {
- stateStorage = {};
- }
- // hang up the event handler to event unload page
- addEvent(eventNamePrefix + 'unload', function() {
- if (sessionStorage) {
- // save current state's object
- sessionStorage.setItem(sessionStorageKey, JSON.stringify(stateStorage));
- } else {
- // save the current 'state' in the cookie
- var state = {};
- if (state[windowLocation.href] = historyObject.state) {
- document.cookie = sessionStorageKey + '=' + JSON.stringify(state);
- }
- }
- }, false);
- }
-
- /**
- * This method is implemented to override the built-in(native)
- * properties in the browser, unfortunately some browsers are
- * not allowed to override all the properties and even add.
- * For this reason, this was written by a method that tries to
- * do everything necessary to get the desired result.
- *
- * @param {Object} object The object in which will be overridden/added property
- * @param {String} prop The property name to be overridden/added
- * @param {Object} [descriptor] An object containing properties set/get
- * @param {Function} [onWrapped] The function to be called when the wrapper is created
- * @return {Object|Boolean} Returns an object on success, otherwise returns false
- */
- function redefineProperty(object, prop, descriptor, onWrapped) {
- // test only if descriptor is undefined
- descriptor = descriptor || {set: emptyFunction};
- // variable will have a value of true the success of attempts to set descriptors
- var isDefinedSetter = !descriptor.set;
- var isDefinedGetter = !descriptor.get;
- // for tests of attempts to set descriptors
- var test = {configurable: true, set: function() {
- isDefinedSetter = 1;
- }, get: function() {
- isDefinedGetter = 1;
- }};
-
- try {
- // testing for the possibility of overriding/adding properties
- defineProperty(object, prop, test);
- // running the test
- object[prop] = object[prop];
- // attempt to override property using the standard method
- defineProperty(object, prop, descriptor);
- } catch(_e_) {
- }
-
- // If the variable 'isDefined' has a false value, it means that need to try other methods
- if (!isDefinedSetter || !isDefinedGetter) {
- // try to override/add the property, using deprecated functions
- if (object.__defineGetter__) {
- // testing for the possibility of overriding/adding properties
- object.__defineGetter__(prop, test.get);
- object.__defineSetter__(prop, test.set);
- // running the test
- object[prop] = object[prop];
- // attempt to override property using the deprecated functions
- descriptor.get && object.__defineGetter__(prop, descriptor.get);
- descriptor.set && object.__defineSetter__(prop, descriptor.set);
- }
-
- // Browser refused to override the property, using the standard and deprecated methods
- if ((!isDefinedSetter || !isDefinedGetter) && object === window) {
- try {
- // save original value from this property
- var originalValue = object[prop];
- // set null to built-in(native) property
- object[prop] = null;
- } catch(_e_) {
- }
- // This rule for Internet Explorer 8
- if ('execScript' in window) {
- /**
- * to IE8 override the global properties using
- * VBScript, declaring it in global scope with
- * the same names.
- */
- window['execScript']('Public ' + prop, 'VBScript');
- } else {
- try {
- /**
- * This hack allows to override a property
- * with the set 'configurable: false', working
- * in the hack 'Safari' to 'Mac'
- */
- defineProperty(object, prop, {value: emptyFunction});
- } catch(_e_) {
- }
- }
- // set old value to new variable
- object[prop] = originalValue;
-
- } else if (!isDefinedSetter || !isDefinedGetter) {
- // the last stage of trying to override the property
- try {
- try {
- // wrap the object in a new empty object
- var temp = Object.create(object);
- defineProperty(Object.getPrototypeOf(temp) === object ? temp : object, prop, descriptor);
- for(var key in object) {
- // need to bind a function to the original object
- if (typeof object[key] === 'function') {
- temp[key] = object[key].bind(object);
- }
- }
- try {
- // to run a function that will inform about what the object was to wrapped
- onWrapped.call(temp, temp, object);
- } catch(_e_) {
- }
- object = temp;
- } catch(_e_) {
- // sometimes works override simply by assigning the prototype property of the constructor
- defineProperty(object.constructor.prototype, prop, descriptor);
- }
- } catch(_e_) {
- // all methods have failed
- return false;
- }
- }
- }
-
- return object;
- }
-
- /**
- * Adds the missing property in descriptor
- *
- * @param {Object} object An object that stores values
- * @param {String} prop Name of the property in the object
- * @param {Object|null} descriptor Descriptor
- * @return {Object} Returns the generated descriptor
- */
- function prepareDescriptorsForObject(object, prop, descriptor) {
- descriptor = descriptor || {};
- // the default for the object 'location' is the standard object 'window.location'
- object = object === locationDescriptors ? windowLocation : object;
- // setter for object properties
- descriptor.set = (descriptor.set || function(value) {
- object[prop] = value;
- });
- // getter for object properties
- descriptor.get = (descriptor.get || function() {
- return object[prop];
- });
- return descriptor;
- }
-
- /**
- * Wrapper for the methods 'addEventListener/attachEvent' in the context of the 'window'
- *
- * @param {String} event The event type for which the user is registering
- * @param {Function} listener The method to be called when the event occurs.
- * @param {Boolean} capture If true, capture indicates that the user wishes to initiate capture.
- * @return void
- */
- function addEventListener(event, listener, capture) {
- if (event in eventsList) {
- // here stored the event listeners 'popstate/hashchange'
- eventsList[event].push(listener);
- } else {
- // FireFox support non-standart four argument aWantsUntrusted
- // https://github.com/devote/HTML5-History-API/issues/13
- if (arguments.length > 3) {
- addEvent(event, listener, capture, arguments[3]);
- } else {
- addEvent(event, listener, capture);
- }
- }
- }
-
- /**
- * Wrapper for the methods 'removeEventListener/detachEvent' in the context of the 'window'
- *
- * @param {String} event The event type for which the user is registered
- * @param {Function} listener The parameter indicates the Listener to be removed.
- * @param {Boolean} capture Was registered as a capturing listener or not.
- * @return void
- */
- function removeEventListener(event, listener, capture) {
- var list = eventsList[event];
- if (list) {
- for(var i = list.length; --i;) {
- if (list[i] === listener) {
- list.splice(i, 1);
- break;
- }
- }
- } else {
- removeEvent(event, listener, capture);
- }
- }
-
- /**
- * Wrapper for the methods 'dispatchEvent/fireEvent' in the context of the 'window'
- *
- * @param {Event|String} event Instance of Event or event type string if 'eventObject' used
- * @param {*} [eventObject] For Internet Explorer 8 required event object on this argument
- * @return {Boolean} If 'preventDefault' was called the value is false, else the value is true.
- */
- function dispatchEvent(event, eventObject) {
- var eventType = ('' + (typeof event === "string" ? event : event.type)).replace(/^on/, '');
- var list = eventsList[eventType];
- if (list) {
- // need to understand that there is one object of Event
- eventObject = typeof event === "string" ? eventObject : event;
- if (eventObject.target == null) {
- // need to override some of the properties of the Event object
- for(var props = ['target', 'currentTarget', 'srcElement', 'type']; event = props.pop();) {
- // use 'redefineProperty' to override the properties
- eventObject = redefineProperty(eventObject, event, {
- get: event === 'type' ? function() {
- return eventType;
- } : function() {
- return window;
- }
- });
- }
- }
- // run function defined in the attributes 'onpopstate/onhashchange' in the 'window' context
- ((eventType === 'popstate' ? window.onpopstate : window.onhashchange)
- || emptyFunction).call(window, eventObject);
- // run other functions that are in the list of handlers
- for(var i = 0, len = list.length; i < len; i++) {
- list[i].call(window, eventObject);
- }
- return true;
- } else {
- return dispatch(event, eventObject);
- }
- }
-
- /**
- * dispatch current state event
- */
- function firePopState() {
- var o = document.createEvent ? document.createEvent('Event') : document.createEventObject();
- if (o.initEvent) {
- o.initEvent('popstate', false, false);
- } else {
- o.type = 'popstate';
- }
- o.state = historyObject.state;
- // send a newly created events to be processed
- dispatchEvent(o);
- }
-
- /**
- * fire initial state for non-HTML5 browsers
- */
- function fireInitialState() {
- if (isFireInitialState) {
- isFireInitialState = false;
- firePopState();
- }
- }
-
- /**
- * Change the data of the current history for HTML4 browsers
- *
- * @param {Object} state
- * @param {string} [url]
- * @param {Boolean} [replace]
- * @param {string} [lastURLValue]
- * @return void
- */
- function changeState(state, url, replace, lastURLValue) {
- if (!isSupportHistoryAPI) {
- // normalization url
- var urlObject = parseURL(url);
- // if current url not equal new url
- if (urlObject._relative !== parseURL()._relative) {
- // if empty lastURLValue to skip hash change event
- lastURL = lastURLValue;
- if (replace) {
- // only replace hash, not store to history
- windowLocation.replace("#" + urlObject._special);
- } else {
- // change hash and add new record to history
- windowLocation.hash = urlObject._special;
- }
- }
- }
- if (!isSupportStateObjectInHistory && state) {
- stateStorage[windowLocation.href] = state;
- }
- isFireInitialState = false;
- }
-
- /**
- * Event handler function changes the hash in the address bar
- *
- * @param {Event} event
- * @return void
- */
- function onHashChange(event) {
- // if not empty lastURL, otherwise skipped the current handler event
- if (lastURL) {
- // if checkUrlForPopState equal current url, this means that the event was raised popstate browser
- if (checkUrlForPopState !== windowLocation.href) {
- // otherwise,
- // the browser does not support popstate event or just does not run the event by changing the hash.
- firePopState();
- }
- // current event object
- event = event || window.event;
-
- var oldURLObject = parseURL(lastURL, true);
- var newURLObject = parseURL();
- // HTML4 browser not support properties oldURL/newURL
- if (!event.oldURL) {
- event.oldURL = oldURLObject._href;
- event.newURL = newURLObject._href;
- }
- if (oldURLObject._hash !== newURLObject._hash) {
- // if current hash not equal previous hash
- dispatchEvent(event);
- }
- }
- // new value to lastURL
- lastURL = windowLocation.href;
- }
-
- /**
- * The event handler is fully loaded document
- *
- * @param {*} [noScroll]
- * @return void
- */
- function onLoad(noScroll) {
- // Get rid of the events popstate when the first loading a document in the webkit browsers
- setTimeout(function() {
- // hang up the event handler for the built-in popstate event in the browser
- addEvent('popstate', function(e) {
- // set the current url, that suppress the creation of the popstate event by changing the hash
- checkUrlForPopState = windowLocation.href;
- // for Safari browser in OS Windows not implemented 'state' object in 'History' interface
- // and not implemented in old HTML4 browsers
- if (!isSupportStateObjectInHistory) {
- e = redefineProperty(e, 'state', {get: function() {
- return historyObject.state;
- }});
- }
- // send events to be processed
- dispatchEvent(e);
- }, false);
- }, 0);
- // for non-HTML5 browsers
- if (!isSupportHistoryAPI && noScroll !== true && historyObject.location) {
- // scroll window to anchor element
- scrollToAnchorId(historyObject.location.hash);
- // fire initial state for non-HTML5 browser after load page
- fireInitialState();
- }
- }
-
- /**
- * handler url with anchor for non-HTML5 browsers
- *
- * @param e
- */
- function onAnchorClick(e) {
- var event = e || window.event;
- var target = event.target || event.srcElement;
- var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false;
- if (target && target.nodeName === "A" && !defaultPrevented) {
- var current = parseURL();
- var expect = parseURL(target.getAttribute("href", 2));
- var isEqualBaseURL = current._href.split('#').shift() === expect._href.split('#').shift();
- if (isEqualBaseURL && expect._hash) {
- if (current._hash !== expect._hash) {
- historyObject.location.hash = expect._hash;
- }
- scrollToAnchorId(expect._hash);
- if (event.preventDefault) {
- event.preventDefault();
- } else {
- event.returnValue = false;
- }
- }
- }
- }
-
- /**
- * Scroll page to current anchor in url-hash
- *
- * @param hash
- */
- function scrollToAnchorId(hash) {
- var target = document.getElementById(hash = (hash || '').replace(/^#/, ''));
- if (target && target.id === hash && target.nodeName === "A") {
- var rect = target.getBoundingClientRect();
- window.scrollTo((documentElement.scrollLeft || 0), rect.top + (documentElement.scrollTop || 0)
- - (documentElement.clientTop || 0));
- }
- }
-
- /**
- * Library initialization
- *
- * @return {Boolean} return true if all is well, otherwise return false value
- */
- function initialize() {
- /**
- * Get custom settings from the query string
- */
- var scripts = document.getElementsByTagName('script');
- var src = (scripts[scripts.length - 1] || {}).src || '';
- var arg = src.indexOf('?') !== -1 ? src.split('?').pop() : '';
- arg.replace(/(\w+)(?:=([^&]*))?/g, function(a, key, value) {
- settings[key] = (value || (key === 'basepath' ? '/' : '')).replace(/^(0|false)$/, '');
- });
-
- /**
- * sessionStorage throws error when cookies are disabled
- * Chrome content settings when running the site in a Facebook IFrame.
- * see: https://github.com/devote/HTML5-History-API/issues/34
- */
- try {
- sessionStorage = window['sessionStorage'];
- } catch(_e_) {}
-
- /**
- * hang up the event handler to listen to the events hashchange
- */
- addEvent(eventNamePrefix + 'hashchange', onHashChange, false);
-
- // a list of objects with pairs of descriptors/object
- var data = [locationDescriptors, locationObject, eventsDescriptors, window, historyDescriptors, historyObject];
-
- // if browser support object 'state' in interface 'History'
- if (isSupportStateObjectInHistory) {
- // remove state property from descriptor
- delete historyDescriptors['state'];
- }
-
- // initializing descriptors
- for(var i = 0; i < data.length; i += 2) {
- for(var prop in data[i]) {
- if (data[i].hasOwnProperty(prop)) {
- if (typeof data[i][prop] === 'function') {
- // If the descriptor is a simple function, simply just assign it an object
- data[i + 1][prop] = data[i][prop];
- } else {
- // prepare the descriptor the required format
- var descriptor = prepareDescriptorsForObject(data[i], prop, data[i][prop]);
- // try to set the descriptor object
- if (!redefineProperty(data[i + 1], prop, descriptor, function(n, o) {
- // is satisfied if the failed override property
- if (o === historyObject) {
- // the problem occurs in Safari on the Mac
- window.history = historyObject = data[i + 1] = n;
- }
- })) {
- // if there is no possibility override.
- // This browser does not support descriptors, such as IE7
-
- // remove previously hung event handlers
- removeEvent(eventNamePrefix + 'hashchange', onHashChange, false);
-
- // fail to initialize :(
- return false;
- }
-
- // create a repository for custom handlers onpopstate/onhashchange
- if (data[i + 1] === window) {
- eventsList[prop] = eventsList[prop.substr(2)] = [];
- }
- }
- }
- }
- }
-
- // redirect if necessary
- if (settings['redirect']) {
- historyObject['redirect']();
- }
-
- // If browser does not support object 'state' in interface 'History'
- if (!isSupportStateObjectInHistory && JSON) {
- storageInitialize();
- }
-
- // track clicks on anchors
- if (!isSupportHistoryAPI) {
- document[addEventListenerName](eventNamePrefix + "click", onAnchorClick, false);
- }
-
- if (document.readyState === 'complete') {
- onLoad(true);
- } else {
- if (!isSupportHistoryAPI && parseURL()._relative !== settings["basepath"]) {
- isFireInitialState = true;
- }
- /**
- * Need to avoid triggering events popstate the initial page load.
- * Hang handler popstate as will be fully loaded document that
- * would prevent triggering event onpopstate
- */
- addEvent(eventNamePrefix + 'load', onLoad, false);
- }
-
- // everything went well
- return true;
- }
-
- /**
- * Starting the library
- */
- if (!initialize()) {
- // if unable to initialize descriptors
- // therefore quite old browser and there
- // is no sense to continue to perform
- return;
- }
-
- /**
- * If the property history.emulate will be true,
- * this will be talking about what's going on
- * emulation capabilities HTML5-History-API.
- * Otherwise there is no emulation, ie the
- * built-in browser capabilities.
- *
- * @type {boolean}
- * @const
- */
- historyObject['emulate'] = !isSupportHistoryAPI;
-
- /**
- * Replace the original methods on the wrapper
- */
- window[addEventListenerName] = addEventListener;
- window[removeEventListenerName] = removeEventListener;
- window[dispatchEventName] = dispatchEvent;
-
-})(window);
\ No newline at end of file
diff --git a/history.iegte8.min.js b/history.iegte8.min.js
deleted file mode 100644
index 81b80e1..0000000
--- a/history.iegte8.min.js
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * History API JavaScript Library v4.0.5
- *
- * Support: IE8+, FF3+, Opera 9+, Safari, Chrome and other
- *
- * Copyright 2011-2013, Dmitrii Pakhtinov ( spb.piksel@gmail.com )
- *
- * http://spb-piksel.ru/
- *
- * Dual licensed under the MIT and GPL licenses:
- * http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
- *
- * Update: 20.08.13 21:16
- */
-(function(e){var i=!0,j=null,n=!1;function D(){}function k(a,b,c){if(a&&!b)var b=k(),c=b.d,f=b.h,a=/^(?:[\w0-9]+\:)?\/\//.test(a)?0===a.indexOf("/")?f+a:a:f+"//"+b.g+(0===a.indexOf("/")?a:0===a.indexOf("?")?c+a:0===a.indexOf("#")?c+b.e+a:c.replace(/[^\/]+$/g,"")+a);else if(a=b?a:d.href,!p||c)a=a.replace(/^[^#]*/,"")||"#",a=d.protocol+"//"+d.host+l.basepath+a.replace(RegExp("^#[/]?(?:"+l.type+")?"),"");I.href=a;var a=/(?:([\w0-9]+:))?(?:\/\/(?:[^@]*@)?([^\/:\?#]+)(?::([0-9]+))?)?([^\?#]*)(?:(\?[^#]+)|\?)?(?:(#.*))?/.exec(I.href),b=
-a[2]+(a[3]?":"+a[3]:""),c=a[4]||"/",f=a[5]||"",e="#"===a[6]?"":a[6]||"",g=c+f+e,h=c.replace(RegExp("^"+l.basepath,"i"),l.type)+f;return{b:a[1]+"//"+b+g,h:a[1],g:b,i:a[2],k:a[3]||"",d:c,e:f,a:e,c:g,j:h,f:h+e}}function W(){var a="";if(t)a+=t.getItem(x);else{var b=g.cookie.split(x+"=");1 1 && cookie.pop().split(";").shift() || 'null';
+ },
+ setItem: function(key, value) {
+ var state = {};
+ // insert one current element to cookie
+ if (state[windowLocation.href] = historyObject.state) {
+ document.cookie = key + '=' + JSON.stringify(state);
+ }
+ }
+ }
+ }
+
+ try {
+ // get cache from the storage in browser
+ stateStorage = JSON.parse(sessionStorage.getItem(sessionStorageKey)) || {};
+ } catch(_e_) {
+ stateStorage = {};
+ }
+
+ // hang up the event handler to event unload page
+ addEvent(eventNamePrefix + 'unload', function() {
+ // save current state's object
+ sessionStorage.setItem(sessionStorageKey, JSON.stringify(stateStorage));
+ }, false);
+ }
+
+ /**
+ * This method is implemented to override the built-in(native)
+ * properties in the browser, unfortunately some browsers are
+ * not allowed to override all the properties and even add.
+ * For this reason, this was written by a method that tries to
+ * do everything necessary to get the desired result.
+ *
+ * @param {Object} object The object in which will be overridden/added property
+ * @param {String} prop The property name to be overridden/added
+ * @param {Object} [descriptor] An object containing properties set/get
+ * @param {Function} [onWrapped] The function to be called when the wrapper is created
+ * @return {Object|Boolean} Returns an object on success, otherwise returns false
+ */
+ function redefineProperty(object, prop, descriptor, onWrapped) {
+ var testOnly = 0;
+ // test only if descriptor is undefined
+ if (!descriptor) {
+ descriptor = {set: emptyFunction};
+ testOnly = 1;
+ }
+ // variable will have a value of true the success of attempts to set descriptors
+ var isDefinedSetter = !descriptor.set;
+ var isDefinedGetter = !descriptor.get;
+ // for tests of attempts to set descriptors
+ var test = {configurable: true, set: function() {
+ isDefinedSetter = 1;
+ }, get: function() {
+ isDefinedGetter = 1;
+ }};
+
+ try {
+ // testing for the possibility of overriding/adding properties
+ defineProperty(object, prop, test);
+ // running the test
+ object[prop] = object[prop];
+ // attempt to override property using the standard method
+ defineProperty(object, prop, descriptor);
+ } catch(_e_) {
+ }
+
+ // If the variable 'isDefined' has a false value, it means that need to try other methods
+ if (!isDefinedSetter || !isDefinedGetter) {
+ // try to override/add the property, using deprecated functions
+ if (object.__defineGetter__) {
+ // testing for the possibility of overriding/adding properties
+ object.__defineGetter__(prop, test.get);
+ object.__defineSetter__(prop, test.set);
+ // running the test
+ object[prop] = object[prop];
+ // attempt to override property using the deprecated functions
+ descriptor.get && object.__defineGetter__(prop, descriptor.get);
+ descriptor.set && object.__defineSetter__(prop, descriptor.set);
+ }
+
+ // Browser refused to override the property, using the standard and deprecated methods
+ if (!isDefinedSetter || !isDefinedGetter) {
+ if (testOnly) {
+ return false;
+ } else if (object === global) {
+ // try override global properties
+ try {
+ // save original value from this property
+ var originalValue = object[prop];
+ // set null to built-in(native) property
+ object[prop] = null;
+ } catch(_e_) {
+ }
+ // This rule for Internet Explorer 8
+ if ('execScript' in global) {
+ /**
+ * to IE8 override the global properties using
+ * VBScript, declaring it in global scope with
+ * the same names.
+ */
+ global['execScript']('Public ' + prop, 'VBScript');
+ global['execScript']('var ' + prop + ';', 'JavaScript');
+ } else {
+ try {
+ /**
+ * This hack allows to override a property
+ * with the set 'configurable: false', working
+ * in the hack 'Safari' to 'Mac'
+ */
+ defineProperty(object, prop, {value: emptyFunction});
+ } catch(_e_) {
+ if (prop === 'onpopstate') {
+ /**
+ * window.onpopstate fires twice in Safari 8.0.
+ * Block initial event on window.onpopstate
+ * See: https://github.com/devote/HTML5-History-API/issues/69
+ */
+ addEvent('popstate', descriptor = function() {
+ removeEvent('popstate', descriptor, false);
+ var onpopstate = object.onpopstate;
+ // cancel initial event on attribute handler
+ object.onpopstate = null;
+ setTimeout(function() {
+ // restore attribute value after short time
+ object.onpopstate = onpopstate;
+ }, 1);
+ }, false);
+ // cancel trigger events on attributes in object the window
+ triggerEventsInWindowAttributes = 0;
+ }
+ }
+ }
+ // set old value to new variable
+ object[prop] = originalValue;
+
+ } else {
+ // the last stage of trying to override the property
+ try {
+ try {
+ // wrap the object in a new empty object
+ var temp = Object.create(object);
+ defineProperty(Object.getPrototypeOf(temp) === object ? temp : object, prop, descriptor);
+ for(var key in object) {
+ // need to bind a function to the original object
+ if (typeof object[key] === 'function') {
+ temp[key] = object[key].bind(object);
+ }
+ }
+ try {
+ // to run a function that will inform about what the object was to wrapped
+ onWrapped.call(temp, temp, object);
+ } catch(_e_) {
+ }
+ object = temp;
+ } catch(_e_) {
+ // sometimes works override simply by assigning the prototype property of the constructor
+ defineProperty(object.constructor.prototype, prop, descriptor);
+ }
+ } catch(_e_) {
+ // all methods have failed
+ return false;
+ }
+ }
+ }
+ }
+
+ return object;
+ }
+
+ /**
+ * Adds the missing property in descriptor
+ *
+ * @param {Object} object An object that stores values
+ * @param {String} prop Name of the property in the object
+ * @param {Object|null} descriptor Descriptor
+ * @return {Object} Returns the generated descriptor
+ */
+ function prepareDescriptorsForObject(object, prop, descriptor) {
+ descriptor = descriptor || {};
+ // the default for the object 'location' is the standard object 'window.location'
+ object = object === locationDescriptors ? windowLocation : object;
+ // setter for object properties
+ descriptor.set = (descriptor.set || function(value) {
+ object[prop] = value;
+ });
+ // getter for object properties
+ descriptor.get = (descriptor.get || function() {
+ return object[prop];
+ });
+ return descriptor;
+ }
+
+ /**
+ * Wrapper for the methods 'addEventListener/attachEvent' in the context of the 'window'
+ *
+ * @param {String} event The event type for which the user is registering
+ * @param {Function} listener The method to be called when the event occurs.
+ * @param {Boolean} capture If true, capture indicates that the user wishes to initiate capture.
+ * @return void
+ */
+ function addEventListener(event, listener, capture) {
+ if (event in eventsList) {
+ // here stored the event listeners 'popstate/hashchange'
+ eventsList[event].push(listener);
+ } else {
+ // FireFox support non-standart four argument aWantsUntrusted
+ // https://github.com/devote/HTML5-History-API/issues/13
+ if (arguments.length > 3) {
+ addEvent(event, listener, capture, arguments[3]);
+ } else {
+ addEvent(event, listener, capture);
+ }
+ }
+ }
+
+ /**
+ * Wrapper for the methods 'removeEventListener/detachEvent' in the context of the 'window'
+ *
+ * @param {String} event The event type for which the user is registered
+ * @param {Function} listener The parameter indicates the Listener to be removed.
+ * @param {Boolean} capture Was registered as a capturing listener or not.
+ * @return void
+ */
+ function removeEventListener(event, listener, capture) {
+ var list = eventsList[event];
+ if (list) {
+ for(var i = list.length; i--;) {
+ if (list[i] === listener) {
+ list.splice(i, 1);
+ break;
+ }
+ }
+ } else {
+ removeEvent(event, listener, capture);
+ }
+ }
+
+ /**
+ * Wrapper for the methods 'dispatchEvent/fireEvent' in the context of the 'window'
+ *
+ * @param {Event|String} event Instance of Event or event type string if 'eventObject' used
+ * @param {*} [eventObject] For Internet Explorer 8 required event object on this argument
+ * @return {Boolean} If 'preventDefault' was called the value is false, else the value is true.
+ */
+ function dispatchEvent(event, eventObject) {
+ var eventType = ('' + (typeof event === "string" ? event : event.type)).replace(/^on/, '');
+ var list = eventsList[eventType];
+ if (list) {
+ // need to understand that there is one object of Event
+ eventObject = typeof event === "string" ? eventObject : event;
+ if (eventObject.target == null) {
+ // need to override some of the properties of the Event object
+ for(var props = ['target', 'currentTarget', 'srcElement', 'type']; event = props.pop();) {
+ // use 'redefineProperty' to override the properties
+ eventObject = redefineProperty(eventObject, event, {
+ get: event === 'type' ? function() {
+ return eventType;
+ } : function() {
+ return global;
+ }
+ });
+ }
+ }
+ if (triggerEventsInWindowAttributes) {
+ // run function defined in the attributes 'onpopstate/onhashchange' in the 'window' context
+ ((eventType === 'popstate' ? global.onpopstate : global.onhashchange)
+ || emptyFunction).call(global, eventObject);
+ }
+ // run other functions that are in the list of handlers
+ for(var i = 0, len = list.length; i < len; i++) {
+ list[i].call(global, eventObject);
+ }
+ return true;
+ } else {
+ return dispatch(event, eventObject);
+ }
+ }
+
+ /**
+ * dispatch current state event
+ */
+ function firePopState() {
+ var o = document.createEvent ? document.createEvent('Event') : document.createEventObject();
+ if (o.initEvent) {
+ o.initEvent('popstate', false, false);
+ } else {
+ o.type = 'popstate';
+ }
+ o.state = historyObject.state;
+ // send a newly created events to be processed
+ dispatchEvent(o);
+ }
+
+ /**
+ * fire initial state for non-HTML5 browsers
+ */
+ function fireInitialState() {
+ if (isFireInitialState) {
+ isFireInitialState = false;
+ firePopState();
+ }
+ }
+
+ /**
+ * Change the data of the current history for HTML4 browsers
+ *
+ * @param {Object} state
+ * @param {string} [url]
+ * @param {Boolean} [replace]
+ * @param {string} [lastURLValue]
+ * @return void
+ */
+ function changeState(state, url, replace, lastURLValue) {
+ if (!isSupportHistoryAPI) {
+ // if not used implementation history.location
+ if (isUsedHistoryLocationFlag === 0) isUsedHistoryLocationFlag = 2;
+ // normalization url
+ var urlObject = parseURL(url, isUsedHistoryLocationFlag === 2 && ('' + url).indexOf("#") !== -1);
+ // if current url not equal new url
+ if (urlObject._relative !== parseURL()._relative) {
+ // if empty lastURLValue to skip hash change event
+ lastURL = lastURLValue;
+ if (replace) {
+ // only replace hash, not store to history
+ windowLocation.replace("#" + urlObject._special);
+ } else {
+ // change hash and add new record to history
+ windowLocation.hash = urlObject._special;
+ }
+ }
+ } else {
+ lastURL = windowLocation.href;
+ }
+ if (!isSupportStateObjectInHistory && state) {
+ stateStorage[windowLocation.href] = state;
+ }
+ isFireInitialState = false;
+ }
+
+ /**
+ * Event handler function changes the hash in the address bar
+ *
+ * @param {Event} event
+ * @return void
+ */
+ function onHashChange(event) {
+ // https://github.com/devote/HTML5-History-API/issues/46
+ var fireNow = lastURL;
+ // new value to lastURL
+ lastURL = windowLocation.href;
+ // if not empty fireNow, otherwise skipped the current handler event
+ if (fireNow) {
+ // if checkUrlForPopState equal current url, this means that the event was raised popstate browser
+ if (checkUrlForPopState !== windowLocation.href) {
+ // otherwise,
+ // the browser does not support popstate event or just does not run the event by changing the hash.
+ firePopState();
+ }
+ // current event object
+ event = event || global.event;
+
+ var oldURLObject = parseURL(fireNow, true);
+ var newURLObject = parseURL();
+ // HTML4 browser not support properties oldURL/newURL
+ if (!event.oldURL) {
+ event.oldURL = oldURLObject._href;
+ event.newURL = newURLObject._href;
+ }
+ if (oldURLObject._hash !== newURLObject._hash) {
+ // if current hash not equal previous hash
+ dispatchEvent(event);
+ }
+ }
+ }
+
+ /**
+ * The event handler is fully loaded document
+ *
+ * @param {*} [noScroll]
+ * @return void
+ */
+ function onLoad(noScroll) {
+ // Get rid of the events popstate when the first loading a document in the webkit browsers
+ setTimeout(function() {
+ // hang up the event handler for the built-in popstate event in the browser
+ addEvent('popstate', function(e) {
+ // set the current url, that suppress the creation of the popstate event by changing the hash
+ checkUrlForPopState = windowLocation.href;
+ // for Safari browser in OS Windows not implemented 'state' object in 'History' interface
+ // and not implemented in old HTML4 browsers
+ if (!isSupportStateObjectInHistory) {
+ e = redefineProperty(e, 'state', {get: function() {
+ return historyObject.state;
+ }});
+ }
+ // send events to be processed
+ dispatchEvent(e);
+ }, false);
+ }, 0);
+ // for non-HTML5 browsers
+ if (!isSupportHistoryAPI && noScroll !== true && "location" in historyObject) {
+ // scroll window to anchor element
+ scrollToAnchorId(locationObject.hash);
+ // fire initial state for non-HTML5 browser after load page
+ fireInitialState();
+ }
+ }
+
+ /**
+ * Finds the closest ancestor anchor element (including the target itself).
+ *
+ * @param {HTMLElement} target The element to start scanning from.
+ * @return {HTMLElement} An element which is the closest ancestor anchor.
+ */
+ function anchorTarget(target) {
+ while (target) {
+ if (target.nodeName === 'A') return target;
+ target = target.parentNode;
+ }
+ }
+
+ /**
+ * Handles anchor elements with a hash fragment for non-HTML5 browsers
+ *
+ * @param {Event} e
+ */
+ function onAnchorClick(e) {
+ var event = e || global.event;
+ var target = anchorTarget(event.target || event.srcElement);
+ var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false;
+ if (target && target.nodeName === "A" && !defaultPrevented) {
+ var current = parseURL();
+ var expect = parseURL(target.getAttribute("href", 2));
+ var isEqualBaseURL = current._href.split('#').shift() === expect._href.split('#').shift();
+ if (isEqualBaseURL && expect._hash) {
+ if (current._hash !== expect._hash) {
+ locationObject.hash = expect._hash;
+ }
+ scrollToAnchorId(expect._hash);
+ if (event.preventDefault) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ }
+ }
+
+ /**
+ * Scroll page to current anchor in url-hash
+ *
+ * @param hash
+ */
+ function scrollToAnchorId(hash) {
+ var target = document.getElementById(hash = (hash || '').replace(/^#/, ''));
+ if (target && target.id === hash && target.nodeName === "A") {
+ var rect = target.getBoundingClientRect();
+ global.scrollTo((documentElement.scrollLeft || 0), rect.top + (documentElement.scrollTop || 0)
+ - (documentElement.clientTop || 0));
+ }
+ }
+
+ /**
+ * Library initialization
+ *
+ * @return {Boolean} return true if all is well, otherwise return false value
+ */
+ function initialize() {
+ /**
+ * Get custom settings from the query string
+ */
+ var scripts = document.getElementsByTagName('script');
+ var src = (scripts[scripts.length - 1] || {}).src || '';
+ var arg = src.indexOf('?') !== -1 ? src.split('?').pop() : '';
+ arg.replace(/(\w+)(?:=([^&]*))?/g, function(a, key, value) {
+ settings[key] = (value || '').replace(/^(0|false)$/, '');
+ });
+
+ /**
+ * Includes support for IE6+
+ */
+ ie6DriverStart();
+
+ /**
+ * hang up the event handler to listen to the events hashchange
+ */
+ addEvent(eventNamePrefix + 'hashchange', onHashChange, false);
+
+ // a list of objects with pairs of descriptors/object
+ var data = [locationDescriptors, locationObject, eventsDescriptors, global, historyDescriptors, historyObject];
+
+ // if browser support object 'state' in interface 'History'
+ if (isSupportStateObjectInHistory) {
+ // remove state property from descriptor
+ delete historyDescriptors['state'];
+ }
+
+ // initializing descriptors
+ for(var i = 0; i < data.length; i += 2) {
+ for(var prop in data[i]) {
+ if (data[i].hasOwnProperty(prop)) {
+ if (typeof data[i][prop] !== 'object') {
+ // If the descriptor is a simple function, simply just assign it an object
+ data[i + 1][prop] = data[i][prop];
+ } else {
+ // prepare the descriptor the required format
+ var descriptor = prepareDescriptorsForObject(data[i], prop, data[i][prop]);
+ // try to set the descriptor object
+ if (!redefineProperty(data[i + 1], prop, descriptor, function(n, o) {
+ // is satisfied if the failed override property
+ if (o === historyObject) {
+ // the problem occurs in Safari on the Mac
+ global.history = historyObject = data[i + 1] = n;
+ }
+ })) {
+ // if there is no possibility override.
+ // This browser does not support descriptors, such as IE7
+
+ // remove previously hung event handlers
+ removeEvent(eventNamePrefix + 'hashchange', onHashChange, false);
+
+ // fail to initialize :(
+ return false;
+ }
+
+ // create a repository for custom handlers onpopstate/onhashchange
+ if (data[i + 1] === global) {
+ eventsList[prop] = eventsList[prop.substr(2)] = [];
+ }
+ }
+ }
+ }
+ }
+
+ // check settings
+ historyObject['setup']();
+
+ // redirect if necessary
+ if (settings['redirect']) {
+ historyObject['redirect']();
+ }
+
+ // initialize
+ if (settings["init"]) {
+ // You agree that you will use window.history.location instead window.location
+ isUsedHistoryLocationFlag = 1;
+ }
+
+ // If browser does not support object 'state' in interface 'History'
+ if (!isSupportStateObjectInHistory && JSON) {
+ storageInitialize();
+ }
+
+ // track clicks on anchors
+ if (!isSupportHistoryAPI) {
+ document[addEventListenerName](eventNamePrefix + "click", onAnchorClick, false);
+ }
+
+ if (document.readyState === 'complete') {
+ onLoad(true);
+ } else {
+ if (!isSupportHistoryAPI && parseURL()._relative !== settings["basepath"]) {
+ isFireInitialState = true;
+ }
+ /**
+ * Need to avoid triggering events popstate the initial page load.
+ * Hang handler popstate as will be fully loaded document that
+ * would prevent triggering event onpopstate
+ */
+ addEvent(eventNamePrefix + 'load', onLoad, false);
+ }
+
+ // everything went well
+ return true;
+ }
+
+ /**
+ * Starting the library
+ */
+ if (!initialize()) {
+ // if unable to initialize descriptors
+ // therefore quite old browser and there
+ // is no sense to continue to perform
+ return;
+ }
+
+ /**
+ * If the property history.emulate will be true,
+ * this will be talking about what's going on
+ * emulation capabilities HTML5-History-API.
+ * Otherwise there is no emulation, ie the
+ * built-in browser capabilities.
+ *
+ * @type {boolean}
+ * @const
+ */
+ historyObject['emulate'] = !isSupportHistoryAPI;
+
+ /**
+ * Replace the original methods on the wrapper
+ */
+ global[addEventListenerName] = addEventListener;
+ global[removeEventListenerName] = removeEventListener;
+ global[dispatchEventName] = dispatchEvent;
+
+ return historyObject;
+
+ // ====================================================================================== //
+ // Driver for IE6+ or below
+ // ====================================================================================== //
+ function ie6DriverStart() {
+ /**
+ * Creates a static object
+ *
+ * @param object
+ * @returns {*}
+ */
+ function createVBObjects(object) {
+ var properties = [];
+ var className = 'VBHistoryClass' + (new Date()).getTime() + msie++;
+ var staticClassParts = ["Class " + className];
+ for(var prop in object) {
+ if (object.hasOwnProperty(prop)) {
+ var value = object[prop];
+ if (value && (value.get || value.set)) {
+ if (value.get) {
+ staticClassParts.push(
+ 'Public ' + (prop === '_' ? 'Default ' : '') + 'Property Get [' + prop + ']',
+ 'Call VBCVal([(accessors)].[' + prop + '].get.call(me),[' + prop + '])',
+ 'End Property'
+ );
+ }
+ if (value.set) {
+ staticClassParts.push('Public Property Let [' + prop + '](val)',
+ (value = 'Call [(accessors)].[' + prop + '].set.call(me,val)\nEnd Property'),
+ 'Public Property Set [' + prop + '](val)', value);
+ }
+ } else {
+ properties.push(prop);
+ staticClassParts.push('Public [' + prop + ']');
+ }
+ }
+ }
+ staticClassParts.push(
+ 'Private [(accessors)]',
+ 'Private Sub Class_Initialize()',
+ 'Set [(accessors)]=' + className + 'FactoryJS()',
+ 'End Sub',
+ 'End Class',
+ 'Function ' + className + 'Factory()',
+ 'Set ' + className + 'Factory=New ' + className,
+ 'End Function'
+ );
+ global['execScript'](staticClassParts.join('\n'), 'VBScript');
+ global[className + 'FactoryJS'] = function() {
+ return object;
+ };
+ var result = global[className + 'Factory']();
+ for(var i = 0; i < properties.length; i++) {
+ result[properties[i]] = object[properties[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Escape special symbols
+ *
+ * @param string
+ * @returns {string}
+ */
+ function quote(string) {
+ var escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
+ var meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\'};
+ return escapable.test(string) ? '"' + string.replace(escapable, function(a) {
+ return a in meta ? meta[a] : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
+ }) + '"' : '"' + string + '"';
+ }
+
+ // testing IE browser
+ var msie = global['execScript'] && (global['execScript']('var documentMsie/*@cc_on =1@*/;', 'JavaScript'), global['documentMsie']);
+ if (!msie || (document.documentMode && document.documentMode > 7)) {
+ // If it is not IE or a version greater than seven
+ return;
+ }
+
+ // save original links to methods
+ var originalChangeState = changeState;
+ var originalRedefineProperty = redefineProperty;
+ var currentHref = parseURL()._href;
+ var iFrame = document.createElement('iframe');
+
+ // insert IFRAME element to DOM
+ iFrame.src = "javascript:true;";
+ iFrame = documentElement.firstChild.appendChild(iFrame).contentWindow;
+
+ // correction value for VB Script
+ global['execScript'](
+ 'Public history\nFunction VBCVal(o,r) If IsObject(o) Then Set r=o Else r=o End If End Function',
+ 'VBScript'
+ );
+
+ // renew standard object
+ locationObject = {"_": {get: locationDescriptors.toString}};
+ historyObject = {
+ // properties to create an object in IE
+ "back": windowHistory.back,
+ "forward": windowHistory.forward,
+ "go": windowHistory.go,
+ "emulate": null,
+ "_": {get: function() {
+ return '[object History]';
+ }}
+ };
+
+ JSON = {
+ /**
+ * Analogue of JSON.parse()
+ *
+ * @param value
+ * @returns {*}
+ */
+ "parse": function(value) {
+ try {
+ return new Function('', 'return ' + value)();
+ } catch(_e_) {
+ return null;
+ }
+ },
+ /**
+ * Analogue of JSON.stringify()
+ *
+ * @param value
+ * @returns {*}
+ */
+ "stringify": function(value) {
+ var n = (typeof value).charCodeAt(2);
+ return n === 114 ? quote(value) : n === 109 ? isFinite(value) ? String(value) : 'null' : n === 111 || n
+ === 108 ? String(value) : n === 106 ? !value ? 'null' : (function(isArray) {
+ var out = isArray ? '[' : '{';
+ if (isArray) {
+ for(var i = 0; i < value.length; i++) {
+ out += (i == 0 ? "" : ",") + JSON.stringify(value[i]);
+ }
+ } else {
+ for(var k in value) {
+ if (value.hasOwnProperty(k)) {
+ out += (out.length == 1 ? "" : ",") + quote(k) + ":" + JSON.stringify(value[k]);
+ }
+ }
+ }
+ return out + (isArray ? ']' : '}');
+ })(Object.prototype.toString.call(value) === '[object Array]') : 'void 0';
+ }
+ };
+
+ /**
+ * Change the data of the current history for IE6+
+ */
+ changeState = function(state, url, replace, lastURLValue, lfirst) {
+ var iFrameDocument = iFrame.document;
+ // if not used implementation history.location
+ if (isUsedHistoryLocationFlag === 0) isUsedHistoryLocationFlag = 2;
+ // normalization url
+ var urlObject = parseURL(url, isUsedHistoryLocationFlag === 2 && ('' + url).indexOf("#") !== -1);
+ isFireInitialState = false;
+ if (urlObject._relative === parseURL()._relative && !lfirst) {
+ if (state) {
+ stateStorage[windowLocation.href] = state;
+ }
+ return;
+ }
+ lastURL = lastURLValue;
+ if (replace) {
+ if (iFrame["lfirst"]) {
+ history.back();
+ changeState(state, urlObject._href, 0, lastURLValue, 1);
+ } else {
+ windowLocation.replace("#" + urlObject._special);
+ }
+ } else if (urlObject._href != currentHref || lfirst) {
+ if (!iFrame['lfirst']) {
+ iFrame["lfirst"] = 1;
+ changeState(state, currentHref, 0, lastURLValue, 1);
+ } else if (lfirst) {
+ lfirst = 0;
+ state = stateStorage[windowLocation.href];
+ }
+ iFrameDocument.open();
+ iFrameDocument.write('\x3Cscript\x3Elfirst=1;parent.location.hash="'
+ + urlObject._special.replace(/"/g, '\\"') + '";\x3C/script\x3E');
+ iFrameDocument.close();
+ }
+ if (!lfirst && state) {
+ stateStorage[windowLocation.href] = state;
+ }
+ };
+
+ /**
+ * See original method
+ */
+ redefineProperty = function(object, prop, descriptor, onWrapped) {
+ if (!originalRedefineProperty.apply(this, arguments)) {
+ if (object === locationObject) {
+ locationObject[prop] = descriptor;
+ } else if (object === historyObject) {
+ historyObject[prop] = descriptor;
+ if (prop === 'state') {
+ locationObject = createVBObjects(locationObject);
+ global.history = historyObject = createVBObjects(historyObject);
+ // hack for IE7
+ global['execScript']('var history = window.history;', 'JavaScript');
+ }
+ } else {
+ object[prop] = descriptor.get && descriptor.get();
+ }
+ }
+ return object;
+ };
+
+ /**
+ * Tracking changes in the hash in the address bar
+ */
+ var interval = setInterval(function() {
+ var href = parseURL()._href;
+ if (href != currentHref) {
+ var e = document.createEventObject();
+ e.oldURL = currentHref;
+ e.newURL = currentHref = href;
+ e.type = 'hashchange';
+ onHashChange(e);
+ }
+ }, 100);
+
+ global['JSON'] = JSON;
+ }
+});
diff --git a/history.ielte7.min.js b/history.ielte7.min.js
new file mode 100644
index 0000000..7758572
--- /dev/null
+++ b/history.ielte7.min.js
@@ -0,0 +1,36 @@
+/*!
+ * History API JavaScript Library v4.2.10
+ *
+ * Support: IE6+, FF3+, Opera 9+, Safari, Chrome and other
+ *
+ * Copyright 2011-2018, Dmitrii Pakhtinov ( spb.piksel@gmail.com )
+ *
+ * http://spb-piksel.ru/
+ *
+ * MIT license:
+ * http://www.opensource.org/licenses/mit-license.php
+ *
+ * Update: 2018-04-15 13:54
+ */
+(function(q){if("function"===typeof define&&define.amd){if("undefined"!==typeof requirejs){var v=requirejs,l="[history"+(new Date).getTime()+"]",D=v.onError;q.toString=function(){return l};v.onError=function(q){-1===q.message.indexOf(l)&&D.call(v,q)}}define([],q)}if("object"===typeof exports&&"undefined"!==typeof module)module.exports=q();else return q()})(function(){var i=!0,k=null,p=!1;function q(a,b){var c=e.history!==r;c&&(e.history=r);a.apply(r,b);c&&(e.history=m)}function v(){}function l(a,b,c){if(a!=k&&""!==a&&
+!b){var b=l(),g=f.getElementsByTagName("base")[0];!c&&(g&&g.getAttribute("href"))&&(g.href=g.href,b=l(g.href,k,i));c=b.e;g=b.h;a=""+a;a=/^(?:\w+\:)?\/\//.test(a)?0===a.indexOf("/")?g+a:a:g+"//"+b.g+(0===a.indexOf("/")?a:0===a.indexOf("?")?c+a:0===a.indexOf("#")?c+b.f+a:c.replace(/[^\/]+$/g,"")+a)}else if(a=b?a:d.href,!j||c)a=a.replace(/^[^#]*/,"")||"#",a=d.protocol.replace(/:.*$|$/,":")+"//"+d.host+h.basepath+a.replace(RegExp("^#[/]?(?:"+h.type+")?"),"");S.href=a;var a=/(?:([a-zA-Z0-9\-]+\:))?(?:\/\/(?:[^@]*@)?([^\/:\?#]+)(?::([0-9]+))?)?([^\?#]*)(?:(\?[^#]+)|\?)?(?:(#.*))?/.exec(S.href),
+b=a[2]+(a[3]?":"+a[3]:""),c=a[4]||"/",g=a[5]||"",e="#"===a[6]?"":a[6]||"",T=c+g+e,x=c.replace(RegExp("^"+h.basepath,"i"),h.type)+g;return{a:a[1]+"//"+b+T,h:a[1],g:b,i:a[2],k:a[3]||"",e:c,f:g,b:e,c:T,j:x,d:x+e}}function D(a){return a&&e&&e.EventTarget&&"function"===typeof e.EventTarget.prototype.addEventListener&&"function"===typeof a.bind?a.bind(e):a}function ca(){var a;try{a=e.sessionStorage,a.setItem(G+"t","1"),a.removeItem(G+"t")}catch(b){a={getItem:function(a){a=f.cookie.split(a+"=");return 1<
+a.length&&a.pop().split(";").shift()||"null"},setItem:function(a){var b={};if(b[d.href]=m.state)f.cookie=a+"="+s.stringify(b)}}}try{n=s.parse(a.getItem(G))||{}}catch(c){n={}}w(y+"unload",function(){a.setItem(G,s.stringify(n))},p)}function z(a,b,c,g){var d=0;c||(c={set:v},d=1);var f=!c.set,x=!c.get,L={configurable:i,set:function(){f=1},get:function(){x=1}};try{E(a,b,L),a[b]=a[b],E(a,b,c)}catch(la){}if(!f||!x)if(a.__defineGetter__&&(a.__defineGetter__(b,L.get),a.__defineSetter__(b,L.set),a[b]=a[b],
+c.get&&a.__defineGetter__(b,c.get),c.set&&a.__defineSetter__(b,c.set)),!f||!x){if(d)return p;if(a===e){try{var da=a[b];a[b]=k}catch(l){}if("execScript"in e)e.execScript("Public "+b,"VBScript"),e.execScript("var "+b+";","JavaScript");else try{E(a,b,{value:v})}catch(m){"onpopstate"===b&&(w("popstate",c=function(){M("popstate",c,p);var b=a.onpopstate;a.onpopstate=k;setTimeout(function(){a.onpopstate=b},1)},p),U=0)}a[b]=da}else try{try{var h=H.create(a);E(H.getPrototypeOf(h)===a?h:a,b,c);for(var j in a)"function"===
+typeof a[j]&&(h[j]=a[j].bind(a));try{g.call(h,h,a)}catch(n){}a=h}catch(o){E(a.constructor.prototype,b,c)}}catch(q){return p}}return a}function ea(a,b,c){c=c||{};a=a===N?d:a;c.set=c.set||function(c){a[b]=c};c.get=c.get||function(){return a[b]};return c}function fa(a,b,c){a in A?A[a].push(b):3lfirst=1;parent.location.hash="'+b.d.replace(/"/g,
+'\\"')+'";<\/script>'),f.close();!g&&a&&(n[d.href]=a)}};z=function(b,c,d,f){g.apply(this,arguments)||(b===t?t[c]=d:b===m?(m[c]=d,"state"===c&&(t=a(t),e.history=m=a(m),e.execScript("var history = window.history;","JavaScript"))):b[c]=d.get&&d.get());return b};setInterval(function(){var a=l().a;if(a!=h){var b=f.createEventObject();b.oldURL=h;b.newURL=h=a;b.type="hashchange";O(b)}},100);e.JSON=s}}var e=("object"===typeof window?window:this)||{};if(!e.history||"emulate"in e.history)return e.history;var f=
+e.document,K=f.documentElement,H=e.Object,s=e.JSON,d=e.location,r=e.history,m=r,P=r.pushState,Z=r.replaceState,j=function(){var a=e.navigator.userAgent;return(-1!==a.indexOf("Android 2.")||-1!==a.indexOf("Android 4.0"))&&-1!==a.indexOf("Mobile Safari")&&-1===a.indexOf("Chrome")&&-1===a.indexOf("Windows Phone")?p:!!P}(),J="state"in r,E=H.defineProperty,t=z({},"t")?{}:f.createElement("a"),y="",Q=e.addEventListener?"addEventListener":(y="on")&&"attachEvent",$=e.removeEventListener?"removeEventListener":
+"detachEvent",aa=e.dispatchEvent?"dispatchEvent":"fireEvent",w=D(e[Q]),M=D(e[$]),ha=D(e[aa]),h={basepath:"/",redirect:0,type:"/",init:0},G="__historyAPI__",S=f.createElement("a"),B=d.href,W="",U=1,F=p,o=0,n={},A={},C=f.title,R,ka={onhashchange:k,onpopstate:k},ba={setup:function(a,b,c){h.basepath=(""+(a==k?h.basepath:a)).replace(/(?:^|\/)[^\/]*$/,"/");h.type=b==k?h.type:b;h.redirect=c==k?h.redirect:!!c},redirect:function(a,b){m.setup(b,a);b=h.basepath;if(e.top==e.self){var c=l(k,p,i).c,g=d.pathname+
+d.search;j?(g=g.replace(/([^\/])$/,"$1/"),c!=b&&RegExp("^"+b+"$","i").test(g)&&d.replace(c)):g!=b&&(g=g.replace(/([^\/])\?/,"$1/?"),RegExp("^"+b,"i").test(g)&&d.replace(b+"#"+g.replace(RegExp("^"+b,"i"),h.type)+d.hash))}},pushState:function(a,b,c){var e=f.title;C!=k&&(f.title=C);P&&q(P,arguments);u(a,c);f.title=e;C=b},replaceState:function(a,b,c){var e=f.title;C!=k&&(f.title=C);delete n[d.href];Z&&q(Z,arguments);u(a,c,i);f.title=e;C=b},location:{set:function(a){0===o&&(o=1);e.location=a},get:function(){0===
+o&&(o=1);return t}},state:{get:function(){return"object"===typeof n[d.href]?s.parse(s.stringify(n[d.href])):"undefined"!==typeof n[d.href]?n[d.href]:k}}},N={assign:function(a){!j&&0===(""+a).indexOf("#")?u(k,a):d.assign(a)},reload:function(a){d.reload(a)},replace:function(a){!j&&0===(""+a).indexOf("#")?u(k,a,i):d.replace(a)},toString:function(){return this.href},origin:{get:function(){return void 0!==R?R:!d.origin?d.protocol+"//"+d.hostname+(d.port?":"+d.port:""):d.origin},set:function(a){R=a}},href:j?
+k:{get:function(){return l().a}},protocol:k,host:k,hostname:k,port:k,pathname:j?k:{get:function(){return l().e}},search:j?k:{get:function(){return l().f}},hash:j?k:{set:function(a){u(k,(""+a).replace(/^(#|)/,"#"),p,B)},get:function(){return l().b}}};if(function(){var a=f.getElementsByTagName("script"),a=(a[a.length-1]||{}).src||"";(-1!==a.indexOf("?")?a.split("?").pop():"").replace(/(\w+)(?:=([^&]*))?/g,function(a,b,c){h[b]=(c||"").replace(/^(0|false)$/,"")});ja();w(y+"hashchange",O,p);var b=[N,t,
+ka,e,ba,m];J&&delete ba.state;for(var c=0;c 1) {
- storage += (cookie.pop().split(";").shift() || 'null');
- }
- }
- try {
- stateStorage = JSON.parse(storage) || {};
- } catch(_e_) {
- stateStorage = {};
- }
- // hang up the event handler to event unload page
- addEvent(eventNamePrefix + 'unload', function() {
- if (sessionStorage) {
- // save current state's object
- sessionStorage.setItem(sessionStorageKey, JSON.stringify(stateStorage));
- } else {
- // save the current 'state' in the cookie
- var state = {};
- if (state[windowLocation.href] = historyObject.state) {
- document.cookie = sessionStorageKey + '=' + JSON.stringify(state);
- }
- }
- }, false);
- }
-
+ reload: function(flag) {
+ windowLocation.reload(flag);
+ },
/**
- * This method is implemented to override the built-in(native)
- * properties in the browser, unfortunately some browsers are
- * not allowed to override all the properties and even add.
- * For this reason, this was written by a method that tries to
- * do everything necessary to get the desired result.
+ * Removes the current page from
+ * the session history and navigates
+ * to the given page.
*
- * @param {Object} object The object in which will be overridden/added property
- * @param {String} prop The property name to be overridden/added
- * @param {Object} [descriptor] An object containing properties set/get
- * @param {Function} [onWrapped] The function to be called when the wrapper is created
- * @return {Object|Boolean} Returns an object on success, otherwise returns false
+ * @namespace history.location
*/
- function redefineProperty(object, prop, descriptor, onWrapped) {
- // test only if descriptor is undefined
- descriptor = descriptor || {set: emptyFunction};
- // variable will have a value of true the success of attempts to set descriptors
- var isDefinedSetter = !descriptor.set;
- var isDefinedGetter = !descriptor.get;
- // for tests of attempts to set descriptors
- var test = {configurable: true, set: function() {
- isDefinedSetter = 1;
- }, get: function() {
- isDefinedGetter = 1;
- }};
-
- try {
- // testing for the possibility of overriding/adding properties
- defineProperty(object, prop, test);
- // running the test
- object[prop] = object[prop];
- // attempt to override property using the standard method
- defineProperty(object, prop, descriptor);
- } catch(_e_) {
- }
-
- // If the variable 'isDefined' has a false value, it means that need to try other methods
- if (!isDefinedSetter || !isDefinedGetter) {
- // try to override/add the property, using deprecated functions
- if (object.__defineGetter__) {
- // testing for the possibility of overriding/adding properties
- object.__defineGetter__(prop, test.get);
- object.__defineSetter__(prop, test.set);
- // running the test
- object[prop] = object[prop];
- // attempt to override property using the deprecated functions
- descriptor.get && object.__defineGetter__(prop, descriptor.get);
- descriptor.set && object.__defineSetter__(prop, descriptor.set);
- }
-
- // Browser refused to override the property, using the standard and deprecated methods
- if ((!isDefinedSetter || !isDefinedGetter) && object === window) {
- try {
- // save original value from this property
- var originalValue = object[prop];
- // set null to built-in(native) property
- object[prop] = null;
- } catch(_e_) {
- }
- // This rule for Internet Explorer 8
- if ('execScript' in window) {
- /**
- * to IE8 override the global properties using
- * VBScript, declaring it in global scope with
- * the same names.
- */
- window['execScript']('Public ' + prop, 'VBScript');
- } else {
- try {
- /**
- * This hack allows to override a property
- * with the set 'configurable: false', working
- * in the hack 'Safari' to 'Mac'
- */
- defineProperty(object, prop, {value: emptyFunction});
- } catch(_e_) {
- }
- }
- // set old value to new variable
- object[prop] = originalValue;
-
- } else if (!isDefinedSetter || !isDefinedGetter) {
- // the last stage of trying to override the property
- try {
- try {
- // wrap the object in a new empty object
- var temp = Object.create(object);
- defineProperty(Object.getPrototypeOf(temp) === object ? temp : object, prop, descriptor);
- for(var key in object) {
- // need to bind a function to the original object
- if (typeof object[key] === 'function') {
- temp[key] = object[key].bind(object);
- }
- }
- try {
- // to run a function that will inform about what the object was to wrapped
- onWrapped.call(temp, temp, object);
- } catch(_e_) {
- }
- object = temp;
- } catch(_e_) {
- // sometimes works override simply by assigning the prototype property of the constructor
- defineProperty(object.constructor.prototype, prop, descriptor);
- }
- } catch(_e_) {
- // all methods have failed
- return false;
- }
- }
- }
-
- return object;
- }
-
+ replace: function(url) {
+ if (!isSupportHistoryAPI && ('' + url).indexOf('#') === 0) {
+ changeState(null, url, true);
+ } else {
+ windowLocation.replace(url);
+ }
+ },
/**
- * Adds the missing property in descriptor
+ * Returns the current page's location.
*
- * @param {Object} object An object that stores values
- * @param {String} prop Name of the property in the object
- * @param {Object|null} descriptor Descriptor
- * @return {Object} Returns the generated descriptor
+ * @namespace history.location
*/
- function prepareDescriptorsForObject(object, prop, descriptor) {
- descriptor = descriptor || {};
- // the default for the object 'location' is the standard object 'window.location'
- object = object === locationDescriptors ? windowLocation : object;
- // setter for object properties
- descriptor.set = (descriptor.set || function(value) {
- object[prop] = value;
- });
- // getter for object properties
- descriptor.get = (descriptor.get || function() {
- return object[prop];
- });
- return descriptor;
- }
-
+ toString: function() {
+ return this.href;
+ },
/**
- * Wrapper for the methods 'addEventListener/attachEvent' in the context of the 'window'
+ * Returns the current origin.
*
- * @param {String} event The event type for which the user is registering
- * @param {Function} listener The method to be called when the event occurs.
- * @param {Boolean} capture If true, capture indicates that the user wishes to initiate capture.
- * @return void
+ * @namespace history.location
*/
- function addEventListener(event, listener, capture) {
- if (event in eventsList) {
- // here stored the event listeners 'popstate/hashchange'
- eventsList[event].push(listener);
- } else {
- // FireFox support non-standart four argument aWantsUntrusted
- // https://github.com/devote/HTML5-History-API/issues/13
- if (arguments.length > 3) {
- addEvent(event, listener, capture, arguments[3]);
- } else {
- addEvent(event, listener, capture);
- }
+ "origin": {
+ get: function() {
+ if (customOrigin !== void 0) {
+ return customOrigin;
}
- }
-
+ if (!windowLocation.origin) {
+ return windowLocation.protocol + "//" + windowLocation.hostname + (windowLocation.port ? ':' + windowLocation.port: '');
+ }
+ return windowLocation.origin;
+ },
+ set: function(value) {
+ customOrigin = value;
+ }
+ },
/**
- * Wrapper for the methods 'removeEventListener/detachEvent' in the context of the 'window'
+ * Returns the current page's location.
+ * Can be set, to navigate to another page.
*
- * @param {String} event The event type for which the user is registered
- * @param {Function} listener The parameter indicates the Listener to be removed.
- * @param {Boolean} capture Was registered as a capturing listener or not.
- * @return void
+ * @namespace history.location
*/
- function removeEventListener(event, listener, capture) {
- var list = eventsList[event];
- if (list) {
- for(var i = list.length; --i;) {
- if (list[i] === listener) {
- list.splice(i, 1);
- break;
- }
- }
- } else {
- removeEvent(event, listener, capture);
- }
- }
-
+ "href": isSupportHistoryAPI ? null : {
+ get: function() {
+ return parseURL()._href;
+ }
+ },
/**
- * Wrapper for the methods 'dispatchEvent/fireEvent' in the context of the 'window'
+ * Returns the current page's protocol.
*
- * @param {Event|String} event Instance of Event or event type string if 'eventObject' used
- * @param {*} [eventObject] For Internet Explorer 8 required event object on this argument
- * @return {Boolean} If 'preventDefault' was called the value is false, else the value is true.
+ * @namespace history.location
*/
- function dispatchEvent(event, eventObject) {
- var eventType = ('' + (typeof event === "string" ? event : event.type)).replace(/^on/, '');
- var list = eventsList[eventType];
- if (list) {
- // need to understand that there is one object of Event
- eventObject = typeof event === "string" ? eventObject : event;
- if (eventObject.target == null) {
- // need to override some of the properties of the Event object
- for(var props = ['target', 'currentTarget', 'srcElement', 'type']; event = props.pop();) {
- // use 'redefineProperty' to override the properties
- eventObject = redefineProperty(eventObject, event, {
- get: event === 'type' ? function() {
- return eventType;
- } : function() {
- return window;
- }
- });
- }
- }
- // run function defined in the attributes 'onpopstate/onhashchange' in the 'window' context
- ((eventType === 'popstate' ? window.onpopstate : window.onhashchange)
- || emptyFunction).call(window, eventObject);
- // run other functions that are in the list of handlers
- for(var i = 0, len = list.length; i < len; i++) {
- list[i].call(window, eventObject);
- }
- return true;
- } else {
- return dispatch(event, eventObject);
- }
- }
-
+ "protocol": null,
/**
- * dispatch current state event
+ * Returns the current page's host and port number.
+ *
+ * @namespace history.location
*/
- function firePopState() {
- var o = document.createEvent ? document.createEvent('Event') : document.createEventObject();
- if (o.initEvent) {
- o.initEvent('popstate', false, false);
- } else {
- o.type = 'popstate';
- }
- o.state = historyObject.state;
- // send a newly created events to be processed
- dispatchEvent(o);
- }
-
+ "host": null,
/**
- * fire initial state for non-HTML5 browsers
+ * Returns the current page's host.
+ *
+ * @namespace history.location
*/
- function fireInitialState() {
- if (isFireInitialState) {
- isFireInitialState = false;
- firePopState();
- }
- }
-
+ "hostname": null,
/**
- * Change the data of the current history for HTML4 browsers
+ * Returns the current page's port number.
*
- * @param {Object} state
- * @param {string} [url]
- * @param {Boolean} [replace]
- * @param {string} [lastURLValue]
- * @return void
+ * @namespace history.location
*/
- function changeState(state, url, replace, lastURLValue) {
- if (!isSupportHistoryAPI) {
- // normalization url
- var urlObject = parseURL(url);
- // if current url not equal new url
- if (urlObject._relative !== parseURL()._relative) {
- // if empty lastURLValue to skip hash change event
- lastURL = lastURLValue;
- if (replace) {
- // only replace hash, not store to history
- windowLocation.replace("#" + urlObject._special);
- } else {
- // change hash and add new record to history
- windowLocation.hash = urlObject._special;
- }
- }
- }
- if (!isSupportStateObjectInHistory && state) {
- stateStorage[windowLocation.href] = state;
- }
- isFireInitialState = false;
- }
-
+ "port": null,
/**
- * Event handler function changes the hash in the address bar
+ * Returns the current page's path only.
*
- * @param {Event} event
- * @return void
+ * @namespace history.location
*/
- function onHashChange(event) {
- // if not empty lastURL, otherwise skipped the current handler event
- if (lastURL) {
- // if checkUrlForPopState equal current url, this means that the event was raised popstate browser
- if (checkUrlForPopState !== windowLocation.href) {
- // otherwise,
- // the browser does not support popstate event or just does not run the event by changing the hash.
- firePopState();
- }
- // current event object
- event = event || window.event;
-
- var oldURLObject = parseURL(lastURL, true);
- var newURLObject = parseURL();
- // HTML4 browser not support properties oldURL/newURL
- if (!event.oldURL) {
- event.oldURL = oldURLObject._href;
- event.newURL = newURLObject._href;
- }
- if (oldURLObject._hash !== newURLObject._hash) {
- // if current hash not equal previous hash
- dispatchEvent(event);
- }
- }
- // new value to lastURL
- lastURL = windowLocation.href;
- }
-
+ "pathname": isSupportHistoryAPI ? null : {
+ get: function() {
+ return parseURL()._pathname;
+ }
+ },
/**
- * The event handler is fully loaded document
+ * Returns the current page's search
+ * string, beginning with the character
+ * '?' and to the symbol '#'
*
- * @param {*} [noScroll]
- * @return void
+ * @namespace history.location
*/
- function onLoad(noScroll) {
- // Get rid of the events popstate when the first loading a document in the webkit browsers
- setTimeout(function() {
- // hang up the event handler for the built-in popstate event in the browser
- addEvent('popstate', function(e) {
- // set the current url, that suppress the creation of the popstate event by changing the hash
- checkUrlForPopState = windowLocation.href;
- // for Safari browser in OS Windows not implemented 'state' object in 'History' interface
- // and not implemented in old HTML4 browsers
- if (!isSupportStateObjectInHistory) {
- e = redefineProperty(e, 'state', {get: function() {
- return historyObject.state;
- }});
- }
- // send events to be processed
- dispatchEvent(e);
- }, false);
- }, 0);
- // for non-HTML5 browsers
- if (!isSupportHistoryAPI && noScroll !== true && historyObject.location) {
- // scroll window to anchor element
- scrollToAnchorId(historyObject.location.hash);
- // fire initial state for non-HTML5 browser after load page
- fireInitialState();
- }
- }
-
+ "search": isSupportHistoryAPI ? null : {
+ get: function() {
+ return parseURL()._search;
+ }
+ },
/**
- * handler url with anchor for non-HTML5 browsers
+ * Returns the current page's hash
+ * string, beginning with the character
+ * '#' and to the end line
*
- * @param e
+ * @namespace history.location
*/
- function onAnchorClick(e) {
- var event = e || window.event;
- var target = event.target || event.srcElement;
- var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false;
- if (target && target.nodeName === "A" && !defaultPrevented) {
- var current = parseURL();
- var expect = parseURL(target.getAttribute("href", 2));
- var isEqualBaseURL = current._href.split('#').shift() === expect._href.split('#').shift();
- if (isEqualBaseURL && expect._hash) {
- if (current._hash !== expect._hash) {
- historyObject.location.hash = expect._hash;
- }
- scrollToAnchorId(expect._hash);
- if (event.preventDefault) {
- event.preventDefault();
- } else {
- event.returnValue = false;
- }
- }
- }
+ "hash": isSupportHistoryAPI ? null : {
+ set: function(value) {
+ changeState(null, ('' + value).replace(/^(#|)/, '#'), false, lastURL);
+ },
+ get: function() {
+ return parseURL()._hash;
+ }
+ }
+ };
+
+ /**
+ * Just empty function
+ *
+ * @return void
+ */
+ function emptyFunction() {
+ // dummy
+ }
+
+ /**
+ * Prepares a parts of the current or specified reference for later use in the library
+ *
+ * @param {string} [href]
+ * @param {boolean} [isWindowLocation]
+ * @param {boolean} [isNotAPI]
+ * @return {Object}
+ */
+ function parseURL(href, isWindowLocation, isNotAPI) {
+ var re = /(?:([a-zA-Z0-9\-]+\:))?(?:\/\/(?:[^@]*@)?([^\/:\?#]+)(?::([0-9]+))?)?([^\?#]*)(?:(\?[^#]+)|\?)?(?:(#.*))?/;
+ if (href != null && href !== '' && !isWindowLocation) {
+ var current = parseURL(),
+ base = document.getElementsByTagName('base')[0];
+ if (!isNotAPI && base && base.getAttribute('href')) {
+ // Fix for IE ignoring relative base tags.
+ // See http://stackoverflow.com/questions/3926197/html-base-tag-and-local-folder-path-with-internet-explorer
+ base.href = base.href;
+ current = parseURL(base.href, null, true);
+ }
+ var _pathname = current._pathname, _protocol = current._protocol;
+ // convert to type of string
+ href = '' + href;
+ // convert relative link to the absolute
+ href = /^(?:\w+\:)?\/\//.test(href) ? href.indexOf("/") === 0
+ ? _protocol + href : href : _protocol + "//" + current._host + (
+ href.indexOf("/") === 0 ? href : href.indexOf("?") === 0
+ ? _pathname + href : href.indexOf("#") === 0
+ ? _pathname + current._search + href : _pathname.replace(/[^\/]+$/g, '') + href
+ );
+ } else {
+ href = isWindowLocation ? href : windowLocation.href;
+ // if current browser not support History-API
+ if (!isSupportHistoryAPI || isNotAPI) {
+ // get hash fragment
+ href = href.replace(/^[^#]*/, '') || "#";
+ // form the absolute link from the hash
+ // https://github.com/devote/HTML5-History-API/issues/50
+ href = windowLocation.protocol.replace(/:.*$|$/, ':') + '//' + windowLocation.host + settings['basepath']
+ + href.replace(new RegExp("^#[\/]?(?:" + settings["type"] + ")?"), "");
+ }
+ }
+ // that would get rid of the links of the form: /../../
+ anchorElement.href = href;
+ // decompose the link in parts
+ var result = re.exec(anchorElement.href);
+ // host name with the port number
+ var host = result[2] + (result[3] ? ':' + result[3] : '');
+ // folder
+ var pathname = result[4] || '/';
+ // the query string
+ var search = result[5] || '';
+ // hash
+ var hash = result[6] === '#' ? '' : (result[6] || '');
+ // relative link, no protocol, no host
+ var relative = pathname + search + hash;
+ // special links for set to hash-link, if browser not support History API
+ var nohash = pathname.replace(new RegExp("^" + settings["basepath"], "i"), settings["type"]) + search;
+ // result
+ return {
+ _href: result[1] + '//' + host + relative,
+ _protocol: result[1],
+ _host: host,
+ _hostname: result[2],
+ _port: result[3] || '',
+ _pathname: pathname,
+ _search: search,
+ _hash: hash,
+ _relative: relative,
+ _nohash: nohash,
+ _special: nohash + hash
+ }
+ }
+
+ /**
+ * Detect HistoryAPI support while taking into account false positives.
+ * Based on https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js
+ */
+ function isSupportHistoryAPIDetect(){
+ var ua = global.navigator.userAgent;
+ // We only want Android 2 and 4.0, stock browser, and not Chrome which identifies
+ // itself as 'Mobile Safari' as well, nor Windows Phone (issue #1471).
+ if ((ua.indexOf('Android 2.') !== -1 ||
+ (ua.indexOf('Android 4.0') !== -1)) &&
+ ua.indexOf('Mobile Safari') !== -1 &&
+ ua.indexOf('Chrome') === -1 &&
+ ua.indexOf('Windows Phone') === -1)
+ {
+ return false;
+ }
+ // Return the regular check
+ return !!historyPushState;
+ }
+
+ /**
+ * This method attempts to bind a function to global.
+ *
+ * @param {Function} [func] The function to be bound
+ * @return {Function} Returns the bound function or func
+ */
+ function maybeBindToGlobal(func) {
+ if (func && global &&
+ global['EventTarget'] &&
+ typeof global['EventTarget'].prototype.addEventListener === 'function' &&
+ typeof func.bind === 'function') {
+ return func.bind(global);
}
+ return func;
+ }
+ /**
+ * Initializing storage for the custom state's object
+ */
+ function storageInitialize() {
+ var sessionStorage;
/**
- * Scroll page to current anchor in url-hash
- *
- * @param hash
+ * sessionStorage throws error when cookies are disabled
+ * Chrome content settings when running the site in a Facebook IFrame.
+ * see: https://github.com/devote/HTML5-History-API/issues/34
+ * and: http://stackoverflow.com/a/12976988/669360
*/
- function scrollToAnchorId(hash) {
- var target = document.getElementById(hash = (hash || '').replace(/^#/, ''));
- if (target && target.id === hash && target.nodeName === "A") {
- var rect = target.getBoundingClientRect();
- window.scrollTo((documentElement.scrollLeft || 0), rect.top + (documentElement.scrollTop || 0)
- - (documentElement.clientTop || 0));
+ try {
+ sessionStorage = global['sessionStorage'];
+ sessionStorage.setItem(sessionStorageKey + 't', '1');
+ sessionStorage.removeItem(sessionStorageKey + 't');
+ } catch(_e_) {
+ sessionStorage = {
+ getItem: function(key) {
+ var cookie = document.cookie.split(key + "=");
+ return cookie.length > 1 && cookie.pop().split(";").shift() || 'null';
+ },
+ setItem: function(key, value) {
+ var state = {};
+ // insert one current element to cookie
+ if (state[windowLocation.href] = historyObject.state) {
+ document.cookie = key + '=' + JSON.stringify(state);
+ }
}
+ }
}
- /**
- * Library initialization
- *
- * @return {Boolean} return true if all is well, otherwise return false value
- */
- function initialize() {
- /**
- * Get custom settings from the query string
- */
- var scripts = document.getElementsByTagName('script');
- var src = (scripts[scripts.length - 1] || {}).src || '';
- var arg = src.indexOf('?') !== -1 ? src.split('?').pop() : '';
- arg.replace(/(\w+)(?:=([^&]*))?/g, function(a, key, value) {
- settings[key] = (value || (key === 'basepath' ? '/' : '')).replace(/^(0|false)$/, '');
- });
+ try {
+ // get cache from the storage in browser
+ stateStorage = JSON.parse(sessionStorage.getItem(sessionStorageKey)) || {};
+ } catch(_e_) {
+ stateStorage = {};
+ }
+
+ // hang up the event handler to event unload page
+ addEvent(eventNamePrefix + 'unload', function() {
+ // save current state's object
+ sessionStorage.setItem(sessionStorageKey, JSON.stringify(stateStorage));
+ }, false);
+ }
- /**
- * sessionStorage throws error when cookies are disabled
- * Chrome content settings when running the site in a Facebook IFrame.
- * see: https://github.com/devote/HTML5-History-API/issues/34
- */
- try {
- sessionStorage = window['sessionStorage'];
- } catch(_e_) {}
+ /**
+ * This method is implemented to override the built-in(native)
+ * properties in the browser, unfortunately some browsers are
+ * not allowed to override all the properties and even add.
+ * For this reason, this was written by a method that tries to
+ * do everything necessary to get the desired result.
+ *
+ * @param {Object} object The object in which will be overridden/added property
+ * @param {String} prop The property name to be overridden/added
+ * @param {Object} [descriptor] An object containing properties set/get
+ * @param {Function} [onWrapped] The function to be called when the wrapper is created
+ * @return {Object|Boolean} Returns an object on success, otherwise returns false
+ */
+ function redefineProperty(object, prop, descriptor, onWrapped) {
+ var testOnly = 0;
+ // test only if descriptor is undefined
+ if (!descriptor) {
+ descriptor = {set: emptyFunction};
+ testOnly = 1;
+ }
+ // variable will have a value of true the success of attempts to set descriptors
+ var isDefinedSetter = !descriptor.set;
+ var isDefinedGetter = !descriptor.get;
+ // for tests of attempts to set descriptors
+ var test = {configurable: true, set: function() {
+ isDefinedSetter = 1;
+ }, get: function() {
+ isDefinedGetter = 1;
+ }};
- /**
- * Includes support for IE6+
- */
- ie6DriverStart();
+ try {
+ // testing for the possibility of overriding/adding properties
+ defineProperty(object, prop, test);
+ // running the test
+ object[prop] = object[prop];
+ // attempt to override property using the standard method
+ defineProperty(object, prop, descriptor);
+ } catch(_e_) {
+ }
- /**
- * hang up the event handler to listen to the events hashchange
- */
- addEvent(eventNamePrefix + 'hashchange', onHashChange, false);
+ // If the variable 'isDefined' has a false value, it means that need to try other methods
+ if (!isDefinedSetter || !isDefinedGetter) {
+ // try to override/add the property, using deprecated functions
+ if (object.__defineGetter__) {
+ // testing for the possibility of overriding/adding properties
+ object.__defineGetter__(prop, test.get);
+ object.__defineSetter__(prop, test.set);
+ // running the test
+ object[prop] = object[prop];
+ // attempt to override property using the deprecated functions
+ descriptor.get && object.__defineGetter__(prop, descriptor.get);
+ descriptor.set && object.__defineSetter__(prop, descriptor.set);
+ }
- // a list of objects with pairs of descriptors/object
- var data = [locationDescriptors, locationObject, eventsDescriptors, window, historyDescriptors, historyObject];
+ // Browser refused to override the property, using the standard and deprecated methods
+ if (!isDefinedSetter || !isDefinedGetter) {
+ if (testOnly) {
+ return false;
+ } else if (object === global) {
+ // try override global properties
+ try {
+ // save original value from this property
+ var originalValue = object[prop];
+ // set null to built-in(native) property
+ object[prop] = null;
+ } catch(_e_) {
+ }
+ // This rule for Internet Explorer 8
+ if ('execScript' in global) {
+ /**
+ * to IE8 override the global properties using
+ * VBScript, declaring it in global scope with
+ * the same names.
+ */
+ global['execScript']('Public ' + prop, 'VBScript');
+ global['execScript']('var ' + prop + ';', 'JavaScript');
+ } else {
+ try {
+ /**
+ * This hack allows to override a property
+ * with the set 'configurable: false', working
+ * in the hack 'Safari' to 'Mac'
+ */
+ defineProperty(object, prop, {value: emptyFunction});
+ } catch(_e_) {
+ if (prop === 'onpopstate') {
+ /**
+ * window.onpopstate fires twice in Safari 8.0.
+ * Block initial event on window.onpopstate
+ * See: https://github.com/devote/HTML5-History-API/issues/69
+ */
+ addEvent('popstate', descriptor = function() {
+ removeEvent('popstate', descriptor, false);
+ var onpopstate = object.onpopstate;
+ // cancel initial event on attribute handler
+ object.onpopstate = null;
+ setTimeout(function() {
+ // restore attribute value after short time
+ object.onpopstate = onpopstate;
+ }, 1);
+ }, false);
+ // cancel trigger events on attributes in object the window
+ triggerEventsInWindowAttributes = 0;
+ }
+ }
+ }
+ // set old value to new variable
+ object[prop] = originalValue;
- // if browser support object 'state' in interface 'History'
- if (isSupportStateObjectInHistory) {
- // remove state property from descriptor
- delete historyDescriptors['state'];
+ } else {
+ // the last stage of trying to override the property
+ try {
+ try {
+ // wrap the object in a new empty object
+ var temp = Object.create(object);
+ defineProperty(Object.getPrototypeOf(temp) === object ? temp : object, prop, descriptor);
+ for(var key in object) {
+ // need to bind a function to the original object
+ if (typeof object[key] === 'function') {
+ temp[key] = object[key].bind(object);
+ }
+ }
+ try {
+ // to run a function that will inform about what the object was to wrapped
+ onWrapped.call(temp, temp, object);
+ } catch(_e_) {
+ }
+ object = temp;
+ } catch(_e_) {
+ // sometimes works override simply by assigning the prototype property of the constructor
+ defineProperty(object.constructor.prototype, prop, descriptor);
+ }
+ } catch(_e_) {
+ // all methods have failed
+ return false;
+ }
}
+ }
+ }
- // initializing descriptors
- for(var i = 0; i < data.length; i += 2) {
- for(var prop in data[i]) {
- if (data[i].hasOwnProperty(prop)) {
- if (typeof data[i][prop] === 'function') {
- // If the descriptor is a simple function, simply just assign it an object
- data[i + 1][prop] = data[i][prop];
- } else {
- // prepare the descriptor the required format
- var descriptor = prepareDescriptorsForObject(data[i], prop, data[i][prop]);
- // try to set the descriptor object
- if (!redefineProperty(data[i + 1], prop, descriptor, function(n, o) {
- // is satisfied if the failed override property
- if (o === historyObject) {
- // the problem occurs in Safari on the Mac
- window.history = historyObject = data[i + 1] = n;
- }
- })) {
- // if there is no possibility override.
- // This browser does not support descriptors, such as IE7
+ return object;
+ }
- // remove previously hung event handlers
- removeEvent(eventNamePrefix + 'hashchange', onHashChange, false);
+ /**
+ * Adds the missing property in descriptor
+ *
+ * @param {Object} object An object that stores values
+ * @param {String} prop Name of the property in the object
+ * @param {Object|null} descriptor Descriptor
+ * @return {Object} Returns the generated descriptor
+ */
+ function prepareDescriptorsForObject(object, prop, descriptor) {
+ descriptor = descriptor || {};
+ // the default for the object 'location' is the standard object 'window.location'
+ object = object === locationDescriptors ? windowLocation : object;
+ // setter for object properties
+ descriptor.set = (descriptor.set || function(value) {
+ object[prop] = value;
+ });
+ // getter for object properties
+ descriptor.get = (descriptor.get || function() {
+ return object[prop];
+ });
+ return descriptor;
+ }
- // fail to initialize :(
- return false;
- }
+ /**
+ * Wrapper for the methods 'addEventListener/attachEvent' in the context of the 'window'
+ *
+ * @param {String} event The event type for which the user is registering
+ * @param {Function} listener The method to be called when the event occurs.
+ * @param {Boolean} capture If true, capture indicates that the user wishes to initiate capture.
+ * @return void
+ */
+ function addEventListener(event, listener, capture) {
+ if (event in eventsList) {
+ // here stored the event listeners 'popstate/hashchange'
+ eventsList[event].push(listener);
+ } else {
+ // FireFox support non-standart four argument aWantsUntrusted
+ // https://github.com/devote/HTML5-History-API/issues/13
+ if (arguments.length > 3) {
+ addEvent(event, listener, capture, arguments[3]);
+ } else {
+ addEvent(event, listener, capture);
+ }
+ }
+ }
- // create a repository for custom handlers onpopstate/onhashchange
- if (data[i + 1] === window) {
- eventsList[prop] = eventsList[prop.substr(2)] = [];
- }
- }
- }
- }
+ /**
+ * Wrapper for the methods 'removeEventListener/detachEvent' in the context of the 'window'
+ *
+ * @param {String} event The event type for which the user is registered
+ * @param {Function} listener The parameter indicates the Listener to be removed.
+ * @param {Boolean} capture Was registered as a capturing listener or not.
+ * @return void
+ */
+ function removeEventListener(event, listener, capture) {
+ var list = eventsList[event];
+ if (list) {
+ for(var i = list.length; i--;) {
+ if (list[i] === listener) {
+ list.splice(i, 1);
+ break;
}
+ }
+ } else {
+ removeEvent(event, listener, capture);
+ }
+ }
- // redirect if necessary
- if (settings['redirect']) {
- historyObject['redirect']();
+ /**
+ * Wrapper for the methods 'dispatchEvent/fireEvent' in the context of the 'window'
+ *
+ * @param {Event|String} event Instance of Event or event type string if 'eventObject' used
+ * @param {*} [eventObject] For Internet Explorer 8 required event object on this argument
+ * @return {Boolean} If 'preventDefault' was called the value is false, else the value is true.
+ */
+ function dispatchEvent(event, eventObject) {
+ var eventType = ('' + (typeof event === "string" ? event : event.type)).replace(/^on/, '');
+ var list = eventsList[eventType];
+ if (list) {
+ // need to understand that there is one object of Event
+ eventObject = typeof event === "string" ? eventObject : event;
+ if (eventObject.target == null) {
+ // need to override some of the properties of the Event object
+ for(var props = ['target', 'currentTarget', 'srcElement', 'type']; event = props.pop();) {
+ // use 'redefineProperty' to override the properties
+ eventObject = redefineProperty(eventObject, event, {
+ get: event === 'type' ? function() {
+ return eventType;
+ } : function() {
+ return global;
+ }
+ });
}
+ }
+ if (triggerEventsInWindowAttributes) {
+ // run function defined in the attributes 'onpopstate/onhashchange' in the 'window' context
+ ((eventType === 'popstate' ? global.onpopstate : global.onhashchange)
+ || emptyFunction).call(global, eventObject);
+ }
+ // run other functions that are in the list of handlers
+ for(var i = 0, len = list.length; i < len; i++) {
+ list[i].call(global, eventObject);
+ }
+ return true;
+ } else {
+ return dispatch(event, eventObject);
+ }
+ }
+
+ /**
+ * dispatch current state event
+ */
+ function firePopState() {
+ var o = document.createEvent ? document.createEvent('Event') : document.createEventObject();
+ if (o.initEvent) {
+ o.initEvent('popstate', false, false);
+ } else {
+ o.type = 'popstate';
+ }
+ o.state = historyObject.state;
+ // send a newly created events to be processed
+ dispatchEvent(o);
+ }
+
+ /**
+ * fire initial state for non-HTML5 browsers
+ */
+ function fireInitialState() {
+ if (isFireInitialState) {
+ isFireInitialState = false;
+ firePopState();
+ }
+ }
- // If browser does not support object 'state' in interface 'History'
- if (!isSupportStateObjectInHistory && JSON) {
- storageInitialize();
+ /**
+ * Change the data of the current history for HTML4 browsers
+ *
+ * @param {Object} state
+ * @param {string} [url]
+ * @param {Boolean} [replace]
+ * @param {string} [lastURLValue]
+ * @return void
+ */
+ function changeState(state, url, replace, lastURLValue) {
+ if (!isSupportHistoryAPI) {
+ // if not used implementation history.location
+ if (isUsedHistoryLocationFlag === 0) isUsedHistoryLocationFlag = 2;
+ // normalization url
+ var urlObject = parseURL(url, isUsedHistoryLocationFlag === 2 && ('' + url).indexOf("#") !== -1);
+ // if current url not equal new url
+ if (urlObject._relative !== parseURL()._relative) {
+ // if empty lastURLValue to skip hash change event
+ lastURL = lastURLValue;
+ if (replace) {
+ // only replace hash, not store to history
+ windowLocation.replace("#" + urlObject._special);
+ } else {
+ // change hash and add new record to history
+ windowLocation.hash = urlObject._special;
}
+ }
+ } else {
+ lastURL = windowLocation.href;
+ }
+ if (!isSupportStateObjectInHistory && state) {
+ stateStorage[windowLocation.href] = state;
+ }
+ isFireInitialState = false;
+ }
+
+ /**
+ * Event handler function changes the hash in the address bar
+ *
+ * @param {Event} event
+ * @return void
+ */
+ function onHashChange(event) {
+ // https://github.com/devote/HTML5-History-API/issues/46
+ var fireNow = lastURL;
+ // new value to lastURL
+ lastURL = windowLocation.href;
+ // if not empty fireNow, otherwise skipped the current handler event
+ if (fireNow) {
+ // if checkUrlForPopState equal current url, this means that the event was raised popstate browser
+ if (checkUrlForPopState !== windowLocation.href) {
+ // otherwise,
+ // the browser does not support popstate event or just does not run the event by changing the hash.
+ firePopState();
+ }
+ // current event object
+ event = event || global.event;
- // track clicks on anchors
- if (!isSupportHistoryAPI) {
- document[addEventListenerName](eventNamePrefix + "click", onAnchorClick, false);
+ var oldURLObject = parseURL(fireNow, true);
+ var newURLObject = parseURL();
+ // HTML4 browser not support properties oldURL/newURL
+ if (!event.oldURL) {
+ event.oldURL = oldURLObject._href;
+ event.newURL = newURLObject._href;
+ }
+ if (oldURLObject._hash !== newURLObject._hash) {
+ // if current hash not equal previous hash
+ dispatchEvent(event);
+ }
+ }
+ }
+
+ /**
+ * The event handler is fully loaded document
+ *
+ * @param {*} [noScroll]
+ * @return void
+ */
+ function onLoad(noScroll) {
+ // Get rid of the events popstate when the first loading a document in the webkit browsers
+ setTimeout(function() {
+ // hang up the event handler for the built-in popstate event in the browser
+ addEvent('popstate', function(e) {
+ // set the current url, that suppress the creation of the popstate event by changing the hash
+ checkUrlForPopState = windowLocation.href;
+ // for Safari browser in OS Windows not implemented 'state' object in 'History' interface
+ // and not implemented in old HTML4 browsers
+ if (!isSupportStateObjectInHistory) {
+ e = redefineProperty(e, 'state', {get: function() {
+ return historyObject.state;
+ }});
}
+ // send events to be processed
+ dispatchEvent(e);
+ }, false);
+ }, 0);
+ // for non-HTML5 browsers
+ if (!isSupportHistoryAPI && noScroll !== true && "location" in historyObject) {
+ // scroll window to anchor element
+ scrollToAnchorId(locationObject.hash);
+ // fire initial state for non-HTML5 browser after load page
+ fireInitialState();
+ }
+ }
- if (document.readyState === 'complete') {
- onLoad(true);
+ /**
+ * Finds the closest ancestor anchor element (including the target itself).
+ *
+ * @param {HTMLElement} target The element to start scanning from.
+ * @return {HTMLElement} An element which is the closest ancestor anchor.
+ */
+ function anchorTarget(target) {
+ while (target) {
+ if (target.nodeName === 'A') return target;
+ target = target.parentNode;
+ }
+ }
+
+ /**
+ * Handles anchor elements with a hash fragment for non-HTML5 browsers
+ *
+ * @param {Event} e
+ */
+ function onAnchorClick(e) {
+ var event = e || global.event;
+ var target = anchorTarget(event.target || event.srcElement);
+ var defaultPrevented = "defaultPrevented" in event ? event['defaultPrevented'] : event.returnValue === false;
+ if (target && target.nodeName === "A" && !defaultPrevented) {
+ var current = parseURL();
+ var expect = parseURL(target.getAttribute("href", 2));
+ var isEqualBaseURL = current._href.split('#').shift() === expect._href.split('#').shift();
+ if (isEqualBaseURL && expect._hash) {
+ if (current._hash !== expect._hash) {
+ locationObject.hash = expect._hash;
+ }
+ scrollToAnchorId(expect._hash);
+ if (event.preventDefault) {
+ event.preventDefault();
} else {
- if (!isSupportHistoryAPI && parseURL()._relative !== settings["basepath"]) {
- isFireInitialState = true;
- }
- /**
- * Need to avoid triggering events popstate the initial page load.
- * Hang handler popstate as will be fully loaded document that
- * would prevent triggering event onpopstate
- */
- addEvent(eventNamePrefix + 'load', onLoad, false);
+ event.returnValue = false;
}
-
- // everything went well
- return true;
+ }
}
+ }
- /**
- * Starting the library
- */
- if (!initialize()) {
- // if unable to initialize descriptors
- // therefore quite old browser and there
- // is no sense to continue to perform
- return;
+ /**
+ * Scroll page to current anchor in url-hash
+ *
+ * @param hash
+ */
+ function scrollToAnchorId(hash) {
+ var target = document.getElementById(hash = (hash || '').replace(/^#/, ''));
+ if (target && target.id === hash && target.nodeName === "A") {
+ var rect = target.getBoundingClientRect();
+ global.scrollTo((documentElement.scrollLeft || 0), rect.top + (documentElement.scrollTop || 0)
+ - (documentElement.clientTop || 0));
}
+ }
+ /**
+ * Library initialization
+ *
+ * @return {Boolean} return true if all is well, otherwise return false value
+ */
+ function initialize() {
/**
- * If the property history.emulate will be true,
- * this will be talking about what's going on
- * emulation capabilities HTML5-History-API.
- * Otherwise there is no emulation, ie the
- * built-in browser capabilities.
- *
- * @type {boolean}
- * @const
+ * Get custom settings from the query string
*/
- historyObject['emulate'] = !isSupportHistoryAPI;
+ var scripts = document.getElementsByTagName('script');
+ var src = (scripts[scripts.length - 1] || {}).src || '';
+ var arg = src.indexOf('?') !== -1 ? src.split('?').pop() : '';
+ arg.replace(/(\w+)(?:=([^&]*))?/g, function(a, key, value) {
+ settings[key] = (value || '').replace(/^(0|false)$/, '');
+ });
/**
- * Replace the original methods on the wrapper
+ * hang up the event handler to listen to the events hashchange
*/
- window[addEventListenerName] = addEventListener;
- window[removeEventListenerName] = removeEventListener;
- window[dispatchEventName] = dispatchEvent;
+ addEvent(eventNamePrefix + 'hashchange', onHashChange, false);
- // ====================================================================================== //
- // Driver for IE6+ or below
- // ====================================================================================== //
- function ie6DriverStart() {
- /**
- * Creates a static object
- *
- * @param object
- * @returns {*}
- */
- function createVBObjects(object) {
- var properties = [];
- var className = 'VBHistoryClass' + (new Date()).getTime() + msie++;
- var staticClassParts = ["Class " + className];
- for(var prop in object) {
- if (object.hasOwnProperty(prop)) {
- var value = object[prop];
- if (value && (value.get || value.set)) {
- if (value.get) {
- staticClassParts.push(
- 'Public ' + (prop === '_' ? 'Default ' : '') + 'Property Get [' + prop + ']',
- 'Call VBCVal([(accessors)].[' + prop + '].get.call(me),[' + prop + '])',
- 'End Property'
- );
- }
- if (value.set) {
- staticClassParts.push('Public Property Let [' + prop + '](val)',
- (value = 'Call [(accessors)].[' + prop + '].set.call(me,val)\nEnd Property'),
- 'Public Property Set [' + prop + '](val)', value);
- }
- } else {
- properties.push(prop);
- staticClassParts.push('Public [' + prop + ']');
- }
- }
+ // a list of objects with pairs of descriptors/object
+ var data = [locationDescriptors, locationObject, eventsDescriptors, global, historyDescriptors, historyObject];
+
+ // if browser support object 'state' in interface 'History'
+ if (isSupportStateObjectInHistory) {
+ // remove state property from descriptor
+ delete historyDescriptors['state'];
+ }
+
+ // initializing descriptors
+ for(var i = 0; i < data.length; i += 2) {
+ for(var prop in data[i]) {
+ if (data[i].hasOwnProperty(prop)) {
+ if (typeof data[i][prop] !== 'object') {
+ // If the descriptor is a simple function, simply just assign it an object
+ data[i + 1][prop] = data[i][prop];
+ } else {
+ // prepare the descriptor the required format
+ var descriptor = prepareDescriptorsForObject(data[i], prop, data[i][prop]);
+ // try to set the descriptor object
+ if (!redefineProperty(data[i + 1], prop, descriptor, function(n, o) {
+ // is satisfied if the failed override property
+ if (o === historyObject) {
+ // the problem occurs in Safari on the Mac
+ global.history = historyObject = data[i + 1] = n;
+ }
+ })) {
+ // if there is no possibility override.
+ // This browser does not support descriptors, such as IE7
+
+ // remove previously hung event handlers
+ removeEvent(eventNamePrefix + 'hashchange', onHashChange, false);
+
+ // fail to initialize :(
+ return false;
}
- staticClassParts.push(
- 'Private [(accessors)]',
- 'Private Sub Class_Initialize()',
- 'Set [(accessors)]=' + className + 'FactoryJS()',
- 'End Sub',
- 'End Class',
- 'Function ' + className + 'Factory()',
- 'Set ' + className + 'Factory=New ' + className,
- 'End Function'
- );
- window['execScript'](staticClassParts.join('\n'), 'VBScript');
- window[className + 'FactoryJS'] = function() {
- return object;
- };
- var result = window[className + 'Factory']();
- for(var i = 0; i < properties.length; i++) {
- result[properties[i]] = object[properties[i]];
+
+ // create a repository for custom handlers onpopstate/onhashchange
+ if (data[i + 1] === global) {
+ eventsList[prop] = eventsList[prop.substr(2)] = [];
}
- return result;
+ }
}
+ }
+ }
- /**
- * Escape special symbols
- *
- * @param string
- * @returns {string}
- */
- function quote(string) {
- var escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
- var meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"': '\\"', '\\': '\\\\'};
- return escapable.test(string) ? '"' + string.replace(escapable, function(a) {
- return a in meta ? meta[a] : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' : '"' + string + '"';
- }
+ // check settings
+ historyObject['setup']();
- // testing IE browser
- var msie = window['eval'] && eval("/*@cc_on 1;@*/");
- if (!msie || +((/msie (\d+)/i.exec(navigator.userAgent) || [, 8])[1]) > 7) {
- // If it is not IE or a version greater than seven
- return;
- }
+ // redirect if necessary
+ if (settings['redirect']) {
+ historyObject['redirect']();
+ }
- // save original links to methods
- var originalChangeState = changeState;
- var originalRedefineProperty = redefineProperty;
- var currentHref = parseURL()._href;
- var iFrame = document.createElement('iframe');
+ // initialize
+ if (settings["init"]) {
+ // You agree that you will use window.history.location instead window.location
+ isUsedHistoryLocationFlag = 1;
+ }
- // insert IFRAME element to DOM
- iFrame.src = "javascript:true;";
- iFrame = documentElement.firstChild.appendChild(iFrame).contentWindow;
+ // If browser does not support object 'state' in interface 'History'
+ if (!isSupportStateObjectInHistory && JSON) {
+ storageInitialize();
+ }
- // correction value for VB Script
- window['execScript'](
- 'Public history\nFunction VBCVal(o,r) If IsObject(o) Then Set r=o Else r=o End If End Function',
- 'VBScript'
- );
+ // track clicks on anchors
+ if (!isSupportHistoryAPI) {
+ document[addEventListenerName](eventNamePrefix + "click", onAnchorClick, false);
+ }
- // renew standard object
- locationObject = {"_": {get: locationDescriptors.toString}};
- historyObject = {
- // properties to create an object in IE
- "back": windowHistory.back,
- "forward": windowHistory.forward,
- "go": windowHistory.go,
- "emulate": null,
- "_": {get: function() {
- return '[object History]';
- }}
- };
+ if (document.readyState === 'complete') {
+ onLoad(true);
+ } else {
+ if (!isSupportHistoryAPI && parseURL()._relative !== settings["basepath"]) {
+ isFireInitialState = true;
+ }
+ /**
+ * Need to avoid triggering events popstate the initial page load.
+ * Hang handler popstate as will be fully loaded document that
+ * would prevent triggering event onpopstate
+ */
+ addEvent(eventNamePrefix + 'load', onLoad, false);
+ }
- JSON = {
- /**
- * Analogue of JSON.parse()
- *
- * @param value
- * @returns {*}
- */
- "parse": function(value) {
- try {
- return new Function('', 'return ' + value)();
- } catch(_e_) {
- return null;
- }
- },
- /**
- * Analogue of JSON.stringify()
- *
- * @param value
- * @returns {*}
- */
- "stringify": function(value) {
- var n = (typeof value).charCodeAt(2);
- return n === 114 ? quote(value) : n === 109 ? isFinite(value) ? String(value) : 'null' : n === 111 || n
- === 108 ? String(value) : n === 106 ? !value ? 'null' : (function(isArray) {
- var out = isArray ? '[' : '{';
- if (isArray) {
- for(var i = 0; i < value.length; i++) {
- out += (i == 0 ? "" : ",") + JSON.stringify(value[i]);
- }
- } else {
- for(var k in value) {
- if (value.hasOwnProperty(k)) {
- out += (out.length == 1 ? "" : ",") + quote(k) + ":" + JSON.stringify(value[k]);
- }
- }
- }
- return out + (isArray ? ']' : '}');
- })(Object.prototype.toString.call(value) === '[object Array]') : 'void 0';
- }
- };
+ // everything went well
+ return true;
+ }
- /**
- * Change the data of the current history for IE6+
- */
- changeState = function(state, url, replace, lastURLValue, lfirst) {
- var iFrameDocument = iFrame.document;
- var urlObject = parseURL(url);
- isFireInitialState = false;
- if (urlObject._relative === parseURL()._relative && !lfirst) {
- if (state) {
- stateStorage[windowLocation.href] = state;
- }
- return;
- }
- lastURL = lastURLValue;
- if (replace) {
- if (iFrame["lfirst"]) {
- history.back();
- changeState(state, urlObject._href, 0, lastURLValue, 1);
- } else {
- windowLocation.replace("#" + urlObject._special);
- }
- } else if (urlObject._href != currentHref || lfirst) {
- if (!iFrame['lfirst']) {
- iFrame["lfirst"] = 1;
- changeState(state, currentHref, 0, lastURLValue, 1);
- }
- iFrameDocument.open();
- iFrameDocument.write('\x3Cscript\x3Elfirst=1;parent.location.hash="'
- + urlObject._special.replace(/"/g, '\\"') + '";\x3C/script\x3E');
- iFrameDocument.close();
- }
- if (!lfirst && state) {
- stateStorage[windowLocation.href] = state;
- }
- };
+ /**
+ * Starting the library
+ */
+ if (!initialize()) {
+ // if unable to initialize descriptors
+ // therefore quite old browser and there
+ // is no sense to continue to perform
+ return;
+ }
- /**
- * See original method
- */
- redefineProperty = function(object, prop, descriptor, onWrapped) {
- if (!originalRedefineProperty.apply(this, arguments)) {
- if (object === locationObject) {
- locationObject[prop] = descriptor;
- } else if (object === historyObject) {
- historyObject[prop] = descriptor;
- if (prop === 'state') {
- locationObject = createVBObjects(locationObject);
- window.history = historyObject = createVBObjects(historyObject);
- }
- } else {
- object[prop] = descriptor.get && descriptor.get();
- }
- }
- return object;
- };
+ /**
+ * If the property history.emulate will be true,
+ * this will be talking about what's going on
+ * emulation capabilities HTML5-History-API.
+ * Otherwise there is no emulation, ie the
+ * built-in browser capabilities.
+ *
+ * @type {boolean}
+ * @const
+ */
+ historyObject['emulate'] = !isSupportHistoryAPI;
- /**
- * Tracking changes in the hash in the address bar
- */
- var interval = setInterval(function() {
- var href = parseURL()._href;
- if (href != currentHref) {
- var e = document.createEventObject();
- e.oldURL = currentHref;
- e.newURL = currentHref = href;
- e.type = 'hashchange';
- onHashChange(e);
- }
- }, 100);
+ /**
+ * Replace the original methods on the wrapper
+ */
+ global[addEventListenerName] = addEventListener;
+ global[removeEventListenerName] = removeEventListener;
+ global[dispatchEventName] = dispatchEvent;
- window['JSON'] = JSON;
- }
-})(window);
\ No newline at end of file
+ return historyObject;
+});
diff --git a/history.min.js b/history.min.js
index e40b11e..d2bab18 100644
--- a/history.min.js
+++ b/history.min.js
@@ -1,33 +1,30 @@
-/*
- * History API JavaScript Library v4.0.5
+/*!
+ * History API JavaScript Library v4.2.10
*
- * Support: IE6+, FF3+, Opera 9+, Safari, Chrome and other
+ * Support: IE8+, FF3+, Opera 9+, Safari, Chrome and other
*
- * Copyright 2011-2013, Dmitrii Pakhtinov ( spb.piksel@gmail.com )
+ * Copyright 2011-2018, Dmitrii Pakhtinov ( spb.piksel@gmail.com )
*
* http://spb-piksel.ru/
*
- * Dual licensed under the MIT and GPL licenses:
+ * MIT license:
* http://www.opensource.org/licenses/mit-license.php
- * http://www.gnu.org/licenses/gpl.html
*
- * Update: 20.08.13 21:16
+ * Update: 2018-04-15 13:54
*/
-(function(f){var i=!0,j=null,o=!1;function K(){}function h(a,b,c){if(a&&!b)var b=h(),c=b.e,d=b.h,a=/^(?:[\w0-9]+\:)?\/\//.test(a)?0===a.indexOf("/")?d+a:a:d+"//"+b.g+(0===a.indexOf("/")?a:0===a.indexOf("?")?c+a:0===a.indexOf("#")?c+b.f+a:c.replace(/[^\/]+$/g,"")+a);else if(a=b?a:e.href,!p||c)a=a.replace(/^[^#]*/,"")||"#",a=e.protocol+"//"+e.host+m.basepath+a.replace(RegExp("^#[/]?(?:"+m.type+")?"),"");P.href=a;var a=/(?:([\w0-9]+:))?(?:\/\/(?:[^@]*@)?([^\/:\?#]+)(?::([0-9]+))?)?([^\?#]*)(?:(\?[^#]+)|\?)?(?:(#.*))?/.exec(P.href),b=
-a[2]+(a[3]?":"+a[3]:""),c=a[4]||"/",d=a[5]||"",f="#"===a[6]?"":a[6]||"",k=c+d+f,u=c.replace(RegExp("^"+m.basepath,"i"),m.type)+d;return{a:a[1]+"//"+b+k,h:a[1],g:b,i:a[2],k:a[3]||"",e:c,f:d,b:f,c:k,j:u,d:u+f}}function $(){var a="";if(C)a+=C.getItem(F);else{var b=l.cookie.split(F+"=");1lfirst=1;parent.location.hash="'+b.d.replace(/"/g,'\\"')+'";<\/script>'),g.close();!f&&a&&(q[e.href]=a)}};x=function(b,c,e,h){d.apply(this,arguments)||(b===t?t[c]=e:b===g?(g[c]=e,"state"===c&&(t=a(t),f.history=g=a(g))):b[c]=e.get&&e.get());return b};setInterval(function(){var a=h().a;if(a!=B){var b=l.createEventObject();
-b.oldURL=B;b.newURL=B=a;b.type="hashchange";M(b)}},100);f.JSON=r}}if(f.history){var l=f.document,J=l.documentElement,C=j,G=f.Object,r=f.JSON,e=f.location,n=f.history,g=n,N=n.pushState,U=n.replaceState,p=!!N,I="state"in n,D=G.defineProperty,t=x({},"t")?{}:l.createElement("a"),w="",O=f.addEventListener?"addEventListener":(w="on")&&"attachEvent",V=f.removeEventListener?"removeEventListener":"detachEvent",W=f.dispatchEvent?"dispatchEvent":"fireEvent",v=f[O],X=f[V],ca=f[W],m={basepath:"/",redirect:0,type:"/"},
-F="__historyAPI__",P=l.createElement("a"),A=e.href,R="",E=o,q={},z={},fa={onhashchange:j,onpopstate:j},Y=function(a,b){var c=f.history!==n;c&&(f.history=n);a.apply(n,b);c&&(f.history=g)},Z={redirect:function(a,b){m.basepath=b=b==j?m.basepath:b;m.type=a=a==j?m.type:a;if(f.top==f.self){var c=h(j,o,i).c,d=e.pathname+e.search;p?(d=d.replace(/([^\/])$/,"$1/"),c!=b&&RegExp("^"+b+"$","i").test(d)&&e.replace(c)):d!=b&&(d=d.replace(/([^\/])\?/,"$1/?"),RegExp("^"+b,"i").test(d)&&e.replace(b+"#"+d.replace(RegExp("^"+
-b,"i"),a)+e.hash))}},pushState:function(a,b,c){N&&Y(N,arguments);s(a,c)},replaceState:function(a,b,c){delete q[e.href];U&&Y(U,arguments);s(a,c,i)},location:{set:function(a){f.location=a},get:function(){return p?e:t}},state:{get:function(){return q[e.href]||j}}},L={assign:function(a){0===(""+a).indexOf("#")?s(j,a):e.assign(a)},reload:function(){e.reload()},replace:function(a){0===(""+a).indexOf("#")?s(j,a,i):e.replace(a)},toString:function(){return this.href},href:{get:function(){return h().a}},protocol:j,
-host:j,hostname:j,port:j,pathname:{get:function(){return h().e}},search:{get:function(){return h().f}},hash:{set:function(a){s(j,(""+a).replace(/^(#|)/,"#"),o,A)},get:function(){return h().b}}};(function(){var a=l.getElementsByTagName("script"),a=(a[a.length-1]||{}).src||"";(-1!==a.indexOf("?")?a.split("?").pop():"").replace(/(\w+)(?:=([^&]*))?/g,function(a,b,c){m[b]=(c||("basepath"===b?"/":"")).replace(/^(0|false)$/,"")});try{C=f.sessionStorage}catch(b){}ea();v(w+"hashchange",M,o);var c=[L,t,fa,
-f,Z,g];I&&delete Z.state;for(var d=0;d
-
-
-
-
-
-
- My Link
- Other Link
-
-
-
-And now show an example in conjunction with jQuery:
-
-
-
-
-
-
-
-
-
- My Link
- Other Link
-
-
-
-Using the event popstate the usual pure JS:
-
- window[ window.addEventListener ? 'addEventListener' : 'attachEvent' ]( 'popstate', function( event ) {
-
- // receiving location from the window.history object
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
-
- }, false);
-
-
-Using the popstate event in conjunction jQuery:
-
- $( window ).bind( 'popstate', function( event ) {
-
- // receiving location from the window.history object
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
- });
-
-
-You can use the advanced configuration library:
- history.min.js?basepath=/pathtosite/ - the base path to the site defaults to the root "/".
- history.min.js?redirect=true - enable link translation.
- history.min.js?type=/ - substitute the string after the anchor, by default, nothing substitutes.
-
-You can also combine options:
- history.min.js?type=/&redirect=true&basepath=/pathtosite/ - the order of options does not matter.
-
-Or execute special method in JavaScript:
- history.redirect( /* type = */ '/', /* basepath = */ '/pathtosite/' );
-
-Demo Site: http://history.spb-piksel.ru/
-
-GitHub Project: https://github.com/devote/HTML5-History-API
-
-I'm on Twitter: https://twitter.com/DimaPakhtinov
diff --git a/readme.ru.txt b/readme.ru.txt
deleted file mode 100644
index b0351a8..0000000
--- a/readme.ru.txt
+++ /dev/null
@@ -1,164 +0,0 @@
-Библиотека эмулирует HTML5 History API в старых браузерах.
-
-Библиотека которая не добавляет ненужные методы заставляя их изучать, а оперирует по спецификации w3c, по интерфейсу History.
-
-Для примера могу привести короткий код как с ней работать.
-
-По принципу мы работаем с HTML5 History API так как описано например тут http://htmlbook.ru/html5/history или по спецификации http://www.w3.org/TR/html5/history.html#the-history-interface
-
-То-есть коротенький пример:
-
-на чистом JS:
-
-
-
-
-
-
-
-
- My Link
- Other Link
-
-
-
-А теперь показываю пример в связке с jQuery:
-
-
-
-
-
-
-
-
-
- My Link
- Other Link
-
-
-
-Использование события popstate при обычном чистом JS:
-
- window[ window.addEventListener ? 'addEventListener' : 'attachEvent' ]( 'popstate', function( event ) {
-
- // получение location из объекта window.history
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
-
- }, false);
-
-
-Использование события popstate в связке jQuery:
-
- $( window ).bind( 'popstate', function( event ) {
-
- // получение location из объекта window.history
- var loc = history.location || document.location;
-
- alert( "return to: " + loc );
- });
-
-
-Вы можете использовать дополнительные параметры конфигурации библиотеки:
- history.min.js?basepath=/pathtosite/ - базовый путь к сайту, по умолчанию имеет значение корня "/".
- history.min.js?redirect=true - включить преобразование ссылок.
- history.min.js?type=/ - подставлять подстроку после якоря, по умолчанию ничего не подставляет.
-
-Также вы можете комбинировать опции:
- history.min.js?type=/&redirect=true&basepath=/pathtosite/ - порядок опций не имеет значение.
-
-Или выполнить специальный метод в JavaScript:
- history.redirect( /* type = */ '/', /* basepath = */ '/pathtosite/' );
-
-Демо-сайт: http://history.spb-piksel.ru/
-
-GitHub Проект: https://github.com/devote/HTML5-History-API
-
-Я в Twitter: https://twitter.com/DimaPakhtinov