utils.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. import _ from 'lodash';
  2. import baseX from 'base-x';
  3. import PAKO from 'pako';
  4. import {Buffer} from 'safe-buffer';
  5. import sjclWrapper from './sjclWrapper';
  6. export const pako = PAKO;
  7. const BASE58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
  8. const bs58 = baseX(BASE58);
  9. export function sleep(ms) {
  10. return new Promise(resolve => setTimeout(resolve, ms));
  11. }
  12. export function stringToHex(str) {
  13. return Buffer.from(str).toString('hex');
  14. }
  15. export function hexToString(str) {
  16. return Buffer.from(str, 'hex').toString();
  17. }
  18. export function randomArray(len) {
  19. const a = new Uint8Array(len);
  20. window.crypto.getRandomValues(a);
  21. return a;
  22. }
  23. export function randomHexString(len) {
  24. return Buffer.from(randomArray(len)).toString('hex');
  25. }
  26. export function formatDate(d, format) {
  27. if (!format)
  28. format = 'normal';
  29. switch (format) {
  30. case 'normal':
  31. return `${d.getDate().toString().padStart(2, '0')}.${(d.getMonth() + 1).toString().padStart(2, '0')}.${d.getFullYear()} ` +
  32. `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
  33. case 'coDate':
  34. return `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
  35. case 'noDate':
  36. return `${d.getDate().toString().padStart(2, '0')}.${(d.getMonth() + 1).toString().padStart(2, '0')}.${d.getFullYear()}`;
  37. }
  38. }
  39. export function fallbackCopyTextToClipboard(text) {
  40. let textArea = document.createElement('textarea');
  41. textArea.value = text;
  42. document.body.appendChild(textArea);
  43. textArea.focus();
  44. textArea.select();
  45. let result = false;
  46. try {
  47. result = document.execCommand('copy');
  48. } catch (e) {
  49. //
  50. }
  51. document.body.removeChild(textArea);
  52. return result;
  53. }
  54. export async function copyTextToClipboard(text) {
  55. if (!navigator.clipboard) {
  56. return fallbackCopyTextToClipboard(text);
  57. }
  58. let result = false;
  59. try {
  60. await navigator.clipboard.writeText(text);
  61. result = true;
  62. } catch (e) {
  63. //
  64. }
  65. return result;
  66. }
  67. export function toBase58(data) {
  68. return bs58.encode(Buffer.from(data));
  69. }
  70. export function fromBase58(data) {
  71. return bs58.decode(data);
  72. }
  73. //base-x слишком тормозит, используем sjcl
  74. export function toBase64(data) {
  75. return sjclWrapper.codec.base64.fromBits(
  76. sjclWrapper.codec.bytes.toBits(Buffer.from(data))
  77. );
  78. }
  79. //base-x слишком тормозит, используем sjcl
  80. export function fromBase64(data) {
  81. return Buffer.from(sjclWrapper.codec.bytes.fromBits(
  82. sjclWrapper.codec.base64.toBits(data)
  83. ));
  84. }
  85. export function getObjDiff(oldObj, newObj, opts = {}) {
  86. const {
  87. exclude = [],
  88. excludeAdd = [],
  89. excludeDel = [],
  90. } = opts;
  91. const ex = new Set(exclude);
  92. const exAdd = new Set(excludeAdd);
  93. const exDel = new Set(excludeDel);
  94. const makeObjDiff = (oldObj, newObj, keyPath) => {
  95. const result = {__isDiff: true, change: {}, add: {}, del: []};
  96. keyPath = `${keyPath}${keyPath ? '/' : ''}`;
  97. for (const key of Object.keys(oldObj)) {
  98. const kp = `${keyPath}${key}`;
  99. if (newObj.hasOwnProperty(key)) {
  100. if (ex.has(kp))
  101. continue;
  102. if (!_.isEqual(oldObj[key], newObj[key])) {
  103. if (_.isObject(oldObj[key]) && _.isObject(newObj[key])) {
  104. result.change[key] = makeObjDiff(oldObj[key], newObj[key], kp);
  105. } else {
  106. result.change[key] = _.cloneDeep(newObj[key]);
  107. }
  108. }
  109. } else {
  110. if (exDel.has(kp))
  111. continue;
  112. result.del.push(key);
  113. }
  114. }
  115. for (const key of Object.keys(newObj)) {
  116. const kp = `${keyPath}${key}`;
  117. if (exAdd.has(kp))
  118. continue;
  119. if (!oldObj.hasOwnProperty(key)) {
  120. result.add[key] = _.cloneDeep(newObj[key]);
  121. }
  122. }
  123. return result;
  124. }
  125. return makeObjDiff(oldObj, newObj, '');
  126. }
  127. export function isObjDiff(diff) {
  128. return (_.isObject(diff) && diff.__isDiff && diff.change && diff.add && diff.del);
  129. }
  130. export function isEmptyObjDiff(diff) {
  131. return (!isObjDiff(diff) ||
  132. !(Object.keys(diff.change).length ||
  133. Object.keys(diff.add).length ||
  134. diff.del.length
  135. )
  136. );
  137. }
  138. export function isEmptyObjDiffDeep(diff, opts = {}) {
  139. if (!isObjDiff(diff))
  140. return true;
  141. const {
  142. isApplyChange = true,
  143. isApplyAdd = true,
  144. isApplyDel = true,
  145. } = opts;
  146. let notEmptyDeep = false;
  147. const change = diff.change;
  148. for (const key of Object.keys(change)) {
  149. if (_.isObject(change[key]))
  150. notEmptyDeep |= !isEmptyObjDiffDeep(change[key], opts);
  151. else if (isApplyChange)
  152. notEmptyDeep = true;
  153. }
  154. return !(
  155. notEmptyDeep ||
  156. (isApplyAdd && Object.keys(diff.add).length) ||
  157. (isApplyDel && diff.del.length)
  158. );
  159. }
  160. export function applyObjDiff(obj, diff, opts = {}) {
  161. const {
  162. isAddChanged = false,
  163. isApplyChange = true,
  164. isApplyAdd = true,
  165. isApplyDel = true,
  166. } = opts;
  167. let result = _.cloneDeep(obj);
  168. if (!diff.__isDiff)
  169. return result;
  170. const change = diff.change;
  171. for (const key of Object.keys(change)) {
  172. if (result.hasOwnProperty(key)) {
  173. if (_.isObject(change[key])) {
  174. result[key] = applyObjDiff(result[key], change[key], opts);
  175. } else {
  176. if (isApplyChange)
  177. result[key] = _.cloneDeep(change[key]);
  178. }
  179. } else if (isAddChanged) {
  180. result[key] = _.cloneDeep(change[key]);
  181. }
  182. }
  183. if (isApplyAdd) {
  184. for (const key of Object.keys(diff.add)) {
  185. result[key] = _.cloneDeep(diff.add[key]);
  186. }
  187. }
  188. if (isApplyDel && diff.del.length) {
  189. for (const key of diff.del) {
  190. delete result[key];
  191. }
  192. if (_.isArray(result))
  193. result = result.filter(v => v);
  194. }
  195. return result;
  196. }
  197. export function parseQuery(str) {
  198. if (typeof str != 'string' || str.length == 0)
  199. return {};
  200. let s = str.split('&');
  201. let s_length = s.length;
  202. let bit, query = {}, first, second;
  203. for (let i = 0; i < s_length; i++) {
  204. bit = s[i].split('=');
  205. first = decodeURIComponent(bit[0]);
  206. if (first.length == 0)
  207. continue;
  208. second = decodeURIComponent(bit[1]);
  209. if (typeof query[first] == 'undefined')
  210. query[first] = second;
  211. else
  212. if (query[first] instanceof Array)
  213. query[first].push(second);
  214. else
  215. query[first] = [query[first], second];
  216. }
  217. return query;
  218. }
  219. export function escapeXml(str) {
  220. return str.replace(/&/g, '&amp;')
  221. .replace(/</g, '&lt;')
  222. .replace(/>/g, '&gt;')
  223. .replace(/"/g, '&quot;')
  224. .replace(/'/g, '&apos;')
  225. ;
  226. }
  227. export function keyEventToCode(event) {
  228. let result = [];
  229. let code = event.code;
  230. const modCode = code.substring(0, 3);
  231. if (event.metaKey && modCode != 'Met')
  232. result.push('Meta');
  233. if (event.ctrlKey && modCode != 'Con')
  234. result.push('Ctrl');
  235. if (event.shiftKey && modCode != 'Shi')
  236. result.push('Shift');
  237. if (event.altKey && modCode != 'Alt')
  238. result.push('Alt');
  239. if (modCode == 'Dig') {
  240. code = code.substring(5, 6);
  241. } else if (modCode == 'Key') {
  242. code = code.substring(3, 4);
  243. }
  244. result.push(code);
  245. return result.join('+');
  246. }
  247. export function userHotKeysObjectSwap(userHotKeys) {
  248. let result = {};
  249. for (const [name, codes] of Object.entries(userHotKeys)) {
  250. for (const code of codes) {
  251. result[code] = name;
  252. }
  253. }
  254. return result;
  255. }
  256. export function removeHtmlTags(s) {
  257. return s.replace(/(<([^>]+)>)/ig, '');
  258. }
  259. export function makeValidFilename(filename, repl = '_') {
  260. let f = filename.replace(/[\x00\\/:*"<>|]/g, repl); // eslint-disable-line no-control-regex
  261. f = f.trim();
  262. while (f.length && (f[f.length - 1] == '.' || f[f.length - 1] == '_')) {
  263. f = f.substring(0, f.length - 1);
  264. }
  265. if (f)
  266. return f;
  267. else
  268. throw new Error('Invalid filename');
  269. }
  270. export function getBookTitle(fb2) {
  271. fb2 = (fb2 ? fb2 : {});
  272. const result = {};
  273. if (fb2.author) {
  274. const authorNames = fb2.author.map(a => _.compact([
  275. a.lastName,
  276. a.firstName,
  277. a.middleName
  278. ]).join(' '));
  279. result.author = authorNames.join(', ');
  280. }
  281. if (fb2.sequence) {
  282. const seqs = fb2.sequence.map(s => _.compact([
  283. s.name,
  284. (s.number ? `#${s.number}` : null),
  285. ]).join(' '));
  286. result.sequence = seqs.join(', ');
  287. if (result.sequence)
  288. result.sequenceTitle = `(${result.sequence})`;
  289. }
  290. result.bookTitle = _.compact([result.sequenceTitle, fb2.bookTitle]).join(' ');
  291. result.fullTitle = _.compact([
  292. result.author,
  293. result.bookTitle
  294. ]).join(' - ');
  295. return result;
  296. }