PageSourceSearch

https://reader.exacteditions.com/assets/reader/lib/Bookmark-0a04cdbc.js

js exacteditions.com collected 2026-10-01 10:19:58 UTC 3,297 bytes, 137 lines download raw bytes

1import Store from 'reader/lib/Store';
2import User from 'reader/core/models/User';
3
4export default class Bookmark {
5  constructor(key, value) {
6    this.key = key;
7    this.value = value;
8  }
9
10  get issueId() {
11    const splitKey = this.key.split('.');
12    return Number(splitKey[splitKey.length - 1]);
13  }
14
15  /**
16   * @returns {{label: string, index: number}[]}
17   */
18  get pages() {
19    return this.value.map((page) => {
20      const newPage = { label: page.label, index: Number(page.index) };
21
22      if (page.title) {
23        newPage.title = page.title;
24      }
25
26      return newPage;
27    });
28  }
29
30  static get namespace() {
31    return [User.current().id, 'bookmark'];
32  }
33
34  static async all() {
35    const bookmarks = await Store
36      .withNamespace(this.namespace)
37      .getAll();
38
39    return bookmarks.map(([key, value]) => new Bookmark(key, value));
40  }
41
42  static async flat() {
43    return (await this.all()).map((bookmark) => (
44      bookmark.pages.map((page) => (
45        {
46          bookmark: bookmark.key,
47          issueId: bookmark.issueId,
48          page: { label: page.label, index: page.index, title: page.title },
49        }
50      ))
51    )).flat();
52  }
53
54  static async totalPages() {
55    return Math.ceil((await this.flat()).length / this.PER_PAGE);
56  }
57
58  static async paginated(currentPage = 1) {
59    const start = (currentPage - 1) * this.PER_PAGE;
60    const end = currentPage * this.PER_PAGE;
61
62    return (await this.flat()).slice(start, end);
63  }
64
65  /**
66   * Toggles a bookmark. Returns `true` if it has been created,
67   * false if it has been deleted.
68   * @returns {Promise<boolean>}
69   */
70  static async toggle(issueId, page) {
71    if (await this.isPresent(issueId, page)) {
72      await this.delete(issueId, page);
73      return false;
74    }
75
76    this.create(issueId, page);
77    return true;
78  }
79
80  static async find(keys) {
81    if (Array.isArray(keys)) {
82      return (await this.all()).filter((bookmark) => keys.includes(bookmark.key));
83    }
84
85    const bookmark = await new Store(keys).get();
86
87    if (!bookmark) return null;
88
89    return new Bookmark(keys, bookmark);
90  }
91
92  static saveAll(toSave) {
93    Store.withNamespace(this.namespace).setAll(toSave);
94  }
95
96  static create(issueId, page) {
97    if (!issueId) return;
98    if (!page || page === 'null') return;
99
100    Store
101      .withNamespace(this.namespace, issueId)
102      .concatUniq(page);
103  }
104
105  static async delete(issueId, page) {
106    if (!issueId) return;
107    if (!page || page === 'null') return;
108
109    const bookmark = await this.find(this.keyFor(issueId));
110
111    if (!bookmark) return;
112    const toSave = bookmark.value
113      .filter((bookmarkPage) => Number(bookmarkPage.index) !== Number(page.index));
114
115    Store.withNamespace(this.namespace, issueId).set(toSave);
116  }
117
118  delete(page) {
119    return Bookmark.delete(this.issueId, page);
120  }
121
122  static keyFor(issueId) {
123    return Store.withNamespace(this.namespace, issueId).key;
124  }
125
126  static async isPresent(issueId, page) {
127    if (!issueId) return false;
128    if (!page || page === 'null') return false;
129
130    return (await this.all())
131      .filter((bookmark) => bookmark.key === this.keyFor(issueId))
132      .some((bookmark) => bookmark.pages
133        .some((bookmarkPage) => Number(bookmarkPage.index) === Number(page.index)));
134  }
135}
136
137Bookmark.PER_PAGE = 28;

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.