aboutsummaryrefslogtreecommitdiffstats
path: root/core/src/OC/util-history.js
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/OC/util-history.js')
-rw-r--r--core/src/OC/util-history.js168
1 files changed, 168 insertions, 0 deletions
diff --git a/core/src/OC/util-history.js b/core/src/OC/util-history.js
new file mode 100644
index 00000000000..7ecd0e098c6
--- /dev/null
+++ b/core/src/OC/util-history.js
@@ -0,0 +1,168 @@
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+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] 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) {
+ 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)
+ }
+ },
+}