nextcloud-server/core/src/OC/util-history.js

189 lines
5.3 KiB
JavaScript

/**
* @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import _ from 'underscore'
import OC from './index.js'
/**
* Utility class for the history API,
* includes fallback to using the URL hash when
* the browser doesn't support the history API.
*
* @namespace OC.Util.History
*/
export default {
_handlers: [],
/**
* Push the current URL parameters to the history stack
* and change the visible URL.
* Note: this includes a workaround for IE8/IE9 that uses
* the hash part instead of the search part.
*
* @param {object | string} params to append to the URL, can be either a string
* or a map
* @param {string} [url] URL to be used, otherwise the current URL will be used,
* using the params as query string
* @param {boolean} [replace=false] whether to replace instead of pushing
*/
_pushState(params, url, replace) {
let strParams
if (typeof (params) === 'string') {
strParams = params
} else {
strParams = OC.buildQueryString(params)
}
if (window.history.pushState) {
url = url || location.pathname + '?' + strParams
// Workaround for bug with SVG and window.history.pushState on Firefox < 51
// https://bugzilla.mozilla.org/show_bug.cgi?id=652991
const isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1
if (isFirefox && parseInt(navigator.userAgent.split('/').pop()) < 51) {
const patterns = document.querySelectorAll('[fill^="url(#"], [stroke^="url(#"], [filter^="url(#invert"]')
for (let i = 0, ii = patterns.length, pattern; i < ii; i++) {
pattern = patterns[i]
// eslint-disable-next-line no-self-assign
pattern.style.fill = pattern.style.fill
// eslint-disable-next-line no-self-assign
pattern.style.stroke = pattern.style.stroke
pattern.removeAttribute('filter')
pattern.setAttribute('filter', 'url(#invert)')
}
}
if (replace) {
window.history.replaceState(params, '', url)
} else {
window.history.pushState(params, '', url)
}
} else {
// use URL hash for IE8
window.location.hash = '?' + strParams
// inhibit next onhashchange that just added itself
// to the event queue
this._cancelPop = true
}
},
/**
* Push the current URL parameters to the history stack
* and change the visible URL.
* Note: this includes a workaround for IE8/IE9 that uses
* the hash part instead of the search part.
*
* @param {object | string} params to append to the URL, can be either a string or a map
* @param {string} [url] URL to be used, otherwise the current URL will be used, using the params as query string
*/
pushState(params, url) {
this._pushState(params, url, false)
},
/**
* Push the current URL parameters to the history stack
* and change the visible URL.
* Note: this includes a workaround for IE8/IE9 that uses
* the hash part instead of the search part.
*
* @param {object | string} params to append to the URL, can be either a string
* or a map
* @param {string} [url] URL to be used, otherwise the current URL will be used,
* using the params as query string
*/
replaceState(params, url) {
this._pushState(params, url, true)
},
/**
* Add a popstate handler
*
* @param {Function} handler handler
*/
addOnPopStateHandler(handler) {
this._handlers.push(handler)
},
/**
* Parse a query string from the hash part of the URL.
* (workaround for IE8 / IE9)
*
* @return {string}
*/
_parseHashQuery() {
const hash = window.location.hash
const pos = hash.indexOf('?')
if (pos >= 0) {
return hash.substr(pos + 1)
}
if (hash.length) {
// remove hash sign
return hash.substr(1)
}
return ''
},
_decodeQuery(query) {
return query.replace(/\+/g, ' ')
},
/**
* Parse the query/search part of the URL.
* Also try and parse it from the URL hash (for IE8)
*
* @return {object} map of parameters
*/
parseUrlQuery() {
const query = this._parseHashQuery()
let params
// try and parse from URL hash first
if (query) {
params = OC.parseQueryString(this._decodeQuery(query))
}
// else read from query attributes
params = _.extend(params || {}, OC.parseQueryString(this._decodeQuery(location.search)))
return params || {}
},
_onPopState(e) {
debugger
if (this._cancelPop) {
this._cancelPop = false
return
}
let params
if (!this._handlers.length) {
return
}
params = (e && e.state)
if (_.isString(params)) {
params = OC.parseQueryString(params)
} else if (!params) {
params = this.parseUrlQuery() || {}
}
for (let i = 0; i < this._handlers.length; i++) {
this._handlers[i](params)
}
},
}