Merge pull request #13957 from nextcloud/refactor/oc-search-passwordconfirm-util

Move OC.Search, OC.PasswordConfirmation and OC.Util to the server bundle
This commit is contained in:
Morris Jobke 2019-02-01 09:09:41 +01:00 committed by GitHub
commit 8709f6b218
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 760 additions and 533 deletions

116
core/js/dist/main.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -492,13 +492,6 @@ Object.assign(window.OC, {
return path;
},
/**
* Do a search query and display the results
* @param {string} query the search query
*/
search: function (query) {
OC.Search.search(query, null, 0, 30);
},
/**
* Dialog helper for jquery dialogs.
*
@ -871,13 +864,6 @@ Object.assign(window.OC, {
});
/**
* @namespace OC.search
*/
OC.search.customResults = {};
/**
* @deprecated use get/setFormatter() instead
*/
OC.search.resultTypes = {};
OC.addStyle.loaded=[];
OC.addScript.loaded=[];
@ -1323,134 +1309,8 @@ function initCore() {
OC.PasswordConfirmation.init();
}
OC.PasswordConfirmation = {
callback: null,
pageLoadTime: null,
init: function() {
$('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this));
this.pageLoadTime = moment.now();
},
requiresPasswordConfirmation: function() {
var serverTimeDiff = this.pageLoadTime - (nc_pageLoad * 1000);
var timeSinceLogin = moment.now() - (serverTimeDiff + (nc_lastLogin * 1000));
// if timeSinceLogin > 30 minutes and user backend allows password confirmation
return (backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000);
},
/**
* @param {function} callback
*/
requirePasswordConfirmation: function(callback, options, rejectCallback) {
options = typeof options !== 'undefined' ? options : {};
var defaults = {
title: t('core','Authentication required'),
text: t(
'core',
'This action requires you to confirm your password'
),
confirm: t('core', 'Confirm'),
label: t('core','Password'),
error: '',
};
var config = _.extend(defaults, options);
var self = this;
if (this.requiresPasswordConfirmation()) {
OC.dialogs.prompt(
config.text,
config.title,
function (result, password) {
if (result && password !== '') {
self._confirmPassword(password, config);
} else if (_.isFunction(rejectCallback)) {
rejectCallback()
}
},
true,
config.label,
true
).then(function() {
var $dialog = $('.oc-dialog:visible');
$dialog.find('.ui-icon').remove();
$dialog.addClass('password-confirmation');
if (config.error !== '') {
var $error = $('<p></p>').addClass('msg warning').text(config.error);
}
$dialog.find('.oc-dialog-content').append($error);
$dialog.find('.oc-dialog-buttonrow').addClass('aside');
var $buttons = $dialog.find('button');
$buttons.eq(0).hide();
$buttons.eq(1).text(config.confirm);
});
}
this.callback = callback;
},
_confirmPassword: function(password, config) {
var self = this;
$.ajax({
url: OC.generateUrl('/login/confirm'),
data: {
password: password
},
type: 'POST',
success: function(response) {
nc_lastLogin = response.lastLogin;
if (_.isFunction(self.callback)) {
self.callback();
}
},
error: function() {
config.error = t('core', 'Failed to authenticate, try again');
OC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config);
}
});
}
};
$(document).ready(initCore);
/**
* Returns a human readable file size
* @param {number} size Size in bytes
* @param {boolean} skipSmallSizes return '< 1 kB' for small files
* @return {string}
*/
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;
}
/**
* Format an UNIX timestamp to a human understandable format
* @param {number} timestamp UNIX timestamp
@ -1486,381 +1346,6 @@ function relative_modified_date(timestamp) {
return OC.Util.relativeModifiedDate(timestamp * 1000);
}
/**
* Utility functions
* @namespace
*/
OC.Util = {
// TODO: remove original functions from global namespace
humanFileSize: 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 = OC.Util._chunkify(a);
var bb = OC.Util._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;
}
};
/**
* 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 = {
_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);
}
}
};
// fallback to hashchange when no history support
if (window.history.pushState) {
window.onpopstate = _.bind(OC.Util.History._onPopState, OC.Util.History);

View File

@ -27,7 +27,10 @@ import EventSource from './eventsource'
import L10N from './l10n'
import msg from './msg'
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 {
@ -39,5 +42,8 @@ export default {
L10N,
msg,
Notification,
PasswordConfirmation,
Plugins,
search,
Util,
}

View File

@ -0,0 +1,126 @@
/* global nc_pageLoad */
/*
* @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 $ from 'jquery'
import moment from 'moment'
import OC from './index'
/**
* @namespace OC.PasswordConfirmation
*/
export default {
callback: null,
pageLoadTime: null,
init: function () {
$('.password-confirm-required').on('click', _.bind(this.requirePasswordConfirmation, this));
this.pageLoadTime = moment.now();
},
requiresPasswordConfirmation: function () {
var serverTimeDiff = this.pageLoadTime - (nc_pageLoad * 1000);
var timeSinceLogin = moment.now() - (serverTimeDiff + (nc_lastLogin * 1000));
// if timeSinceLogin > 30 minutes and user backend allows password confirmation
return (backendAllowsPasswordConfirmation && timeSinceLogin > 30 * 60 * 1000);
},
/**
* @param {function} callback
*/
requirePasswordConfirmation: function (callback, options, rejectCallback) {
options = typeof options !== 'undefined' ? options : {};
var defaults = {
title: t('core', 'Authentication required'),
text: t(
'core',
'This action requires you to confirm your password'
),
confirm: t('core', 'Confirm'),
label: t('core', 'Password'),
error: '',
};
var config = _.extend(defaults, options);
var self = this;
if (this.requiresPasswordConfirmation()) {
OC.dialogs.prompt(
config.text,
config.title,
function (result, password) {
if (result && password !== '') {
self._confirmPassword(password, config);
} else if (_.isFunction(rejectCallback)) {
rejectCallback()
}
},
true,
config.label,
true
).then(function () {
var $dialog = $('.oc-dialog:visible');
$dialog.find('.ui-icon').remove();
$dialog.addClass('password-confirmation');
if (config.error !== '') {
var $error = $('<p></p>').addClass('msg warning').text(config.error);
}
$dialog.find('.oc-dialog-content').append($error);
$dialog.find('.oc-dialog-buttonrow').addClass('aside');
var $buttons = $dialog.find('button');
$buttons.eq(0).hide();
$buttons.eq(1).text(config.confirm);
});
}
this.callback = callback;
},
_confirmPassword: function (password, config) {
var self = this;
$.ajax({
url: OC.generateUrl('/login/confirm'),
data: {
password: password
},
type: 'POST',
success: function (response) {
nc_lastLogin = response.lastLogin;
if (_.isFunction(self.callback)) {
self.callback();
}
},
error: function () {
config.error = t('core', 'Failed to authenticate, try again');
OC.PasswordConfirmation.requirePasswordConfirmation(self.callback, config);
}
});
}
};

41
core/src/OC/search.js Normal file
View File

@ -0,0 +1,41 @@
/*
* @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 OC from './index'
/**
* Do a search query and display the results
* @param {string} query the search query
*/
const search = function (query) {
OC.Search.search(query, null, 0, 30);
};
/**
* @namespace OC.search
*/
search.customResults = {};
/**
* @deprecated use get/setFormatter() instead
*/
search.resultTypes = {};
export default search;

183
core/src/OC/util-history.js Normal file
View File

@ -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);
}
}
}

251
core/src/OC/util.js Normal file
View File

@ -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'
function chunkify(t) {
// Adapted from http://my.opera.com/GreyWyvern/blog/show.dml/1671288
let 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;
}
/**
* 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());
},
/**
* 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 = chunkify(a);
var bb = 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;
}
}

View File

@ -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;
}

View File

@ -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