summaryrefslogtreecommitdiffstats
path: root/core/src
diff options
context:
space:
mode:
authorChristoph Wurst <christoph@winzerhof-wurst.at>2019-01-31 18:50:59 +0100
committerChristoph Wurst <christoph@winzerhof-wurst.at>2019-02-01 08:25:01 +0100
commit0d43ef06f5dbd05edfe61e9c78461e15312834ea (patch)
treee11ee21cdca73769064e4a19bc65a48711cecaaa /core/src
parent9de02d326799285896e163cb7b6b1b47a1a5bbb9 (diff)
downloadnextcloud-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.js2
-rw-r--r--core/src/OC/util-history.js183
-rw-r--r--core/src/OC/util.js251
-rw-r--r--core/src/Util/human-file-size.js51
-rw-r--r--core/src/globals.js2
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