@jbrowse/plugin-grid-bookmark 2.6.2 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/GridBookmarkWidget/components/AssemblySelector.d.ts +3 -4
  2. package/dist/GridBookmarkWidget/components/AssemblySelector.js +27 -37
  3. package/dist/GridBookmarkWidget/components/BookmarkGrid.d.ts +6 -0
  4. package/dist/GridBookmarkWidget/components/BookmarkGrid.js +120 -0
  5. package/dist/GridBookmarkWidget/components/{ClearBookmarks.d.ts → DeleteBookmarks.d.ts} +2 -3
  6. package/dist/GridBookmarkWidget/components/DeleteBookmarks.js +41 -0
  7. package/dist/GridBookmarkWidget/components/DeleteBookmarksDialog.d.ts +7 -0
  8. package/dist/GridBookmarkWidget/components/DeleteBookmarksDialog.js +29 -0
  9. package/dist/GridBookmarkWidget/components/EditBookmarkLabelDialog.d.ts +8 -0
  10. package/dist/GridBookmarkWidget/components/EditBookmarkLabelDialog.js +50 -0
  11. package/dist/GridBookmarkWidget/components/ExportBookmarks.d.ts +6 -0
  12. package/dist/GridBookmarkWidget/components/{ClearBookmarks.js → ExportBookmarks.js} +9 -19
  13. package/dist/GridBookmarkWidget/components/ExportBookmarksDialog.d.ts +7 -0
  14. package/dist/GridBookmarkWidget/components/{DownloadBookmarks.js → ExportBookmarksDialog.js} +22 -20
  15. package/dist/GridBookmarkWidget/components/GridBookmarkWidget.d.ts +3 -4
  16. package/dist/GridBookmarkWidget/components/GridBookmarkWidget.js +28 -97
  17. package/dist/GridBookmarkWidget/components/ImportBookmarks.d.ts +3 -5
  18. package/dist/GridBookmarkWidget/components/ImportBookmarks.js +8 -64
  19. package/dist/GridBookmarkWidget/components/ImportBookmarksDialog.d.ts +7 -0
  20. package/dist/GridBookmarkWidget/components/ImportBookmarksDialog.js +129 -0
  21. package/{esm/GridBookmarkWidget/components/ClearBookmarks.d.ts → dist/GridBookmarkWidget/components/ShareBookmarks.d.ts} +2 -3
  22. package/dist/GridBookmarkWidget/components/ShareBookmarks.js +37 -0
  23. package/dist/GridBookmarkWidget/components/ShareBookmarksDialog.d.ts +7 -0
  24. package/dist/GridBookmarkWidget/components/ShareBookmarksDialog.js +106 -0
  25. package/dist/GridBookmarkWidget/model.d.ts +178 -11
  26. package/dist/GridBookmarkWidget/model.js +105 -27
  27. package/dist/GridBookmarkWidget/sessionSharing.d.ts +6 -0
  28. package/dist/GridBookmarkWidget/sessionSharing.js +96 -0
  29. package/dist/GridBookmarkWidget/utils.d.ts +19 -3
  30. package/dist/GridBookmarkWidget/utils.js +132 -40
  31. package/dist/index.d.ts +1 -1
  32. package/dist/index.js +100 -75
  33. package/esm/GridBookmarkWidget/components/AssemblySelector.d.ts +3 -4
  34. package/esm/GridBookmarkWidget/components/AssemblySelector.js +28 -38
  35. package/esm/GridBookmarkWidget/components/BookmarkGrid.d.ts +6 -0
  36. package/esm/GridBookmarkWidget/components/BookmarkGrid.js +92 -0
  37. package/{dist/GridBookmarkWidget/components/DownloadBookmarks.d.ts → esm/GridBookmarkWidget/components/DeleteBookmarks.d.ts} +2 -3
  38. package/esm/GridBookmarkWidget/components/DeleteBookmarks.js +13 -0
  39. package/esm/GridBookmarkWidget/components/DeleteBookmarksDialog.d.ts +7 -0
  40. package/esm/GridBookmarkWidget/components/DeleteBookmarksDialog.js +24 -0
  41. package/esm/GridBookmarkWidget/components/EditBookmarkLabelDialog.d.ts +8 -0
  42. package/esm/GridBookmarkWidget/components/EditBookmarkLabelDialog.js +25 -0
  43. package/esm/GridBookmarkWidget/components/ExportBookmarks.d.ts +6 -0
  44. package/esm/GridBookmarkWidget/components/ExportBookmarks.js +13 -0
  45. package/esm/GridBookmarkWidget/components/ExportBookmarksDialog.d.ts +7 -0
  46. package/esm/GridBookmarkWidget/components/ExportBookmarksDialog.js +35 -0
  47. package/esm/GridBookmarkWidget/components/GridBookmarkWidget.d.ts +3 -4
  48. package/esm/GridBookmarkWidget/components/GridBookmarkWidget.js +28 -74
  49. package/esm/GridBookmarkWidget/components/ImportBookmarks.d.ts +3 -5
  50. package/esm/GridBookmarkWidget/components/ImportBookmarks.js +10 -66
  51. package/esm/GridBookmarkWidget/components/ImportBookmarksDialog.d.ts +7 -0
  52. package/esm/GridBookmarkWidget/components/ImportBookmarksDialog.js +101 -0
  53. package/esm/GridBookmarkWidget/components/{DownloadBookmarks.d.ts → ShareBookmarks.d.ts} +2 -3
  54. package/esm/GridBookmarkWidget/components/ShareBookmarks.js +12 -0
  55. package/esm/GridBookmarkWidget/components/ShareBookmarksDialog.d.ts +7 -0
  56. package/esm/GridBookmarkWidget/components/ShareBookmarksDialog.js +78 -0
  57. package/esm/GridBookmarkWidget/model.d.ts +178 -11
  58. package/esm/GridBookmarkWidget/model.js +106 -28
  59. package/esm/GridBookmarkWidget/sessionSharing.d.ts +6 -0
  60. package/esm/GridBookmarkWidget/sessionSharing.js +68 -0
  61. package/esm/GridBookmarkWidget/utils.d.ts +19 -3
  62. package/esm/GridBookmarkWidget/utils.js +105 -39
  63. package/esm/index.d.ts +1 -1
  64. package/esm/index.js +101 -76
  65. package/package.json +4 -3
  66. package/dist/GridBookmarkWidget/components/DeleteBookmark.d.ts +0 -9
  67. package/dist/GridBookmarkWidget/components/DeleteBookmark.js +0 -31
  68. package/esm/GridBookmarkWidget/components/ClearBookmarks.js +0 -23
  69. package/esm/GridBookmarkWidget/components/DeleteBookmark.d.ts +0 -9
  70. package/esm/GridBookmarkWidget/components/DeleteBookmark.js +0 -26
  71. package/esm/GridBookmarkWidget/components/DownloadBookmarks.js +0 -33
@@ -2,55 +2,133 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const mobx_state_tree_1 = require("mobx-state-tree");
4
4
  const mst_1 = require("@jbrowse/core/util/types/mst");
5
+ const util_1 = require("@jbrowse/core/util");
6
+ const mobx_1 = require("mobx");
5
7
  const LabeledRegionModel = mobx_state_tree_1.types
6
- .compose(mst_1.Region, mobx_state_tree_1.types.model('Label', { label: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.string, '') }))
8
+ .compose(mst_1.Region, mobx_state_tree_1.types.model('Label', {
9
+ label: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.string, ''),
10
+ }))
7
11
  .actions(self => ({
8
12
  setLabel(label) {
9
13
  self.label = label;
10
14
  },
11
15
  }));
12
- function f(pluginManager) {
16
+ const SharedBookmarksModel = mobx_state_tree_1.types.model('SharedBookmarksModel', {
17
+ sharedBookmarks: mobx_state_tree_1.types.maybe(mobx_state_tree_1.types.array(LabeledRegionModel)),
18
+ });
19
+ const localStorageKeyF = () => typeof window !== undefined
20
+ ? `bookmarks-${[window.location.host + window.location.pathname].join('-')}`
21
+ : 'empty';
22
+ function f(_pluginManager) {
13
23
  return mobx_state_tree_1.types
14
24
  .model('GridBookmarkModel', {
25
+ /**
26
+ * #property
27
+ */
15
28
  id: mst_1.ElementId,
29
+ /**
30
+ * #property
31
+ */
16
32
  type: mobx_state_tree_1.types.literal('GridBookmarkWidget'),
17
- view: mobx_state_tree_1.types.safeReference(pluginManager.pluggableMstType('view', 'stateModel')),
18
- bookmarkedRegions: mobx_state_tree_1.types.array(LabeledRegionModel),
19
- modelSelectedAssembly: '',
33
+ /**
34
+ * #property
35
+ * removed by postProcessSnapshot, only loaded from localStorage
36
+ */
37
+ bookmarks: mobx_state_tree_1.types.optional(mobx_state_tree_1.types.array(LabeledRegionModel), () => JSON.parse((0, util_1.localStorageGetItem)(localStorageKeyF()) || '[]')),
20
38
  })
39
+ .volatile(() => ({
40
+ selectedBookmarks: [],
41
+ selectedAssembliesPre: undefined,
42
+ }))
43
+ .views(self => ({
44
+ get bookmarkAssemblies() {
45
+ return [...new Set(self.bookmarks.map(r => r.assemblyName))];
46
+ },
47
+ get validAssemblies() {
48
+ const { assemblyManager } = (0, util_1.getSession)(self);
49
+ return new Set(this.bookmarkAssemblies.filter(a => assemblyManager.get(a)));
50
+ },
51
+ }))
52
+ .views(self => ({
53
+ get bookmarksWithValidAssemblies() {
54
+ return self.bookmarks.filter(e => self.validAssemblies.has(e.assemblyName));
55
+ },
56
+ }))
57
+ .views(self => ({
58
+ get sharedBookmarksModel() {
59
+ // requires cloning bookmarks with JSON.stringify/parse to avoid duplicate
60
+ // reference to same object in the same state tree, will otherwise error
61
+ // when performing share
62
+ return SharedBookmarksModel.create({
63
+ sharedBookmarks: JSON.parse(JSON.stringify(self.selectedBookmarks)),
64
+ });
65
+ },
66
+ get allBookmarksModel() {
67
+ // requires cloning bookmarks with JSON.stringify/parse to avoid duplicate
68
+ // reference to same object in the same state tree, will otherwise error
69
+ // when performing share
70
+ return SharedBookmarksModel.create({
71
+ sharedBookmarks: JSON.parse(JSON.stringify(self.bookmarksWithValidAssemblies)),
72
+ });
73
+ },
74
+ }))
75
+ .actions(self => ({
76
+ setSelectedAssemblies(assemblies) {
77
+ self.selectedAssembliesPre = assemblies;
78
+ },
79
+ }))
80
+ .views(self => ({
81
+ get selectedAssemblies() {
82
+ var _a, _b;
83
+ return ((_b = (_a = self.selectedAssembliesPre) === null || _a === void 0 ? void 0 : _a.filter(f => self.validAssemblies.has(f))) !== null && _b !== void 0 ? _b : [...self.validAssemblies]);
84
+ },
85
+ }))
21
86
  .actions(self => ({
22
87
  importBookmarks(regions) {
23
- self.bookmarkedRegions = (0, mobx_state_tree_1.cast)([...self.bookmarkedRegions, ...regions]);
88
+ self.bookmarks = (0, mobx_state_tree_1.cast)([...self.bookmarks, ...regions]);
24
89
  },
25
90
  addBookmark(region) {
26
- self.bookmarkedRegions.push(region);
91
+ self.bookmarks.push(region);
27
92
  },
28
93
  removeBookmark(index) {
29
- self.bookmarkedRegions.splice(index, 1);
94
+ self.bookmarks.splice(index, 1);
95
+ },
96
+ updateBookmarkLabel(bookmark, label) {
97
+ bookmark.correspondingObj.setLabel(label);
98
+ },
99
+ setSelectedBookmarks(bookmarks) {
100
+ self.selectedBookmarks = bookmarks;
30
101
  },
102
+ setBookmarkedRegions(regions) {
103
+ self.bookmarks = (0, mobx_state_tree_1.cast)(regions);
104
+ },
105
+ }))
106
+ .actions(self => ({
31
107
  clearAllBookmarks() {
32
- self.bookmarkedRegions.clear();
108
+ for (const bookmark of self.bookmarks) {
109
+ if (self.validAssemblies.has(bookmark.assemblyName)) {
110
+ self.bookmarks.remove(bookmark);
111
+ }
112
+ }
33
113
  },
34
- updateBookmarkLabel(index, label) {
35
- var _a;
36
- (_a = self.bookmarkedRegions[index]) === null || _a === void 0 ? void 0 : _a.setLabel(label);
114
+ clearSelectedBookmarks() {
115
+ for (const bookmark of self.selectedBookmarks) {
116
+ self.bookmarks.remove(bookmark.correspondingObj);
117
+ }
118
+ self.selectedBookmarks = [];
37
119
  },
38
- setSelectedAssembly(assembly) {
39
- self.modelSelectedAssembly = assembly;
120
+ }))
121
+ .actions(self => ({
122
+ afterAttach() {
123
+ const key = localStorageKeyF();
124
+ (0, mobx_state_tree_1.addDisposer)(self, (0, mobx_1.autorun)(() => {
125
+ (0, util_1.localStorageSetItem)(key, JSON.stringify(self.bookmarks));
126
+ }));
40
127
  },
41
128
  }))
42
- .views(self => ({
43
- get selectedAssembly() {
44
- return (self.modelSelectedAssembly ||
45
- (self.bookmarkedRegions.length
46
- ? self.bookmarkedRegions[0].assemblyName
47
- : ''));
48
- },
49
- get assemblies() {
50
- return [
51
- ...new Set(self.bookmarkedRegions.map(region => region.assemblyName)),
52
- ];
53
- },
54
- }));
129
+ .postProcessSnapshot(snap => {
130
+ const { bookmarks: _, ...rest } = snap;
131
+ return rest;
132
+ });
55
133
  }
56
134
  exports.default = f;
@@ -0,0 +1,6 @@
1
+ export declare function shareSessionToDynamo(session: unknown, url: string, referer: string): Promise<{
2
+ json: any;
3
+ encryptedSession: string;
4
+ password: string;
5
+ }>;
6
+ export declare function readSessionFromDynamo(baseUrl: string, sessionQueryParam: string, password: string, signal?: AbortSignal): Promise<string>;
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.readSessionFromDynamo = exports.shareSessionToDynamo = void 0;
27
+ // duplicated from products/jbrowse-web/src/sessionSharing.ts ; could possibly be moved into a higher directory and shared between the two
28
+ const utils_1 = require("./utils");
29
+ // from https://stackoverflow.com/questions/1349404/
30
+ function generateUID(length) {
31
+ return window
32
+ .btoa([...window.crypto.getRandomValues(new Uint8Array(length * 2))]
33
+ .map(b => String.fromCharCode(b))
34
+ .join(''))
35
+ .replaceAll(/[+/]/g, '')
36
+ .slice(0, length);
37
+ }
38
+ const encrypt = async (text, password) => {
39
+ const AES = await Promise.resolve().then(() => __importStar(require('crypto-js/aes')));
40
+ return AES.encrypt(text, password).toString();
41
+ };
42
+ const decrypt = async (text, password) => {
43
+ const AES = await Promise.resolve().then(() => __importStar(require('crypto-js/aes')));
44
+ const Utf8 = await Promise.resolve().then(() => __importStar(require('crypto-js/enc-utf8')));
45
+ const bytes = AES.decrypt(text, password);
46
+ return bytes.toString(Utf8);
47
+ };
48
+ function getErrorMsg(err) {
49
+ try {
50
+ const obj = JSON.parse(err);
51
+ return obj.message;
52
+ }
53
+ catch (e) {
54
+ return err;
55
+ }
56
+ }
57
+ // writes the encrypted session, current datetime, and referer to DynamoDB
58
+ async function shareSessionToDynamo(session, url, referer) {
59
+ const sess = await (0, utils_1.toUrlSafeB64)(JSON.stringify(session));
60
+ const password = generateUID(5);
61
+ const encryptedSession = await encrypt(sess, password);
62
+ const data = new FormData();
63
+ data.append('session', encryptedSession);
64
+ data.append('dateShared', `${Date.now()}`);
65
+ data.append('referer', referer);
66
+ const response = await fetch(`${url}share`, {
67
+ method: 'POST',
68
+ mode: 'cors',
69
+ body: data,
70
+ });
71
+ if (!response.ok) {
72
+ const err = await response.text();
73
+ throw new Error(getErrorMsg(err));
74
+ }
75
+ const json = await response.json();
76
+ return {
77
+ json,
78
+ encryptedSession,
79
+ password,
80
+ };
81
+ }
82
+ exports.shareSessionToDynamo = shareSessionToDynamo;
83
+ async function readSessionFromDynamo(baseUrl, sessionQueryParam, password, signal) {
84
+ const sessionId = sessionQueryParam.split('share-')[1];
85
+ const url = `${baseUrl}?sessionId=${encodeURIComponent(sessionId)}`;
86
+ const response = await fetch(url, {
87
+ signal,
88
+ });
89
+ if (!response.ok) {
90
+ const err = await response.text();
91
+ throw new Error(getErrorMsg(err));
92
+ }
93
+ const json = await response.json();
94
+ return decrypt(json.session, password);
95
+ }
96
+ exports.readSessionFromDynamo = readSessionFromDynamo;
@@ -1,5 +1,21 @@
1
1
  import { AbstractViewModel } from '@jbrowse/core/util/types';
2
2
  import { GridBookmarkModel } from './model';
3
- import { LabeledRegion } from './types';
4
- export declare function navToBookmark(locString: string, views: AbstractViewModel[], model: GridBookmarkModel): Promise<void>;
5
- export declare function downloadBookmarkFile(bookmarkedRegions: LabeledRegion[], fileFormat: string, model: GridBookmarkModel): void;
3
+ export declare function navToBookmark(locString: string, assembly: string, views: AbstractViewModel[], model: GridBookmarkModel): Promise<void>;
4
+ export declare function downloadBookmarkFile(fileFormat: string, model: GridBookmarkModel): void;
5
+ /**
6
+ * Pad the end of a base64 string with "=" to make it valid
7
+ * @param b64 - unpadded b64 string
8
+ */
9
+ export declare function b64PadSuffix(b64: string): string;
10
+ /**
11
+ * Decode and inflate a url-safe base64 to a string
12
+ * See {@link https://en.wikipedia.org/wiki/Base64#URL_applications}
13
+ * @param b64 - a base64 string to decode and inflate
14
+ */
15
+ export declare function fromUrlSafeB64(b64: string): Promise<string>;
16
+ /**
17
+ * Compress and encode a string as url-safe base64
18
+ * See {@link https://en.wikipedia.org/wiki/Base64#URL_applications}
19
+ * @param str- a string to compress and encode
20
+ */
21
+ export declare function toUrlSafeB64(str: string): Promise<string>;
@@ -1,25 +1,51 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.downloadBookmarkFile = exports.navToBookmark = void 0;
26
+ exports.toUrlSafeB64 = exports.fromUrlSafeB64 = exports.b64PadSuffix = exports.downloadBookmarkFile = exports.navToBookmark = void 0;
4
27
  const file_saver_1 = require("file-saver");
5
28
  const util_1 = require("@jbrowse/core/util");
6
- async function navToBookmark(locString, views, model) {
29
+ async function navToBookmark(locString, assembly, views, model) {
7
30
  const session = (0, util_1.getSession)(model);
8
31
  try {
9
- // search for exact match to an lgv that this bookmark widget launched, or
10
- // any lgv that looks like it is relevant to what we are browsing
11
- const { selectedAssembly } = model;
12
- const newViewId = `${model.id}_${selectedAssembly}`;
13
- let view = (views.find(v => v.type === 'LinearGenomeView' && v.id === newViewId) ||
14
- views.find(v => v.type === 'LinearGenomeView' &&
15
- // @ts-expect-error
16
- v.assemblyNames[0] === selectedAssembly));
32
+ // get the focused view
33
+ let view = views.find(view => view.id === session.focusedViewId);
34
+ // check if the focused view is the appropriate assembly, if not proceed
35
+ if ((view === null || view === void 0 ? void 0 : view.assemblyNames[0]) !== assembly) {
36
+ view = views.find(elt =>
37
+ // @ts-expect-error
38
+ elt.type === 'LinearGenomeView' && elt.assemblyNames[0] === assembly);
39
+ }
40
+ // if no view is opened of the selectedAssembly, open a new
41
+ // view with that assembly
17
42
  if (!view) {
43
+ const newViewId = `${model.id}_${assembly}`;
18
44
  view = session.addView('LinearGenomeView', {
19
45
  id: newViewId,
20
46
  });
21
47
  }
22
- await view.navToLocString(locString, selectedAssembly);
48
+ await view.navToLocString(locString, assembly);
23
49
  }
24
50
  catch (e) {
25
51
  console.error(e);
@@ -27,35 +53,101 @@ async function navToBookmark(locString, views, model) {
27
53
  }
28
54
  }
29
55
  exports.navToBookmark = navToBookmark;
30
- function downloadBookmarkFile(bookmarkedRegions, fileFormat, model) {
31
- const { selectedAssembly } = model;
32
- const fileHeader = fileFormat === 'TSV'
33
- ? 'chrom\tstart\tend\tlabel\tassembly_name\tcoord_range\n'
34
- : '';
35
- const fileContents = bookmarkedRegions
36
- .map(b => {
37
- const { label } = b;
38
- const labelVal = label === '' ? '.' : label;
39
- const locString = (0, util_1.assembleLocString)(b);
40
- if (fileFormat === 'BED') {
41
- if (b.assemblyName === selectedAssembly || selectedAssembly === 'all') {
42
- return `${b.refName}\t${b.start}\t${b.end}\t${labelVal}\n`;
43
- }
44
- return '';
45
- }
46
- else {
47
- return `${b.refName}\t${b.start + 1}\t${b.end}\t${labelVal}\t${b.assemblyName}\t${locString}\n`;
56
+ function downloadBookmarkFile(fileFormat, model) {
57
+ const { selectedBookmarks, bookmarksWithValidAssemblies } = model;
58
+ const bookmarksToDownload = selectedBookmarks.length === 0
59
+ ? bookmarksWithValidAssemblies
60
+ : selectedBookmarks;
61
+ if (fileFormat === 'BED') {
62
+ const fileHeader = '';
63
+ const fileContents = {};
64
+ bookmarksToDownload.forEach(bookmark => {
65
+ const { label } = bookmark;
66
+ const labelVal = label === '' ? '.' : label;
67
+ const line = `${bookmark.refName}\t${bookmark.start}\t${bookmark.end}\t${labelVal}\n`;
68
+ fileContents[bookmark.assemblyName]
69
+ ? fileContents[bookmark.assemblyName].push(line)
70
+ : (fileContents[bookmark.assemblyName] = [line]);
71
+ });
72
+ for (const assembly in fileContents) {
73
+ const fileContent = fileContents[assembly].reduce((a, b) => a + b, fileHeader);
74
+ const blob = new Blob([fileContent || ''], {
75
+ type: 'text/x-bed;charset=utf-8',
76
+ });
77
+ const fileName = `jbrowse_bookmarks_${assembly}.bed`;
78
+ (0, file_saver_1.saveAs)(blob, fileName);
48
79
  }
49
- })
50
- .reduce((a, b) => a + b, fileHeader);
51
- const blob = new Blob([fileContents || ''], {
52
- type: fileFormat === 'BED'
53
- ? 'text/x-bed;charset=utf-8'
54
- : 'text/tab-separated-values;charset=utf-8',
55
- });
56
- const fileName = fileFormat === 'BED'
57
- ? `jbrowse_bookmarks_${selectedAssembly}.bed`
58
- : 'jbrowse_bookmarks.tsv';
59
- (0, file_saver_1.saveAs)(blob, fileName);
80
+ }
81
+ else {
82
+ // TSV
83
+ const fileHeader = 'chrom\tstart\tend\tlabel\tassembly_name\tcoord_range\n';
84
+ const fileContents = bookmarksToDownload
85
+ .map(bookmark => {
86
+ const { label } = bookmark;
87
+ const labelVal = label === '' ? '.' : label;
88
+ const locString = (0, util_1.assembleLocString)(bookmark);
89
+ return `${bookmark.refName}\t${bookmark.start + 1}\t${bookmark.end}\t${labelVal}\t${bookmark.assemblyName}\t${locString}\n`;
90
+ })
91
+ .reduce((a, b) => a + b, fileHeader);
92
+ const blob = new Blob([fileContents || ''], {
93
+ type: 'text/tab-separated-values;charset=utf-8',
94
+ });
95
+ const fileName = 'jbrowse_bookmarks.tsv';
96
+ (0, file_saver_1.saveAs)(blob, fileName);
97
+ }
60
98
  }
61
99
  exports.downloadBookmarkFile = downloadBookmarkFile;
100
+ /**
101
+ * Pad the end of a base64 string with "=" to make it valid
102
+ * @param b64 - unpadded b64 string
103
+ */
104
+ function b64PadSuffix(b64) {
105
+ let num = 0;
106
+ const mo = b64.length % 4;
107
+ switch (mo) {
108
+ case 3:
109
+ num = 1;
110
+ break;
111
+ case 2:
112
+ num = 2;
113
+ break;
114
+ case 0:
115
+ num = 0;
116
+ break;
117
+ default:
118
+ throw new Error('base64 not a valid length');
119
+ }
120
+ return b64 + '='.repeat(num);
121
+ }
122
+ exports.b64PadSuffix = b64PadSuffix;
123
+ /**
124
+ * Decode and inflate a url-safe base64 to a string
125
+ * See {@link https://en.wikipedia.org/wiki/Base64#URL_applications}
126
+ * @param b64 - a base64 string to decode and inflate
127
+ */
128
+ async function fromUrlSafeB64(b64) {
129
+ const originalB64 = b64PadSuffix(b64.replaceAll('-', '+').replaceAll('_', '/'));
130
+ const { toByteArray } = await Promise.resolve().then(() => __importStar(require('base64-js')));
131
+ const { inflate } = await Promise.resolve().then(() => __importStar(require('pako')));
132
+ const bytes = toByteArray(originalB64);
133
+ const inflated = inflate(bytes);
134
+ return new TextDecoder().decode(inflated);
135
+ }
136
+ exports.fromUrlSafeB64 = fromUrlSafeB64;
137
+ /**
138
+ * Compress and encode a string as url-safe base64
139
+ * See {@link https://en.wikipedia.org/wiki/Base64#URL_applications}
140
+ * @param str- a string to compress and encode
141
+ */
142
+ async function toUrlSafeB64(str) {
143
+ const bytes = new TextEncoder().encode(str);
144
+ const { deflate } = await Promise.resolve().then(() => __importStar(require('pako')));
145
+ const { fromByteArray } = await Promise.resolve().then(() => __importStar(require('base64-js')));
146
+ const deflated = deflate(bytes);
147
+ const encoded = fromByteArray(deflated);
148
+ const pos = encoded.indexOf('=');
149
+ return pos > 0
150
+ ? encoded.slice(0, pos).replaceAll('+', '-').replaceAll('/', '_')
151
+ : encoded.replaceAll('+', '-').replaceAll('/', '_');
152
+ }
153
+ exports.toUrlSafeB64 = toUrlSafeB64;
package/dist/index.d.ts CHANGED
@@ -3,5 +3,5 @@ import PluginManager from '@jbrowse/core/PluginManager';
3
3
  export default class extends Plugin {
4
4
  name: string;
5
5
  install(pluginManager: PluginManager): void;
6
- configure(_pluginManager: PluginManager): void;
6
+ configure(pluginManager: PluginManager): void;
7
7
  }