util.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /**
  2. * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at>
  3. *
  4. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  5. * @author John Molakvoæ <skjnldsv@protonmail.com>
  6. *
  7. * @license AGPL-3.0-or-later
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU Affero General Public License as
  11. * published by the Free Software Foundation, either version 3 of the
  12. * License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU Affero General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Affero General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. import moment from 'moment'
  24. import History from './util-history'
  25. import OC from './index'
  26. import { formatFileSize as humanFileSize } from '@nextcloud/files'
  27. /**
  28. * @param {any} t -
  29. */
  30. function chunkify(t) {
  31. // Adapted from http://my.opera.com/GreyWyvern/blog/show.dml/1671288
  32. const tz = []
  33. let x = 0
  34. let y = -1
  35. let n = 0
  36. let c
  37. while (x < t.length) {
  38. c = t.charAt(x)
  39. // only include the dot in strings
  40. const m = ((!n && c === '.') || (c >= '0' && c <= '9'))
  41. if (m !== n) {
  42. // next chunk
  43. y++
  44. tz[y] = ''
  45. n = m
  46. }
  47. tz[y] += c
  48. x++
  49. }
  50. return tz
  51. }
  52. /**
  53. * Utility functions
  54. *
  55. * @namespace OC.Util
  56. */
  57. export default {
  58. History,
  59. /**
  60. * @deprecated use https://nextcloud.github.io/nextcloud-files/functions/formatFileSize.html
  61. */
  62. humanFileSize,
  63. /**
  64. * Returns a file size in bytes from a humanly readable string
  65. * Makes 2kB to 2048.
  66. * Inspired by computerFileSize in helper.php
  67. *
  68. * @param {string} string file size in human-readable format
  69. * @return {number} or null if string could not be parsed
  70. *
  71. *
  72. */
  73. computerFileSize(string) {
  74. if (typeof string !== 'string') {
  75. return null
  76. }
  77. const s = string.toLowerCase().trim()
  78. let bytes = null
  79. const bytesArray = {
  80. b: 1,
  81. k: 1024,
  82. kb: 1024,
  83. mb: 1024 * 1024,
  84. m: 1024 * 1024,
  85. gb: 1024 * 1024 * 1024,
  86. g: 1024 * 1024 * 1024,
  87. tb: 1024 * 1024 * 1024 * 1024,
  88. t: 1024 * 1024 * 1024 * 1024,
  89. pb: 1024 * 1024 * 1024 * 1024 * 1024,
  90. p: 1024 * 1024 * 1024 * 1024 * 1024,
  91. }
  92. const matches = s.match(/^[\s+]?([0-9]*)(\.([0-9]+))?( +)?([kmgtp]?b?)$/i)
  93. if (matches !== null) {
  94. bytes = parseFloat(s)
  95. if (!isFinite(bytes)) {
  96. return null
  97. }
  98. } else {
  99. return null
  100. }
  101. if (matches[5]) {
  102. bytes = bytes * bytesArray[matches[5]]
  103. }
  104. bytes = Math.round(bytes)
  105. return bytes
  106. },
  107. /**
  108. * @param {string|number} timestamp timestamp
  109. * @param {string} format date format, see momentjs docs
  110. * @return {string} timestamp formatted as requested
  111. */
  112. formatDate(timestamp, format) {
  113. if (window.TESTING === undefined) {
  114. console.warn('OC.Util.formatDate is deprecated and will be removed in Nextcloud 21. See @nextcloud/moment')
  115. }
  116. format = format || 'LLL'
  117. return moment(timestamp).format(format)
  118. },
  119. /**
  120. * @param {string|number} timestamp timestamp
  121. * @return {string} human readable difference from now
  122. */
  123. relativeModifiedDate(timestamp) {
  124. if (window.TESTING === undefined) {
  125. console.warn('OC.Util.relativeModifiedDate is deprecated and will be removed in Nextcloud 21. See @nextcloud/moment')
  126. }
  127. const diff = moment().diff(moment(timestamp))
  128. if (diff >= 0 && diff < 45000) {
  129. return t('core', 'seconds ago')
  130. }
  131. return moment(timestamp).fromNow()
  132. },
  133. /**
  134. * Returns the width of a generic browser scrollbar
  135. *
  136. * @return {number} width of scrollbar
  137. */
  138. getScrollBarWidth() {
  139. if (this._scrollBarWidth) {
  140. return this._scrollBarWidth
  141. }
  142. const inner = document.createElement('p')
  143. inner.style.width = '100%'
  144. inner.style.height = '200px'
  145. const outer = document.createElement('div')
  146. outer.style.position = 'absolute'
  147. outer.style.top = '0px'
  148. outer.style.left = '0px'
  149. outer.style.visibility = 'hidden'
  150. outer.style.width = '200px'
  151. outer.style.height = '150px'
  152. outer.style.overflow = 'hidden'
  153. outer.appendChild(inner)
  154. document.body.appendChild(outer)
  155. const w1 = inner.offsetWidth
  156. outer.style.overflow = 'scroll'
  157. let w2 = inner.offsetWidth
  158. if (w1 === w2) {
  159. w2 = outer.clientWidth
  160. }
  161. document.body.removeChild(outer)
  162. this._scrollBarWidth = (w1 - w2)
  163. return this._scrollBarWidth
  164. },
  165. /**
  166. * Remove the time component from a given date
  167. *
  168. * @param {Date} date date
  169. * @return {Date} date with stripped time
  170. */
  171. stripTime(date) {
  172. // FIXME: likely to break when crossing DST
  173. // would be better to use a library like momentJS
  174. return new Date(date.getFullYear(), date.getMonth(), date.getDate())
  175. },
  176. /**
  177. * Compare two strings to provide a natural sort
  178. *
  179. * @param {string} a first string to compare
  180. * @param {string} b second string to compare
  181. * @return {number} -1 if b comes before a, 1 if a comes before b
  182. * or 0 if the strings are identical
  183. */
  184. naturalSortCompare(a, b) {
  185. let x
  186. const aa = chunkify(a)
  187. const bb = chunkify(b)
  188. for (x = 0; aa[x] && bb[x]; x++) {
  189. if (aa[x] !== bb[x]) {
  190. const aNum = Number(aa[x]); const bNum = Number(bb[x])
  191. // note: == is correct here
  192. /* eslint-disable-next-line */
  193. if (aNum == aa[x] && bNum == bb[x]) {
  194. return aNum - bNum
  195. } else {
  196. // Note: This locale setting isn't supported by all browsers but for the ones
  197. // that do there will be more consistency between client-server sorting
  198. return aa[x].localeCompare(bb[x], OC.getLanguage())
  199. }
  200. }
  201. }
  202. return aa.length - bb.length
  203. },
  204. /**
  205. * Calls the callback in a given interval until it returns true
  206. *
  207. * @param {Function} callback function to call on success
  208. * @param {number} interval in milliseconds
  209. */
  210. waitFor(callback, interval) {
  211. const internalCallback = function() {
  212. if (callback() !== true) {
  213. setTimeout(internalCallback, interval)
  214. }
  215. }
  216. internalCallback()
  217. },
  218. /**
  219. * Checks if a cookie with the given name is present and is set to the provided value.
  220. *
  221. * @param {string} name name of the cookie
  222. * @param {string} value value of the cookie
  223. * @return {boolean} true if the cookie with the given name has the given value
  224. */
  225. isCookieSetToValue(name, value) {
  226. const cookies = document.cookie.split(';')
  227. for (let i = 0; i < cookies.length; i++) {
  228. const cookie = cookies[i].split('=')
  229. if (cookie[0].trim() === name && cookie[1].trim() === value) {
  230. return true
  231. }
  232. }
  233. return false
  234. },
  235. }