utils.js 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. const b64 = require('base64-js');
  2. function arrayToB64(array) {
  3. return b64
  4. .fromByteArray(array)
  5. .replace(/\+/g, '-')
  6. .replace(/\//g, '_')
  7. .replace(/=/g, '');
  8. }
  9. function b64ToArray(str) {
  10. return b64.toByteArray(str + '==='.slice((str.length + 3) % 4));
  11. }
  12. function loadShim(polyfill) {
  13. return new Promise((resolve, reject) => {
  14. const shim = document.createElement('script');
  15. shim.src = polyfill;
  16. shim.addEventListener('load', () => resolve(true));
  17. shim.addEventListener('error', () => resolve(false));
  18. document.head.appendChild(shim);
  19. });
  20. }
  21. async function canHasSend() {
  22. try {
  23. const key = await window.crypto.subtle.generateKey(
  24. {
  25. name: 'AES-GCM',
  26. length: 128
  27. },
  28. true,
  29. ['encrypt', 'decrypt']
  30. );
  31. await window.crypto.subtle.encrypt(
  32. {
  33. name: 'AES-GCM',
  34. iv: window.crypto.getRandomValues(new Uint8Array(12)),
  35. tagLength: 128
  36. },
  37. key,
  38. new ArrayBuffer(8)
  39. );
  40. await window.crypto.subtle.importKey(
  41. 'raw',
  42. window.crypto.getRandomValues(new Uint8Array(16)),
  43. 'PBKDF2',
  44. false,
  45. ['deriveKey']
  46. );
  47. await window.crypto.subtle.importKey(
  48. 'raw',
  49. window.crypto.getRandomValues(new Uint8Array(16)),
  50. 'HKDF',
  51. false,
  52. ['deriveKey']
  53. );
  54. return true;
  55. } catch (err) {
  56. console.error(err);
  57. return false;
  58. }
  59. }
  60. function isFile(id) {
  61. return /^[0-9a-fA-F]{10}$/.test(id);
  62. }
  63. function copyToClipboard(str) {
  64. const aux = document.createElement('input');
  65. aux.setAttribute('value', str);
  66. aux.contentEditable = true;
  67. aux.readOnly = true;
  68. document.body.appendChild(aux);
  69. if (navigator.userAgent.match(/iphone|ipad|ipod/i)) {
  70. const range = document.createRange();
  71. range.selectNodeContents(aux);
  72. const sel = window.getSelection();
  73. sel.removeAllRanges();
  74. sel.addRange(range);
  75. aux.setSelectionRange(0, str.length);
  76. } else {
  77. aux.select();
  78. }
  79. const result = document.execCommand('copy');
  80. document.body.removeChild(aux);
  81. return result;
  82. }
  83. const LOCALIZE_NUMBERS = !!(
  84. typeof Intl === 'object' &&
  85. Intl &&
  86. typeof Intl.NumberFormat === 'function' &&
  87. typeof navigator === 'object'
  88. );
  89. const UNITS = ['B', 'kB', 'MB', 'GB'];
  90. function bytes(num) {
  91. if (num < 1) {
  92. return '0B';
  93. }
  94. const exponent = Math.min(Math.floor(Math.log10(num) / 3), UNITS.length - 1);
  95. const n = Number(num / Math.pow(1000, exponent));
  96. let nStr = n.toFixed(1);
  97. if (LOCALIZE_NUMBERS) {
  98. try {
  99. const locale = document.querySelector('html').lang;
  100. nStr = n.toLocaleString(locale, {
  101. minimumFractionDigits: 1,
  102. maximumFractionDigits: 1
  103. });
  104. } catch (e) {
  105. // fall through
  106. }
  107. }
  108. return `${nStr}${UNITS[exponent]}`;
  109. }
  110. function percent(ratio) {
  111. if (LOCALIZE_NUMBERS) {
  112. try {
  113. const locale = document.querySelector('html').lang;
  114. return ratio.toLocaleString(locale, { style: 'percent' });
  115. } catch (e) {
  116. // fall through
  117. }
  118. }
  119. return `${Math.floor(ratio * 100)}%`;
  120. }
  121. function number(n) {
  122. if (LOCALIZE_NUMBERS) {
  123. const locale = document.querySelector('html').lang;
  124. return n.toLocaleString(locale);
  125. }
  126. return n.toString();
  127. }
  128. function allowedCopy() {
  129. const support = !!document.queryCommandSupported;
  130. return support ? document.queryCommandSupported('copy') : false;
  131. }
  132. function delay(delay = 100) {
  133. return new Promise(resolve => setTimeout(resolve, delay));
  134. }
  135. function fadeOut(selector) {
  136. const classes = document.querySelector(selector).classList;
  137. classes.remove('effect--fadeIn');
  138. classes.add('effect--fadeOut');
  139. return delay(300);
  140. }
  141. function openLinksInNewTab(links, should = true) {
  142. links = links || Array.from(document.querySelectorAll('a:not([target])'));
  143. if (should) {
  144. links.forEach(l => {
  145. l.setAttribute('target', '_blank');
  146. l.setAttribute('rel', 'noopener noreferrer');
  147. });
  148. } else {
  149. links.forEach(l => {
  150. l.removeAttribute('target');
  151. l.removeAttribute('rel');
  152. });
  153. }
  154. return links;
  155. }
  156. module.exports = {
  157. fadeOut,
  158. delay,
  159. allowedCopy,
  160. bytes,
  161. percent,
  162. number,
  163. copyToClipboard,
  164. arrayToB64,
  165. b64ToArray,
  166. loadShim,
  167. canHasSend,
  168. isFile,
  169. openLinksInNewTab
  170. };