diff --git a/LICENSE.md b/LICENSE.md --- a/LICENSE.md +++ b/LICENSE.md @@ -186,37 +186,6 @@ using the ''kallithea-cli front-end-buil -History.js ----------- - -Kallithea incorporates some CSS from a system called History.js, which is - -Copyright 2010-2011 Benjamin Arthur Lupton - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - - - Flot ---- diff --git a/kallithea/public/js/native.history.js b/kallithea/public/js/native.history.js deleted file mode 100644 --- a/kallithea/public/js/native.history.js +++ /dev/null @@ -1,1974 +0,0 @@ -/** - * History.js Native Adapter - * @author Benjamin Arthur Lupton - * @copyright 2010-2011 Benjamin Arthur Lupton - * @license New BSD License - */ - -// Closure -(function(window,undefined){ - "use strict"; - - // Localise Globals - var History = window.History = window.History||{}; - - // Check Existence - if ( typeof History.Adapter !== 'undefined' ) { - throw new Error('History.js Adapter has already been loaded...'); - } - - // Add the Adapter - History.Adapter = { - /** - * History.Adapter.handlers[uid][eventName] = Array - */ - handlers: {}, - - /** - * History.Adapter._uid - * The current element unique identifier - */ - _uid: 1, - - /** - * History.Adapter.uid(element) - * @param {Element} element - * @return {String} uid - */ - uid: function(element){ - return element._uid || (element._uid = History.Adapter._uid++); - }, - - /** - * History.Adapter.bind(el,event,callback) - * @param {Element} element - * @param {String} eventName - custom and standard events - * @param {Function} callback - * @return - */ - bind: function(element,eventName,callback){ - // Prepare - var uid = History.Adapter.uid(element); - - // Apply Listener - History.Adapter.handlers[uid] = History.Adapter.handlers[uid] || {}; - History.Adapter.handlers[uid][eventName] = History.Adapter.handlers[uid][eventName] || []; - History.Adapter.handlers[uid][eventName].push(callback); - - // Bind Global Listener - element['on'+eventName] = (function(element,eventName){ - return function(event){ - History.Adapter.trigger(element,eventName,event); - }; - })(element,eventName); - }, - - /** - * History.Adapter.trigger(el,event) - * @param {Element} element - * @param {String} eventName - custom and standard events - * @param {Object} event - a object of event data - * @return - */ - trigger: function(element,eventName,event){ - // Prepare - event = event || {}; - var uid = History.Adapter.uid(element), - i,n; - - // Apply Listener - History.Adapter.handlers[uid] = History.Adapter.handlers[uid] || {}; - History.Adapter.handlers[uid][eventName] = History.Adapter.handlers[uid][eventName] || []; - - // Fire Listeners - for ( i=0,n=History.Adapter.handlers[uid][eventName].length; i - * @copyright 2010-2011 Benjamin Arthur Lupton - * @license New BSD License - */ - -(function(window,undefined){ - "use strict"; - - // -------------------------------------------------------------------------- - // Initialise - - // Localise Globals - var - console = window.console||undefined, // Prevent a JSLint complain - document = window.document, // Make sure we are using the correct document - navigator = window.navigator, // Make sure we are using the correct navigator - amplify = window.amplify||false, // Amplify.js - setTimeout = window.setTimeout, - clearTimeout = window.clearTimeout, - setInterval = window.setInterval, - clearInterval = window.clearInterval, - JSON = window.JSON, - History = window.History = window.History||{}, // Public History Object - history = window.history; // Old History Object - - // MooTools Compatibility - JSON.stringify = JSON.stringify||JSON.encode; - JSON.parse = JSON.parse||JSON.decode; - - // Check Existence - if ( typeof History.init !== 'undefined' ) { - throw new Error('History.js Core has already been loaded...'); - } - - // Initialise History - History.init = function(){ - // Check Load Status of Adapter - if ( typeof History.Adapter === 'undefined' ) { - return false; - } - - // Check Load Status of Core - if ( typeof History.initCore !== 'undefined' ) { - History.initCore(); - } - - // Check Load Status of HTML4 Support - if ( typeof History.initHtml4 !== 'undefined' ) { - History.initHtml4(); - } - - // Return true - return true; - }; - - // -------------------------------------------------------------------------- - // Initialise Core - - // Initialise Core - History.initCore = function(){ - // Initialise - if ( typeof History.initCore.initialized !== 'undefined' ) { - // Already Loaded - return false; - } - else { - History.initCore.initialized = true; - } - - // ---------------------------------------------------------------------- - // Options - - /** - * History.options - * Configurable options - */ - History.options = History.options||{}; - - /** - * History.options.hashChangeInterval - * How long should the interval be before hashchange checks - */ - History.options.hashChangeInterval = History.options.hashChangeInterval || 100; - - /** - * History.options.safariPollInterval - * How long should the interval be before safari poll checks - */ - History.options.safariPollInterval = History.options.safariPollInterval || 500; - - /** - * History.options.doubleCheckInterval - * How long should the interval be before we perform a double check - */ - History.options.doubleCheckInterval = History.options.doubleCheckInterval || 500; - - /** - * History.options.storeInterval - * How long should we wait between store calls - */ - History.options.storeInterval = History.options.storeInterval || 1000; - - /** - * History.options.busyDelay - * How long should we wait between busy events - */ - History.options.busyDelay = History.options.busyDelay || 250; - - /** - * History.options.debug - * If true will enable debug messages to be logged - */ - History.options.debug = History.options.debug || false; - - /** - * History.options.initialTitle - * What is the title of the initial state - */ - History.options.initialTitle = History.options.initialTitle || document.title; - - - // ---------------------------------------------------------------------- - // Interval record - - /** - * History.intervalList - * List of intervals set, to be cleared when document is unloaded. - */ - History.intervalList = []; - - /** - * History.clearAllIntervals - * Clears all setInterval instances. - */ - History.clearAllIntervals = function(){ - var i, il = History.intervalList; - if (typeof il !== "undefined" && il !== null) { - for (i = 0; i < il.length; i++) { - clearInterval(il[i]); - } - History.intervalList = null; - } - }; - History.Adapter.bind(window,"beforeunload",History.clearAllIntervals); - History.Adapter.bind(window,"unload",History.clearAllIntervals); - - - // ---------------------------------------------------------------------- - // Debug - - /** - * History.debug(message,...) - * Logs the passed arguments if debug enabled - */ - History.debug = function(){ - if ( (History.options.debug||false) ) { - History.log.apply(History,arguments); - } - }; - - /** - * History.log(message,...) - * Logs the passed arguments - */ - History.log = function(){ - // Prepare - var - consoleExists = !(typeof console === 'undefined' || typeof console.log === 'undefined' || typeof console.log.apply === 'undefined'), - textarea = document.getElementById('log'), - message, - i,n - ; - - // Write to Console - if ( consoleExists ) { - var args = Array.prototype.slice.call(arguments); - message = args.shift(); - if ( typeof console.debug !== 'undefined' ) { - console.debug.apply(console,[message,args]); - } - else { - console.log.apply(console,[message,args]); - } - } - else { - message = ("\n"+arguments[0]+"\n"); - } - - // Write to log - for ( i=1,n=arguments.length; i - * @author James Padolsey - */ - History.getInternetExplorerMajorVersion = function(){ - var result = History.getInternetExplorerMajorVersion.cached = - (typeof History.getInternetExplorerMajorVersion.cached !== 'undefined') - ? History.getInternetExplorerMajorVersion.cached - : (function(){ - var v = 3, - div = document.createElement('div'), - all = div.getElementsByTagName('i'); - while ( (div.innerHTML = '') && all[0] ) {} - return (v > 4) ? v : false; - })() - ; - return result; - }; - - /** - * History.isInternetExplorer() - * Are we using Internet Explorer? - * @return {boolean} - * @license Public Domain - * @author Benjamin Arthur Lupton - */ - History.isInternetExplorer = function(){ - var result = - History.isInternetExplorer.cached = - (typeof History.isInternetExplorer.cached !== 'undefined') - ? History.isInternetExplorer.cached - : Boolean(History.getInternetExplorerMajorVersion()) - ; - return result; - }; - - /** - * History.emulated - * Which features require emulating? - */ - History.emulated = { - pushState: !Boolean( - window.history && window.history.pushState && window.history.replaceState - && !( - (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent) /* disable for versions of iOS before version 4.3 (8F190) */ - || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent) /* disable for the mercury iOS browser, or at least older versions of the webkit engine */ - ) - ), - hashChange: Boolean( - !(('onhashchange' in window) || ('onhashchange' in document)) - || - (History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8) - ) - }; - - /** - * History.enabled - * Is History enabled? - */ - History.enabled = !History.emulated.pushState; - - /** - * History.bugs - * Which bugs are present - */ - History.bugs = { - /** - * Safari 5 and Safari iOS 4 fail to return to the correct state once a hash is replaced by a `replaceState` call - * https://bugs.webkit.org/show_bug.cgi?id=56249 - */ - setHash: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)), - - /** - * Safari 5 and Safari iOS 4 sometimes fail to apply the state change under busy conditions - * https://bugs.webkit.org/show_bug.cgi?id=42940 - */ - safariPoll: Boolean(!History.emulated.pushState && navigator.vendor === 'Apple Computer, Inc.' && /AppleWebKit\/5([0-2]|3[0-3])/.test(navigator.userAgent)), - - /** - * MSIE 6 and 7 sometimes do not apply a hash even it was told to (requiring a second call to the apply function) - */ - ieDoubleCheck: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 8), - - /** - * MSIE 6 requires the entire hash to be encoded for the hashes to trigger the onHashChange event - */ - hashEscape: Boolean(History.isInternetExplorer() && History.getInternetExplorerMajorVersion() < 7) - }; - - /** - * History.isEmptyObject(obj) - * Checks to see if the Object is Empty - * @param {Object} obj - * @return {boolean} - */ - History.isEmptyObject = function(obj) { - for ( var name in obj ) { - return false; - } - return true; - }; - - /** - * History.cloneObject(obj) - * Clones a object - * @param {Object} obj - * @return {Object} - */ - History.cloneObject = function(obj) { - var hash,newObj; - if ( obj ) { - hash = JSON.stringify(obj); - newObj = JSON.parse(hash); - } - else { - newObj = {}; - } - return newObj; - }; - - // ---------------------------------------------------------------------- - // URL Helpers - - /** - * History.getRootUrl() - * Turns "http://mysite.com/dir/page.html?asd" into "http://mysite.com" - * @return {String} rootUrl - */ - History.getRootUrl = function(){ - // Create - var rootUrl = document.location.protocol+'//'+(document.location.hostname||document.location.host); - if ( document.location.port||false ) { - rootUrl += ':'+document.location.port; - } - rootUrl += '/'; - - // Return - return rootUrl; - }; - - /** - * History.getBaseHref() - * Fetches the `href` attribute of the `` element if it exists - * @return {String} baseHref - */ - History.getBaseHref = function(){ - // Create - var - baseElements = document.getElementsByTagName('base'), - baseElement = null, - baseHref = ''; - - // Test for Base Element - if ( baseElements.length === 1 ) { - // Prepare for Base Element - baseElement = baseElements[0]; - baseHref = baseElement.href.replace(/[^\/]+$/,''); - } - - // Adjust trailing slash - baseHref = baseHref.replace(/\/+$/,''); - if ( baseHref ) baseHref += '/'; - - // Return - return baseHref; - }; - - /** - * History.getBaseUrl() - * Fetches the baseHref or basePageUrl or rootUrl (whichever one exists first) - * @return {String} baseUrl - */ - History.getBaseUrl = function(){ - // Create - var baseUrl = History.getBaseHref()||History.getBasePageUrl()||History.getRootUrl(); - - // Return - return baseUrl; - }; - - /** - * History.getPageUrl() - * Fetches the URL of the current page - * @return {String} pageUrl - */ - History.getPageUrl = function(){ - // Fetch - var - State = History.getState(false,false), - stateUrl = (State||{}).url||document.URL||document.location.href; - - // Create - var pageUrl = stateUrl.replace(/\/+$/,'').replace(/[^\/]+$/,function(part,index,string){ - return (/\./).test(part) ? part : part+'/'; - }); - - // Return - return pageUrl; - }; - - /** - * History.getBasePageUrl() - * Fetches the Url of the directory of the current page - * @return {String} basePageUrl - */ - History.getBasePageUrl = function(){ - // Create - var basePageUrl = (document.URL||document.location.href).replace(/[#\?].*/,'').replace(/[^\/]+$/,function(part,index,string){ - return (/[^\/]$/).test(part) ? '' : part; - }).replace(/\/+$/,'')+'/'; - - // Return - return basePageUrl; - }; - - /** - * History.getFullUrl(url) - * Ensures that we have an absolute URL and not a relative URL - * @param {string} url - * @param {Boolean} allowBaseHref - * @return {string} fullUrl - */ - History.getFullUrl = function(url,allowBaseHref){ - // Prepare - var fullUrl = url, firstChar = url.substring(0,1); - allowBaseHref = (typeof allowBaseHref === 'undefined') ? true : allowBaseHref; - - // Check - if ( /[a-z]+\:\/\//.test(url) ) { - // Full URL - } - else if ( firstChar === '/' ) { - // Root URL - fullUrl = History.getRootUrl()+url.replace(/^\/+/,''); - } - else if ( firstChar === '#' ) { - // Anchor URL - fullUrl = History.getPageUrl().replace(/#.*/,'')+url; - } - else if ( firstChar === '?' ) { - // Query URL - fullUrl = History.getPageUrl().replace(/[\?#].*/,'')+url; - } - else { - // Relative URL - if ( allowBaseHref ) { - fullUrl = History.getBaseUrl()+url.replace(/^(\.\/)+/,''); - } else { - fullUrl = History.getBasePageUrl()+url.replace(/^(\.\/)+/,''); - } - // We have an if condition above as we do not want hashes - // which are relative to the baseHref in our URLs - // as if the baseHref changes, then all our bookmarks - // would now point to different locations - // whereas the basePageUrl will always stay the same - } - - // Return - return fullUrl.replace(/\#$/,''); - }; - - /** - * History.getShortUrl(url) - * Ensures that we have a relative URL and not a absolute URL - * @param {string} url - * @return {string} url - */ - History.getShortUrl = function(url){ - // Prepare - var shortUrl = url, baseUrl = History.getBaseUrl(), rootUrl = History.getRootUrl(); - - // Trim baseUrl - if ( History.emulated.pushState ) { - // We are in a if statement as when pushState is not emulated - // The actual url these short urls are relative to can change - // So within the same session, we the url may end up somewhere different - shortUrl = shortUrl.replace(baseUrl,''); - } - - // Trim rootUrl - shortUrl = shortUrl.replace(rootUrl,'/'); - - // Ensure we can still detect it as a state - if ( History.isTraditionalAnchor(shortUrl) ) { - shortUrl = './'+shortUrl; - } - - // Clean It - shortUrl = shortUrl.replace(/^(\.\/)+/g,'./').replace(/\#$/,''); - - // Return - return shortUrl; - }; - - // ---------------------------------------------------------------------- - // State Storage - - /** - * History.store - * The store for all session specific data - */ - History.store = amplify ? (amplify.store('History.store')||{}) : {}; - History.store.idToState = History.store.idToState||{}; - History.store.urlToId = History.store.urlToId||{}; - History.store.stateToId = History.store.stateToId||{}; - - /** - * History.idToState - * 1-1: State ID to State Object - */ - History.idToState = History.idToState||{}; - - /** - * History.stateToId - * 1-1: State String to State ID - */ - History.stateToId = History.stateToId||{}; - - /** - * History.urlToId - * 1-1: State URL to State ID - */ - History.urlToId = History.urlToId||{}; - - /** - * History.storedStates - * Store the states in an array - */ - History.storedStates = History.storedStates||[]; - - /** - * History.savedStates - * Saved the states in an array - */ - History.savedStates = History.savedStates||[]; - - /** - * History.getState() - * Get an object containing the data, title and url of the current state - * @param {Boolean} friendly - * @param {Boolean} create - * @return {Object} State - */ - History.getState = function(friendly,create){ - // Prepare - if ( typeof friendly === 'undefined' ) { friendly = true; } - if ( typeof create === 'undefined' ) { create = true; } - - // Fetch - var State = History.getLastSavedState(); - - // Create - if ( !State && create ) { - State = History.createStateObject(); - } - - // Adjust - if ( friendly ) { - State = History.cloneObject(State); - State.url = State.cleanUrl||State.url; - } - - // Return - return State; - }; - - /** - * History.getIdByState(State) - * Gets a ID for a State - * @param {State} newState - * @return {String} id - */ - History.getIdByState = function(newState){ - - // Fetch ID - var id = History.extractId(newState.url); - if ( !id ) { - // Find ID via State String - var str = History.getStateString(newState); - if ( typeof History.stateToId[str] !== 'undefined' ) { - id = History.stateToId[str]; - } - else if ( typeof History.store.stateToId[str] !== 'undefined' ) { - id = History.store.stateToId[str]; - } - else { - // Generate a new ID - while ( true ) { - id = String(Math.floor(Math.random()*1000)); - if ( typeof History.idToState[id] === 'undefined' && typeof History.store.idToState[id] === 'undefined' ) { - break; - } - } - - // Apply the new State to the ID - History.stateToId[str] = id; - History.idToState[id] = newState; - } - } - - // Return ID - return id; - }; - - /** - * History.normalizeState(State) - * Expands a State Object - * @param {object} State - * @return {object} - */ - History.normalizeState = function(oldState){ - // Prepare - if ( !oldState || (typeof oldState !== 'object') ) { - oldState = {}; - } - - // Check - if ( typeof oldState.normalized !== 'undefined' ) { - return oldState; - } - - // Adjust - if ( !oldState.data || (typeof oldState.data !== 'object') ) { - oldState.data = {}; - } - - // ---------------------------------------------------------------------- - - // Create - var newState = {}; - newState.normalized = true; - newState.title = oldState.title||''; - newState.url = History.getFullUrl(oldState.url?decodeURIComponent(oldState.url):(document.URL||document.location.href)); - newState.hash = History.getShortUrl(newState.url); - newState.data = History.cloneObject(oldState.data); - - // Fetch ID - newState.id = History.getIdByState(newState); - - // ---------------------------------------------------------------------- - - // Clean the URL - newState.cleanUrl = newState.url.replace(/\??\&_suid.*/,''); - newState.url = newState.cleanUrl; - - // Check to see if we have more than just a url - var dataNotEmpty = !History.isEmptyObject(newState.data); - - // Apply - if ( newState.title || dataNotEmpty ) { - // Add ID to Hash - newState.hash = History.getShortUrl(newState.url).replace(/\??\&_suid.*/,''); - if ( !/\?/.test(newState.hash) ) { - newState.hash += '?'; - } - newState.hash += '&_suid='+newState.id; - } - - // Create the Hashed URL - newState.hashedUrl = History.getFullUrl(newState.hash); - - // ---------------------------------------------------------------------- - - // Update the URL if we have a duplicate - if ( (History.emulated.pushState || History.bugs.safariPoll) && History.hasUrlDuplicate(newState) ) { - newState.url = newState.hashedUrl; - } - - // ---------------------------------------------------------------------- - - // Return - return newState; - }; - - /** - * History.createStateObject(data,title,url) - * Creates a object based on the data, title and url state params - * @param {object} data - * @param {string} title - * @param {string} url - * @return {object} - */ - History.createStateObject = function(data,title,url){ - // Hashify - var State = { - 'data': data, - 'title': title, - 'url': encodeURIComponent(url||"") - }; - - // Expand the State - State = History.normalizeState(State); - - // Return object - return State; - }; - - /** - * History.getStateById(id) - * Get a state by it's UID - * @param {String} id - */ - History.getStateById = function(id){ - // Prepare - id = String(id); - - // Retrieve - var State = History.idToState[id] || History.store.idToState[id] || undefined; - - // Return State - return State; - }; - - /** - * Get a State's String - * @param {State} passedState - */ - History.getStateString = function(passedState){ - // Prepare - var State = History.normalizeState(passedState); - - // Clean - var cleanedState = { - data: State.data, - title: passedState.title, - url: passedState.url - }; - - // Fetch - var str = JSON.stringify(cleanedState); - - // Return - return str; - }; - - /** - * Get a State's ID - * @param {State} passedState - * @return {String} id - */ - History.getStateId = function(passedState){ - // Prepare - var State = History.normalizeState(passedState); - - // Fetch - var id = State.id; - - // Return - return id; - }; - - /** - * History.getHashByState(State) - * Creates a Hash for the State Object - * @param {State} passedState - * @return {String} hash - */ - History.getHashByState = function(passedState){ - // Prepare - var hash, State = History.normalizeState(passedState); - - // Fetch - hash = State.hash; - - // Return - return hash; - }; - - /** - * History.extractId(url_or_hash) - * Get a State ID by it's URL or Hash - * @param {string} url_or_hash - * @return {string} id - */ - History.extractId = function ( url_or_hash ) { - // Prepare - var id; - - // Extract - var parts,url; - parts = /(.*)\&_suid=([0-9]+)$/.exec(url_or_hash); - url = parts ? (parts[1]||url_or_hash) : url_or_hash; - id = parts ? String(parts[2]||'') : ''; - - // Return - return id||false; - }; - - /** - * History.isTraditionalAnchor - * Checks to see if the url is a traditional anchor or not - * @param {String} url_or_hash - * @return {Boolean} - */ - History.isTraditionalAnchor = function(url_or_hash){ - // Check - var isTraditional = !(/[\/\?\.]/.test(url_or_hash)); - - // Return - return isTraditional; - }; - - /** - * History.extractState - * Get a State by it's URL or Hash - * @param {String} url_or_hash - * @return {State|null} - */ - History.extractState = function(url_or_hash,create){ - // Prepare - var State = null; - create = create||false; - - // Fetch SUID - var id = History.extractId(url_or_hash); - if ( id ) { - State = History.getStateById(id); - } - - // Fetch SUID returned no State - if ( !State ) { - // Fetch URL - var url = History.getFullUrl(url_or_hash); - - // Check URL - id = History.getIdByUrl(url)||false; - if ( id ) { - State = History.getStateById(id); - } - - // Create State - if ( !State && create && !History.isTraditionalAnchor(url_or_hash) ) { - State = History.createStateObject(null,null,url); - } - } - - // Return - return State; - }; - - /** - * History.getIdByUrl() - * Get a State ID by a State URL - */ - History.getIdByUrl = function(url){ - // Fetch - var id = History.urlToId[url] || History.store.urlToId[url] || undefined; - - // Return - return id; - }; - - /** - * History.getLastSavedState() - * Get an object containing the data, title and url of the current state - * @return {Object} State - */ - History.getLastSavedState = function(){ - return History.savedStates[History.savedStates.length-1]||undefined; - }; - - /** - * History.getLastStoredState() - * Get an object containing the data, title and url of the current state - * @return {Object} State - */ - History.getLastStoredState = function(){ - return History.storedStates[History.storedStates.length-1]||undefined; - }; - - /** - * History.hasUrlDuplicate - * Checks if a Url will have a url conflict - * @param {Object} newState - * @return {Boolean} hasDuplicate - */ - History.hasUrlDuplicate = function(newState) { - // Prepare - var hasDuplicate = false; - - // Fetch - var oldState = History.extractState(newState.url); - - // Check - hasDuplicate = oldState && oldState.id !== newState.id; - - // Return - return hasDuplicate; - }; - - /** - * History.storeState - * Store a State - * @param {Object} newState - * @return {Object} newState - */ - History.storeState = function(newState){ - // Store the State - History.urlToId[newState.url] = newState.id; - - // Push the State - History.storedStates.push(History.cloneObject(newState)); - - // Return newState - return newState; - }; - - /** - * History.isLastSavedState(newState) - * Tests to see if the state is the last state - * @param {Object} newState - * @return {boolean} isLast - */ - History.isLastSavedState = function(newState){ - // Prepare - var isLast = false; - - // Check - if ( History.savedStates.length ) { - var - newId = newState.id, - oldState = History.getLastSavedState(), - oldId = oldState.id; - - // Check - isLast = (newId === oldId); - } - - // Return - return isLast; - }; - - /** - * History.saveState - * Push a State - * @param {Object} newState - * @return {boolean} changed - */ - History.saveState = function(newState){ - // Check Hash - if ( History.isLastSavedState(newState) ) { - return false; - } - - // Push the State - History.savedStates.push(History.cloneObject(newState)); - - // Return true - return true; - }; - - /** - * History.getStateByIndex() - * Gets a state by the index - * @param {integer} index - * @return {Object} - */ - History.getStateByIndex = function(index){ - // Prepare - var State = null; - - // Handle - if ( typeof index === 'undefined' ) { - // Get the last inserted - State = History.savedStates[History.savedStates.length-1]; - } - else if ( index < 0 ) { - // Get from the end - State = History.savedStates[History.savedStates.length+index]; - } - else { - // Get from the beginning - State = History.savedStates[index]; - } - - // Return State - return State; - }; - - // ---------------------------------------------------------------------- - // Hash Helpers - - - /** - * History.escapeString() - * Escape a string - * @param {String} str - * @return {string} - */ - History.escapeString = function(str){ - return encodeURI(url).replace(/%25/g, "%", "g"); - }; - - /** - * History.getHash() - * @param {Location=} location - * Gets the current document hash - * Note: unlike location.hash, this is guaranteed to return the escaped hash in all browsers - * @return {string} - */ - History.getHash = function(location){ - if ( !location ) location = document.location; - var href = location.href.replace( /^[^#]*/, "" ); - return href.substr(1); - }; - - /** - * History.unescapeHash() - * normalize and Unescape a Hash - * @param {String} hash - * @return {string} - */ - History.unescapeHash = function(hash){ - // Prepare - var result = History.normalizeHash(hash); - - // Unescape hash - result = decodeURIComponent(result); - - // Return result - return result; - }; - - /** - * History.normalizeHash() - * normalize a hash across browsers - * @return {string} - */ - History.normalizeHash = function(hash){ - var result = hash.replace(/[^#]*#/,'').replace(/#.*/, ''); - - // Return result - return result; - }; - - /** - * History.setHash(hash) - * Sets the document hash - * @param {string} hash - * @return {History} - */ - History.setHash = function(hash,queue){ - // Handle Queueing - if ( queue !== false && History.busy() ) { - // Wait + Push to Queue - //History.debug('History.setHash: we must wait', arguments); - History.pushQueue({ - scope: History, - callback: History.setHash, - args: arguments, - queue: queue - }); - return false; - } - - // Log - //History.debug('History.setHash: called',hash); - - // Make Busy + Continue - History.busy(true); - - // Check if hash is a state - var State = History.extractState(hash,true); - if ( State && !History.emulated.pushState ) { - // Hash is a state so skip the setHash - //History.debug('History.setHash: Hash is a state so skipping the hash set with a direct pushState call',arguments); - - // PushState - History.pushState(State.data,State.title,State.url,false); - } - else if ( History.getHash() !== hash ) { - // Hash is a proper hash, so apply it - - // Handle browser bugs - if ( History.bugs.setHash ) { - // Fix Safari Bug https://bugs.webkit.org/show_bug.cgi?id=56249 - - // Fetch the base page - var pageUrl = History.getPageUrl(); - - // Safari hash apply - History.pushState(null,null,pageUrl+'#'+hash,false); - } - else { - // Normal hash apply - document.location.hash = hash; - } - } - - // Chain - return History; - }; - - /** - * History.escape() - * normalize and Escape a Hash - * @return {string} - */ - History.escapeHash = function(hash){ - var result = History.normalizeHash(hash); - - // Escape hash - result = window.encodeURIComponent(result); - - // IE6 Escape Bug - if ( !History.bugs.hashEscape ) { - // Restore common parts - result = result - .replace(/\%21/g,'!') - .replace(/\%26/g,'&') - .replace(/\%3D/g,'=') - .replace(/\%3F/g,'?'); - } - - // Return result - return result; - }; - - /** - * History.getHashByUrl(url) - * Extracts the Hash from a URL - * @param {string} url - * @return {string} url - */ - History.getHashByUrl = function(url){ - // Extract the hash - var hash = String(url) - .replace(/([^#]*)#?([^#]*)#?(.*)/, '$2') - ; - - // Unescape hash - hash = History.unescapeHash(hash); - - // Return hash - return hash; - }; - - /** - * History.setTitle(title) - * Applies the title to the document - * @param {State} newState - * @return {Boolean} - */ - History.setTitle = function(newState){ - // Prepare - var title = newState.title; - - // Initial - if ( !title ) { - var firstState = History.getStateByIndex(0); - if ( firstState && firstState.url === newState.url ) { - title = firstState.title||History.options.initialTitle; - } - } - - // Apply - try { - document.getElementsByTagName('title')[0].innerHTML = title.replace('<','<').replace('>','>').replace(' & ',' & '); - } - catch ( Exception ) { } - document.title = title; - - // Chain - return History; - }; - - // ---------------------------------------------------------------------- - // Queueing - - /** - * History.queues - * The list of queues to use - * First In, First Out - */ - History.queues = []; - - /** - * History.busy(value) - * @param {boolean} value [optional] - * @return {boolean} busy - */ - History.busy = function(value){ - // Apply - if ( typeof value !== 'undefined' ) { - //History.debug('History.busy: changing ['+(History.busy.flag||false)+'] to ['+(value||false)+']', History.queues.length); - History.busy.flag = value; - } - // Default - else if ( typeof History.busy.flag === 'undefined' ) { - History.busy.flag = false; - } - - // Queue - if ( !History.busy.flag ) { - // Execute the next item in the queue - clearTimeout(History.busy.timeout); - var fireNext = function(){ - if ( History.busy.flag ) return; - for ( var i=History.queues.length-1; i >= 0; --i ) { - var queue = History.queues[i]; - if ( queue.length === 0 ) continue; - var item = queue.shift(); - History.fireQueueItem(item); - History.busy.timeout = setTimeout(fireNext,History.options.busyDelay); - } - }; - History.busy.timeout = setTimeout(fireNext,History.options.busyDelay); - } - - // Return - return History.busy.flag; - }; - - /** - * History.fireQueueItem(item) - * Fire a Queue Item - * @param {Object} item - * @return {Mixed} result - */ - History.fireQueueItem = function(item){ - return item.callback.apply(item.scope||History,item.args||[]); - }; - - /** - * History.pushQueue(callback,args) - * Add an item to the queue - * @param {Object} item [scope,callback,args,queue] - */ - History.pushQueue = function(item){ - // Prepare the queue - History.queues[item.queue||0] = History.queues[item.queue||0]||[]; - - // Add to the queue - History.queues[item.queue||0].push(item); - - // Chain - return History; - }; - - /** - * History.queue (item,queue), (func,queue), (func), (item) - * Either firs the item now if not busy, or adds it to the queue - */ - History.queue = function(item,queue){ - // Prepare - if ( typeof item === 'function' ) { - item = { - callback: item - }; - } - if ( typeof queue !== 'undefined' ) { - item.queue = queue; - } - - // Handle - if ( History.busy() ) { - History.pushQueue(item); - } else { - History.fireQueueItem(item); - } - - // Chain - return History; - }; - - /** - * History.clearQueue() - * Clears the Queue - */ - History.clearQueue = function(){ - History.busy.flag = false; - History.queues = []; - return History; - }; - - - // ---------------------------------------------------------------------- - // IE Bug Fix - - /** - * History.stateChanged - * States whether or not the state has changed since the last double check was initialised - */ - History.stateChanged = false; - - /** - * History.doubleChecker - * Contains the timeout used for the double checks - */ - History.doubleChecker = false; - - /** - * History.doubleCheckComplete() - * Complete a double check - * @return {History} - */ - History.doubleCheckComplete = function(){ - // Update - History.stateChanged = true; - - // Clear - History.doubleCheckClear(); - - // Chain - return History; - }; - - /** - * History.doubleCheckClear() - * Clear a double check - * @return {History} - */ - History.doubleCheckClear = function(){ - // Clear - if ( History.doubleChecker ) { - clearTimeout(History.doubleChecker); - History.doubleChecker = false; - } - - // Chain - return History; - }; - - /** - * History.doubleCheck() - * Create a double check - * @return {History} - */ - History.doubleCheck = function(tryAgain){ - // Reset - History.stateChanged = false; - History.doubleCheckClear(); - - // Fix IE6,IE7 bug where calling history.back or history.forward does not actually change the hash (whereas doing it manually does) - // Fix Safari 5 bug where sometimes the state does not change: https://bugs.webkit.org/show_bug.cgi?id=42940 - if ( History.bugs.ieDoubleCheck ) { - // Apply Check - History.doubleChecker = setTimeout( - function(){ - History.doubleCheckClear(); - if ( !History.stateChanged ) { - //History.debug('History.doubleCheck: State has not yet changed, trying again', arguments); - // Re-Attempt - tryAgain(); - } - return true; - }, - History.options.doubleCheckInterval - ); - } - - // Chain - return History; - }; - - // ---------------------------------------------------------------------- - // Safari Bug Fix - - /** - * History.safariStatePoll() - * Poll the current state - * @return {History} - */ - History.safariStatePoll = function(){ - // Poll the URL - - // Get the Last State which has the new URL - var - urlState = History.extractState(document.URL||document.location.href), - newState; - - // Check for a difference - if ( !History.isLastSavedState(urlState) ) { - newState = urlState; - } - else { - return; - } - - // Check if we have a state with that url - // If not create it - if ( !newState ) { - //History.debug('History.safariStatePoll: new'); - newState = History.createStateObject(); - } - - // Apply the New State - //History.debug('History.safariStatePoll: trigger'); - History.Adapter.trigger(window,'popstate'); - - // Chain - return History; - }; - - // ---------------------------------------------------------------------- - // State Aliases - - /** - * History.back(queue) - * Send the browser history back one item - * @param {Integer} queue [optional] - */ - History.back = function(queue){ - //History.debug('History.back: called', arguments); - - // Handle Queueing - if ( queue !== false && History.busy() ) { - // Wait + Push to Queue - //History.debug('History.back: we must wait', arguments); - History.pushQueue({ - scope: History, - callback: History.back, - args: arguments, - queue: queue - }); - return false; - } - - // Make Busy + Continue - History.busy(true); - - // Fix certain browser bugs that prevent the state from changing - History.doubleCheck(function(){ - History.back(false); - }); - - // Go back - history.go(-1); - - // End back closure - return true; - }; - - /** - * History.forward(queue) - * Send the browser history forward one item - * @param {Integer} queue [optional] - */ - History.forward = function(queue){ - //History.debug('History.forward: called', arguments); - - // Handle Queueing - if ( queue !== false && History.busy() ) { - // Wait + Push to Queue - //History.debug('History.forward: we must wait', arguments); - History.pushQueue({ - scope: History, - callback: History.forward, - args: arguments, - queue: queue - }); - return false; - } - - // Make Busy + Continue - History.busy(true); - - // Fix certain browser bugs that prevent the state from changing - History.doubleCheck(function(){ - History.forward(false); - }); - - // Go forward - history.go(1); - - // End forward closure - return true; - }; - - /** - * History.go(index,queue) - * Send the browser history back or forward index times - * @param {Integer} queue [optional] - */ - History.go = function(index,queue){ - //History.debug('History.go: called', arguments); - - // Prepare - var i; - - // Handle - if ( index > 0 ) { - // Forward - for ( i=1; i<=index; ++i ) { - History.forward(queue); - } - } - else if ( index < 0 ) { - // Backward - for ( i=-1; i>=index; --i ) { - History.back(queue); - } - } - else { - throw new Error('History.go: History.go requires a positive or negative integer passed.'); - } - - // Chain - return History; - }; - - - // ---------------------------------------------------------------------- - // Initialise - - /** - * Create the initial State - */ - History.saveState(History.storeState(History.extractState(document.URL||document.location.href,true))); - - /** - * Bind for Saving Store - */ - if ( amplify ) { - History.onUnload = function(){ - // Prepare - var - currentStore = amplify.store('History.store')||{}, - item; - - // Ensure - currentStore.idToState = currentStore.idToState || {}; - currentStore.urlToId = currentStore.urlToId || {}; - currentStore.stateToId = currentStore.stateToId || {}; - - // Sync - for ( item in History.idToState ) { - if ( !History.idToState.hasOwnProperty(item) ) { - continue; - } - currentStore.idToState[item] = History.idToState[item]; - } - for ( item in History.urlToId ) { - if ( !History.urlToId.hasOwnProperty(item) ) { - continue; - } - currentStore.urlToId[item] = History.urlToId[item]; - } - for ( item in History.stateToId ) { - if ( !History.stateToId.hasOwnProperty(item) ) { - continue; - } - currentStore.stateToId[item] = History.stateToId[item]; - } - - // Update - History.store = currentStore; - - // Store - amplify.store('History.store',currentStore); - }; - // For Internet Explorer - History.intervalList.push(setInterval(History.onUnload,History.options.storeInterval)); - // For Other Browsers - History.Adapter.bind(window,'beforeunload',History.onUnload); - History.Adapter.bind(window,'unload',History.onUnload); - // Both are enabled for consistency - } - - - // ---------------------------------------------------------------------- - // HTML5 State Support - - if ( History.emulated.pushState ) { - /* - * Provide Skeleton for HTML4 Browsers - */ - - // Prepare - var emptyFunction = function(){}; - History.pushState = History.pushState||emptyFunction; - History.replaceState = History.replaceState||emptyFunction; - } - else { - /* - * Use native HTML5 History API Implementation - */ - - /** - * History.onPopState(event,extra) - * Refresh the Current State - */ - History.onPopState = function(event){ - // Reset the double check - History.doubleCheckComplete(); - - // Check for a Hash, and handle apporiatly - var currentHash = History.getHash(); - if ( currentHash ) { - // Expand Hash - var currentState = History.extractState(currentHash||document.URL||document.location.href,true); - if ( currentState ) { - // We were able to parse it, it must be a State! - // Let's forward to replaceState - //History.debug('History.onPopState: state anchor', currentHash, currentState); - History.replaceState(currentState.data, currentState.title, currentState.url, false); - } - else { - // Traditional Anchor - //History.debug('History.onPopState: traditional anchor', currentHash); - History.Adapter.trigger(window,'anchorchange'); - History.busy(false); - } - - // We don't care for hashes - History.expectedStateId = false; - return false; - } - - // Prepare - var newState = false; - - // Prepare - event = event||{}; - if ( typeof event.state === 'undefined' ) { - // jQuery - if ( typeof event.originalEvent !== 'undefined' && typeof event.originalEvent.state !== 'undefined' ) { - event.state = event.originalEvent.state||false; - } - // MooTools - else if ( typeof event.event !== 'undefined' && typeof event.event.state !== 'undefined' ) { - event.state = event.event.state||false; - } - // Ensure - event.state = (event.state||false); - } - - // Fetch State - if ( event.state ) { - // Vanilla: Back/forward button was used - newState = History.getStateById(event.state); - } - else if ( History.expectedStateId ) { - // Vanilla: A new state was pushed, and popstate was called manually - newState = History.getStateById(History.expectedStateId); - } - else { - // Initial State - newState = History.extractState(document.URL||document.location.href); - } - - // The State did not exist in our store - if ( !newState ) { - // Regenerate the State - newState = History.createStateObject(null,null,document.URL||document.location.href); - } - - // Clean - History.expectedStateId = false; - - // Check if we are the same state - if ( History.isLastSavedState(newState) ) { - // There has been no change (just the page's hash has finally propagated) - //History.debug('History.onPopState: no change', newState, History.savedStates); - History.busy(false); - return false; - } - - // Store the State - History.storeState(newState); - History.saveState(newState); - - // Force update of the title - History.setTitle(newState); - - // Fire Our Event - History.Adapter.trigger(window,'statechange'); - History.busy(false); - - // Return true - return true; - }; - History.Adapter.bind(window,'popstate',History.onPopState); - - /** - * History.pushState(data,title,url) - * Add a new State to the history object, become it, and trigger onpopstate - * We have to trigger for HTML4 compatibility - * @param {object} data - * @param {string} title - * @param {string} url - * @return {true} - */ - History.pushState = function(data,title,url,queue){ - //History.debug('History.pushState: called', arguments); - - // Check the State - if ( History.getHashByUrl(url) && History.emulated.pushState ) { - throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).'); - } - - // Handle Queueing - if ( queue !== false && History.busy() ) { - // Wait + Push to Queue - //History.debug('History.pushState: we must wait', arguments); - History.pushQueue({ - scope: History, - callback: History.pushState, - args: arguments, - queue: queue - }); - return false; - } - - // Make Busy + Continue - History.busy(true); - - // Create the newState - var newState = History.createStateObject(data,title,url); - - // Check it - if ( History.isLastSavedState(newState) ) { - // Won't be a change - History.busy(false); - } - else { - // Store the newState - History.storeState(newState); - History.expectedStateId = newState.id; - - // Push the newState - history.pushState(newState.id,newState.title,newState.url); - - // Fire HTML5 Event - History.Adapter.trigger(window,'popstate'); - } - - // End pushState closure - return true; - }; - - /** - * History.replaceState(data,title,url) - * Replace the State and trigger onpopstate - * We have to trigger for HTML4 compatibility - * @param {object} data - * @param {string} title - * @param {string} url - * @return {true} - */ - History.replaceState = function(data,title,url,queue){ - //History.debug('History.replaceState: called', arguments); - - // Check the State - if ( History.getHashByUrl(url) && History.emulated.pushState ) { - throw new Error('History.js does not support states with fragement-identifiers (hashes/anchors).'); - } - - // Handle Queueing - if ( queue !== false && History.busy() ) { - // Wait + Push to Queue - //History.debug('History.replaceState: we must wait', arguments); - History.pushQueue({ - scope: History, - callback: History.replaceState, - args: arguments, - queue: queue - }); - return false; - } - - // Make Busy + Continue - History.busy(true); - - // Create the newState - var newState = History.createStateObject(data,title,url); - - // Check it - if ( History.isLastSavedState(newState) ) { - // Won't be a change - History.busy(false); - } - else { - // Store the newState - History.storeState(newState); - History.expectedStateId = newState.id; - - // Push the newState - history.replaceState(newState.id,newState.title,newState.url); - - // Fire HTML5 Event - History.Adapter.trigger(window,'popstate'); - } - - // End replaceState closure - return true; - }; - - // Be aware, the following is only for native pushState implementations - // If you are wanting to include something for all browsers - // Then include it above this if block - - /** - * Setup Safari Fix - */ - if ( History.bugs.safariPoll ) { - History.intervalList.push(setInterval(History.safariStatePoll, History.options.safariPollInterval)); - } - - /** - * Ensure Cross Browser Compatibility - */ - if ( navigator.vendor === 'Apple Computer, Inc.' || (navigator.appCodeName||'') === 'Mozilla' ) { - /** - * Fix Safari HashChange Issue - */ - - // Setup Alias - History.Adapter.bind(window,'hashchange',function(){ - History.Adapter.trigger(window,'popstate'); - }); - - // Initialise Alias - if ( History.getHash() ) { - History.Adapter.onDomLoad(function(){ - History.Adapter.trigger(window,'hashchange'); - }); - } - } - - } // !History.emulated.pushState - - }; // History.initCore - - // Try and Initialise History - History.init(); - -})(window); diff --git a/kallithea/templates/base/root.html b/kallithea/templates/base/root.html --- a/kallithea/templates/base/root.html +++ b/kallithea/templates/base/root.html @@ -72,22 +72,12 @@ - ## EXTRA FOR JS <%block name="js_extra"/>