diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2019-01-31 18:50:59 +0100 |
---|---|---|
committer | Christoph Wurst <christoph@winzerhof-wurst.at> | 2019-02-01 08:25:01 +0100 |
commit | 0d43ef06f5dbd05edfe61e9c78461e15312834ea (patch) | |
tree | e11ee21cdca73769064e4a19bc65a48711cecaaa /core/src | |
parent | 9de02d326799285896e163cb7b6b1b47a1a5bbb9 (diff) | |
download | nextcloud-server-0d43ef06f5dbd05edfe61e9c78461e15312834ea.tar.gz nextcloud-server-0d43ef06f5dbd05edfe61e9c78461e15312834ea.zip |
Add OC.Util to the server bundle
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Diffstat (limited to 'core/src')
-rw-r--r-- | core/src/OC/index.js | 2 | ||||
-rw-r--r-- | core/src/OC/util-history.js | 183 | ||||
-rw-r--r-- | core/src/OC/util.js | 251 | ||||
-rw-r--r-- | core/src/Util/human-file-size.js | 51 | ||||
-rw-r--r-- | core/src/globals.js | 2 |
5 files changed, 489 insertions, 0 deletions
diff --git a/core/src/OC/index.js b/core/src/OC/index.js index 6779eb7d7f7..63ba47ca7fa 100644 --- a/core/src/OC/index.js +++ b/core/src/OC/index.js @@ -30,6 +30,7 @@ import Notification from './notification' import PasswordConfirmation from './password-confirmation' import Plugins from './plugins' import search from './search' +import Util from './util' /** @namespace OC */ export default { @@ -44,4 +45,5 @@ export default { PasswordConfirmation, Plugins, search, + Util, } diff --git a/core/src/OC/util-history.js b/core/src/OC/util-history.js new file mode 100644 index 00000000000..3dd1a104ef8 --- /dev/null +++ b/core/src/OC/util-history.js @@ -0,0 +1,183 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * 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' + +/** + * 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: function (params, url, replace) { + var 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 + var isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; + if (isFirefox && parseInt(navigator.userAgent.split('/').pop()) < 51) { + var patterns = document.querySelectorAll('[fill^="url(#"], [stroke^="url(#"], [filter^="url(#invert"]'); + for (var i = 0, ii = patterns.length, pattern; i < ii; i++) { + pattern = patterns[i]; + pattern.style.fill = pattern.style.fill; + 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); + } + } + // use URL hash for IE8 + else { + 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: function (params, url) { + return 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: function (params, url) { + return this._pushState(params, url, true); + }, + + /** + * Add a popstate handler + * + * @param handler function + */ + addOnPopStateHandler: function (handler) { + this._handlers.push(handler); + }, + + /** + * Parse a query string from the hash part of the URL. + * (workaround for IE8 / IE9) + */ + _parseHashQuery: function () { + var hash = window.location.hash, + pos = hash.indexOf('?'); + if (pos >= 0) { + return hash.substr(pos + 1); + } + if (hash.length) { + // remove hash sign + return hash.substr(1); + } + return ''; + }, + + _decodeQuery: function (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 map of parameters + */ + parseUrlQuery: function () { + var query = this._parseHashQuery(), + 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: function (e) { + if (this._cancelPop) { + this._cancelPop = false; + return; + } + var params; + if (!this._handlers.length) { + return; + } + params = (e && e.state); + if (_.isString(params)) { + params = OC.parseQueryString(params); + } else if (!params) { + params = this.parseUrlQuery() || {}; + } + for (var i = 0; i < this._handlers.length; i++) { + this._handlers[i](params); + } + } +} diff --git a/core/src/OC/util.js b/core/src/OC/util.js new file mode 100644 index 00000000000..c139a57be07 --- /dev/null +++ b/core/src/OC/util.js @@ -0,0 +1,251 @@ +/* global t */ + +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * 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 'jquery' +import moment from 'moment' + +import History from './util-history' +import OC from './index' +import humanFileSize from '../Util/human-file-size' + +/** + * Utility functions + * @namespace OC.Util + */ +export default { + + History, + + // TODO: remove original functions from global namespace + humanFileSize, + + /** + * Returns a file size in bytes from a humanly readable string + * Makes 2kB to 2048. + * Inspired by computerFileSize in helper.php + * @param {string} string file size in human readable format + * @return {number} or null if string could not be parsed + * + * + */ + computerFileSize: function (string) { + if (typeof string !== 'string') { + return null; + } + + var s = string.toLowerCase().trim(); + var bytes = null; + + var bytesArray = { + 'b': 1, + 'k': 1024, + 'kb': 1024, + 'mb': 1024 * 1024, + 'm': 1024 * 1024, + 'gb': 1024 * 1024 * 1024, + 'g': 1024 * 1024 * 1024, + 'tb': 1024 * 1024 * 1024 * 1024, + 't': 1024 * 1024 * 1024 * 1024, + 'pb': 1024 * 1024 * 1024 * 1024 * 1024, + 'p': 1024 * 1024 * 1024 * 1024 * 1024 + }; + + var matches = s.match(/^[\s+]?([0-9]*)(\.([0-9]+))?( +)?([kmgtp]?b?)$/i); + if (matches !== null) { + bytes = parseFloat(s); + if (!isFinite(bytes)) { + return null; + } + } else { + return null; + } + if (matches[5]) { + bytes = bytes * bytesArray[matches[5]]; + } + + bytes = Math.round(bytes); + return bytes; + }, + + /** + * @param timestamp + * @param format + * @returns {string} timestamp formatted as requested + */ + formatDate: function (timestamp, format) { + format = format || "LLL"; + return moment(timestamp).format(format); + }, + + /** + * @param timestamp + * @returns {string} human readable difference from now + */ + relativeModifiedDate: function (timestamp) { + var diff = moment().diff(moment(timestamp)); + if (diff >= 0 && diff < 45000) { + return t('core', 'seconds ago'); + } + return moment(timestamp).fromNow(); + }, + + /** + * Returns whether this is IE + * + * @return {bool} true if this is IE, false otherwise + */ + isIE: function () { + return $('html').hasClass('ie'); + }, + + /** + * Returns the width of a generic browser scrollbar + * + * @return {int} width of scrollbar + */ + getScrollBarWidth: function () { + if (this._scrollBarWidth) { + return this._scrollBarWidth; + } + + var inner = document.createElement('p'); + inner.style.width = "100%"; + inner.style.height = "200px"; + + var outer = document.createElement('div'); + outer.style.position = "absolute"; + outer.style.top = "0px"; + outer.style.left = "0px"; + outer.style.visibility = "hidden"; + outer.style.width = "200px"; + outer.style.height = "150px"; + outer.style.overflow = "hidden"; + outer.appendChild(inner); + + document.body.appendChild(outer); + var w1 = inner.offsetWidth; + outer.style.overflow = 'scroll'; + var w2 = inner.offsetWidth; + if (w1 === w2) { + w2 = outer.clientWidth; + } + + document.body.removeChild(outer); + + this._scrollBarWidth = (w1 - w2); + + return this._scrollBarWidth; + }, + + /** + * Remove the time component from a given date + * + * @param {Date} date date + * @return {Date} date with stripped time + */ + stripTime: function (date) { + // FIXME: likely to break when crossing DST + // would be better to use a library like momentJS + return new Date(date.getFullYear(), date.getMonth(), date.getDate()); + }, + + _chunkify: function (t) { + // Adapted from http://my.opera.com/GreyWyvern/blog/show.dml/1671288 + var tz = [], x = 0, y = -1, n = 0, code, c; + + while (x < t.length) { + c = t.charAt(x); + // only include the dot in strings + var m = ((!n && c === '.') || (c >= '0' && c <= '9')); + if (m !== n) { + // next chunk + y++; + tz[y] = ''; + n = m; + } + tz[y] += c; + x++; + } + return tz; + }, + + /** + * Compare two strings to provide a natural sort + * @param a first string to compare + * @param b second string to compare + * @return -1 if b comes before a, 1 if a comes before b + * or 0 if the strings are identical + */ + naturalSortCompare: function (a, b) { + var x; + var aa = this._chunkify(a); + var bb = this._chunkify(b); + + for (x = 0; aa[x] && bb[x]; x++) { + if (aa[x] !== bb[x]) { + var aNum = Number(aa[x]), bNum = Number(bb[x]); + // note: == is correct here + if (aNum == aa[x] && bNum == bb[x]) { + return aNum - bNum; + } else { + // Note: This locale setting isn't supported by all browsers but for the ones + // that do there will be more consistency between client-server sorting + return aa[x].localeCompare(bb[x], OC.getLanguage()); + } + } + } + return aa.length - bb.length; + }, + + /** + * Calls the callback in a given interval until it returns true + * @param {function} callback + * @param {integer} interval in milliseconds + */ + waitFor: function (callback, interval) { + var internalCallback = function () { + if (callback() !== true) { + setTimeout(internalCallback, interval); + } + }; + + internalCallback(); + }, + + /** + * Checks if a cookie with the given name is present and is set to the provided value. + * @param {string} name name of the cookie + * @param {string} value value of the cookie + * @return {boolean} true if the cookie with the given name has the given value + */ + isCookieSetToValue: function (name, value) { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = cookies[i].split('='); + if (cookie[0].trim() === name && cookie[1].trim() === value) { + return true; + } + } + return false; + } +} diff --git a/core/src/Util/human-file-size.js b/core/src/Util/human-file-size.js new file mode 100644 index 00000000000..e671a86d053 --- /dev/null +++ b/core/src/Util/human-file-size.js @@ -0,0 +1,51 @@ +/* + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @license GNU AGPL version 3 or any later version + * + * 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/>. + */ + +/** + * Returns a human readable file size + * @param {number} size Size in bytes + * @param {boolean} skipSmallSizes return '< 1 kB' for small files + * @return {string} + */ +export default function humanFileSize (size, skipSmallSizes) { + var humanList = ['B', 'KB', 'MB', 'GB', 'TB']; + // Calculate Log with base 1024: size = 1024 ** order + var order = size > 0 ? Math.floor(Math.log(size) / Math.log(1024)) : 0; + // Stay in range of the byte sizes that are defined + order = Math.min(humanList.length - 1, order); + var readableFormat = humanList[order]; + var relativeSize = (size / Math.pow(1024, order)).toFixed(1); + if (skipSmallSizes === true && order === 0) { + if (relativeSize !== "0.0") { + return '< 1 KB'; + } else { + return '0 KB'; + } + } + if (order < 2) { + relativeSize = parseFloat(relativeSize).toFixed(0); + } else if (relativeSize.substr(relativeSize.length - 2, 2) === '.0') { + relativeSize = relativeSize.substr(0, relativeSize.length - 2); + } else { + relativeSize = parseFloat(relativeSize).toLocaleString(OC.getCanonicalLocale()); + } + return relativeSize + ' ' + readableFormat; +} diff --git a/core/src/globals.js b/core/src/globals.js index ad1997c470d..4542a59b7b3 100644 --- a/core/src/globals.js +++ b/core/src/globals.js @@ -52,6 +52,7 @@ import OC from './OC/index' import OCP from './OCP/index' import OCA from './OCA/index' import escapeHTML from './Util/escapeHTML' +import humanFileSize from './Util/human-file-size' window['_'] = _ window['$'] = $ @@ -74,6 +75,7 @@ window['OC'] = OC window['OCP'] = OCP window['OCA'] = OCA window['escapeHTML'] = escapeHTML +window['humanFileSize'] = humanFileSize /** * translate a string |