@internetarchive/bookreader 5.0.0-48-alpha2 → 5.0.0-48
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.
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +89 -89
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReaderDemo/IADemoBr.js +2 -0
- package/CHANGELOG.md +4 -0
- package/package.json +1 -1
- package/src/BookNavigator/book-navigator.js +2 -2
- package/src/BookNavigator/downloads/downloads-provider.js +42 -10
- package/src/BookNavigator/downloads/downloads.js +47 -83
- package/src/BookNavigator/volumes/volumes-provider.js +1 -1
- package/src/util/manifestGenerator.js +0 -0
- package/tests/jest/BookNavigator/book-navigator.test.js +19 -15
|
@@ -82,6 +82,8 @@ const initializeBookReader = (brManifest) => {
|
|
|
82
82
|
// we expect this at the global level
|
|
83
83
|
BookReaderJSIAinit(brManifest.data, options);
|
|
84
84
|
|
|
85
|
+
const isRestricted = brManifest.data.isRestricted;
|
|
86
|
+
window.dispatchEvent(new CustomEvent('contextmenu', { detail: { isRestricted } }));
|
|
85
87
|
if (customAutoflipParams.autoflip) {
|
|
86
88
|
br.autoToggle(customAutoflipParams);
|
|
87
89
|
}
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
|
@@ -449,10 +449,10 @@ export class BookNavigator extends LitElement {
|
|
|
449
449
|
/** Display an element's context menu */
|
|
450
450
|
manageContextMenuVisibility(e) {
|
|
451
451
|
if (window.archive_analytics) {
|
|
452
|
-
window.archive_analytics?.
|
|
452
|
+
window.archive_analytics?.send_event(
|
|
453
453
|
'BookReader',
|
|
454
454
|
`contextmenu-${this.bookIsRestricted ? 'restricted' : 'unrestricted'}`,
|
|
455
|
-
e.target
|
|
455
|
+
e.target?.classList?.value
|
|
456
456
|
);
|
|
457
457
|
}
|
|
458
458
|
if (!this.bookIsRestricted) {
|
|
@@ -2,16 +2,46 @@ import { html } from 'lit';
|
|
|
2
2
|
import '@internetarchive/icon-dl/icon-dl';
|
|
3
3
|
import './downloads';
|
|
4
4
|
|
|
5
|
+
const menuBase = {
|
|
6
|
+
pdf: {
|
|
7
|
+
type: 'Encrypted Adobe PDF',
|
|
8
|
+
url: '#',
|
|
9
|
+
note: 'PDF files contain high quality images of pages.',
|
|
10
|
+
},
|
|
11
|
+
lcppdf: {
|
|
12
|
+
type: 'Get LCP PDF',
|
|
13
|
+
url: '#',
|
|
14
|
+
note: 'PDF files contain high quality images of pages.',
|
|
15
|
+
},
|
|
16
|
+
lcpepub: {
|
|
17
|
+
type: 'Get LCP ePub',
|
|
18
|
+
url: '#',
|
|
19
|
+
note: 'ePub files are smaller in size, but may contain errors.',
|
|
20
|
+
},
|
|
21
|
+
epub: {
|
|
22
|
+
type: 'Encrypted Adobe ePub',
|
|
23
|
+
url: '#',
|
|
24
|
+
note: 'ePub files are smaller in size, but may contain errors.',
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const publicMenuBase = {
|
|
29
|
+
pdf: "PDF",
|
|
30
|
+
epub: "ePub",
|
|
31
|
+
lcppdf: "LCP PDF",
|
|
32
|
+
lcpepub: "LCP ePub",
|
|
33
|
+
};
|
|
34
|
+
|
|
5
35
|
export default class DownloadsProvider {
|
|
6
36
|
|
|
7
37
|
constructor({ bookreader }) {
|
|
8
38
|
this.icon = html`<ia-icon-dl style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-dl>`;
|
|
9
|
-
this.label = '
|
|
39
|
+
this.label = 'Downloadable files';
|
|
10
40
|
this.menuDetails = '';
|
|
11
41
|
this.downloads = [];
|
|
12
42
|
this.id = 'downloads';
|
|
13
43
|
this.component = '';
|
|
14
|
-
this.isBookProtected = bookreader?.options?.
|
|
44
|
+
this.isBookProtected = bookreader?.options?.isProtected || false;
|
|
15
45
|
}
|
|
16
46
|
|
|
17
47
|
update(downloadTypes) {
|
|
@@ -24,21 +54,23 @@ export default class DownloadsProvider {
|
|
|
24
54
|
}
|
|
25
55
|
|
|
26
56
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
57
|
+
* Generates Download Menu Info for available types
|
|
58
|
+
* sets global `downloads`
|
|
29
59
|
* @param availableTypes
|
|
30
60
|
*/
|
|
31
61
|
computeAvailableTypes(availableTypes = []) {
|
|
32
62
|
const menuData = availableTypes.reduce((found, incoming = []) => {
|
|
33
63
|
const [ type = '', link = '' ] = incoming;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
64
|
+
const formattedType = type.toLowerCase();
|
|
65
|
+
const downloadOption = menuBase[formattedType] || null;
|
|
66
|
+
|
|
67
|
+
if (downloadOption) {
|
|
68
|
+
const menuButtonText = this.isBookProtected ? menuBase[formattedType].type : publicMenuBase[formattedType];
|
|
69
|
+
const menuInfo = Object.assign({}, downloadOption, { url: link, type: menuButtonText});
|
|
70
|
+
found.push(menuInfo);
|
|
38
71
|
}
|
|
39
|
-
found[formattedType] = link;
|
|
40
72
|
return found;
|
|
41
|
-
},
|
|
73
|
+
}, []);
|
|
42
74
|
|
|
43
75
|
this.downloads = menuData;
|
|
44
76
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { css, html, LitElement, nothing } from 'lit';
|
|
2
2
|
import buttonStyles from '../assets/button-base.js';
|
|
3
|
-
|
|
4
3
|
export class IABookDownloads extends LitElement {
|
|
5
4
|
static get properties() {
|
|
6
5
|
return {
|
|
@@ -19,26 +18,6 @@ export class IABookDownloads extends LitElement {
|
|
|
19
18
|
this.isBookProtected = false;
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
render() {
|
|
23
|
-
return html`
|
|
24
|
-
${this.header}
|
|
25
|
-
${this.loanExpiryMessage}
|
|
26
|
-
${this.renderDownloadOptions()}
|
|
27
|
-
`;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
get header() {
|
|
31
|
-
if (!this.renderHeader) {
|
|
32
|
-
return nothing;
|
|
33
|
-
}
|
|
34
|
-
return html`
|
|
35
|
-
<header>
|
|
36
|
-
<h3>Read offline</h3>
|
|
37
|
-
${this.formatsCount}
|
|
38
|
-
</header>
|
|
39
|
-
`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
21
|
get formatsCount() {
|
|
43
22
|
const count = this.downloads.length;
|
|
44
23
|
return count ? html`<p>${count} format${count > 1 ? 's' : ''}</p>` : html``;
|
|
@@ -51,80 +30,65 @@ export class IABookDownloads extends LitElement {
|
|
|
51
30
|
}
|
|
52
31
|
|
|
53
32
|
renderDownloadOptions() {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (this.downloads[format]) {
|
|
57
|
-
downloadOptions.push(this.downloadOption(format, this.downloads[format]));
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
return html`
|
|
61
|
-
<ul>
|
|
62
|
-
${downloadOptions}
|
|
63
|
-
</ul>
|
|
64
|
-
`;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
downloadOption(format, link) {
|
|
68
|
-
if (/^adobe/.test(format)) {
|
|
69
|
-
return html`
|
|
33
|
+
return this.downloads.map(option => (
|
|
34
|
+
html`
|
|
70
35
|
<li>
|
|
71
|
-
<a
|
|
72
|
-
|
|
73
|
-
data-event-click-tracking="BookReader|Download-${format}"
|
|
74
|
-
>${this.menuText[format].linkText}</a>
|
|
75
|
-
${this.menuText[format].message ?? nothing}
|
|
36
|
+
<a class="ia-button link primary" href="${option.url}">Get ${option.type}</a>
|
|
37
|
+
${option.note ? html`<p>${option.note}</p>` : html``}
|
|
76
38
|
</li>
|
|
77
|
-
|
|
39
|
+
`
|
|
40
|
+
));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* checks if downloads list contains an LCP option
|
|
45
|
+
* @return {boolean}
|
|
46
|
+
*/
|
|
47
|
+
get hasLCPOption() {
|
|
48
|
+
const regex = /^(LCP)/g;
|
|
49
|
+
const lcpAvailable = this.downloads.some(option => option.type?.match(regex));
|
|
50
|
+
return lcpAvailable;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
get header() {
|
|
54
|
+
if (!this.renderHeader) {
|
|
55
|
+
return nothing;
|
|
78
56
|
}
|
|
79
57
|
return html`
|
|
80
|
-
<
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
>${this.menuText[format].linkText}</a>
|
|
85
|
-
${this.menuText[format].message ? html`<p>${this.menuText[format].message}</p>` : nothing}
|
|
86
|
-
</li>
|
|
58
|
+
<header>
|
|
59
|
+
<h3>Downloadable files</h3>
|
|
60
|
+
${this.formatsCount}
|
|
61
|
+
</header>
|
|
87
62
|
`;
|
|
88
63
|
}
|
|
89
64
|
|
|
90
|
-
get
|
|
91
|
-
return {
|
|
92
|
-
pdf: {
|
|
93
|
-
linkText: 'Get high-resolution PDF',
|
|
94
|
-
message: html``,
|
|
95
|
-
},
|
|
96
|
-
epub: {
|
|
97
|
-
linkText: 'Get text-based ebook',
|
|
98
|
-
message: html`Smaller size. May contain some errors.`,
|
|
99
|
-
},
|
|
100
|
-
lcppdf: {
|
|
101
|
-
linkText: 'Get high-resolution PDF',
|
|
102
|
-
message: this.lcpNote,
|
|
103
|
-
},
|
|
104
|
-
lcpepub: {
|
|
105
|
-
linkText: 'Get text-based ebook',
|
|
106
|
-
message: html`Smaller size. May contain some errors. ${this.lcpNote}`,
|
|
107
|
-
},
|
|
108
|
-
adobepdf: {
|
|
109
|
-
linkText:'Get legacy Adobe DRM PDF version here.',
|
|
110
|
-
message: this.adobeNote,
|
|
111
|
-
},
|
|
112
|
-
adobeepub: {
|
|
113
|
-
linkText: 'Get legacy Adobe DRM EPUB version here.',
|
|
114
|
-
message: this.adobeNote,
|
|
115
|
-
},
|
|
116
|
-
};
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
get lcpNote() {
|
|
65
|
+
get accessProtectedBook() {
|
|
120
66
|
return html`
|
|
121
|
-
|
|
67
|
+
<p>To access downloaded books, you need Adobe-compliant software on your device. The Internet Archive will administer this loan, but Adobe may also collect some information.</p>
|
|
68
|
+
<a class="ia-button external primary" href="https://www.adobe.com/solutions/ebook/digital-editions/download.html" rel="noopener noreferrer" target="_blank">Install Adobe Digital Editions</a>
|
|
122
69
|
`;
|
|
123
70
|
}
|
|
124
71
|
|
|
125
|
-
get
|
|
72
|
+
get installSimplyEAldikoThoriumMsg() {
|
|
73
|
+
return html`
|
|
74
|
+
<p>For LCP downloads, make sure you have SimplyE or Aldiko Next installed on mobile or Thorium on desktop.</p>
|
|
75
|
+
<ul>
|
|
76
|
+
<li><a href="https://librarysimplified.org/simplye/" rel="noopener noreferrer nofollow" target="_blank">Install SimplyE</a></li>
|
|
77
|
+
<li><a href="https://www.demarque.com/en-aldiko" rel="noopener noreferrer nofollow" target="_blank">Install Aldiko</a></li>
|
|
78
|
+
<li><a href="https://www.edrlab.org/software/thorium-reader/" rel="noopener noreferrer nofollow" target="_blank">Install Thorium</a></li>
|
|
79
|
+
</ul>
|
|
80
|
+
`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
render() {
|
|
126
84
|
return html`
|
|
127
|
-
|
|
85
|
+
${this.header}
|
|
86
|
+
${this.loanExpiryMessage}
|
|
87
|
+
<ul>${this.renderDownloadOptions()}</ul>
|
|
88
|
+
${this.hasLCPOption
|
|
89
|
+
? this.installSimplyEAldikoThoriumMsg
|
|
90
|
+
: (this.isBookProtected ? this.accessProtectedBook : nothing)
|
|
91
|
+
}
|
|
128
92
|
`;
|
|
129
93
|
}
|
|
130
94
|
|
|
@@ -104,7 +104,7 @@ export default class VolumesProvider {
|
|
|
104
104
|
if (!window.archive_analytics) {
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
|
-
window.archive_analytics?.
|
|
107
|
+
window.archive_analytics?.send_event(
|
|
108
108
|
'BookReader',
|
|
109
109
|
`VolumesSort|${orderBy}`,
|
|
110
110
|
window.location.path,
|
|
File without changes
|
|
@@ -46,6 +46,13 @@ window.ResizeObserver = class ResizeObserver {
|
|
|
46
46
|
disconnect = sinon.fake()
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
window.archive_analytics = {
|
|
51
|
+
send_event_no_sampling: sinon.fake(),
|
|
52
|
+
send_event: sinon.fake()
|
|
53
|
+
};
|
|
54
|
+
});
|
|
55
|
+
|
|
49
56
|
afterEach(() => {
|
|
50
57
|
window.br = null;
|
|
51
58
|
fixtureCleanup();
|
|
@@ -516,8 +523,6 @@ describe('<book-navigator>', () => {
|
|
|
516
523
|
describe('Handles Restricted Books', () => {
|
|
517
524
|
describe('contextMenu is prevented when book is restricted', () => {
|
|
518
525
|
it('watches on `div.BRscreen`', async () => {
|
|
519
|
-
window.archive_analytics = { send_event_no_sampling: sinon.fake() };
|
|
520
|
-
|
|
521
526
|
const el = fixtureSync(container());
|
|
522
527
|
const brStub = {
|
|
523
528
|
options: { restricted: true },
|
|
@@ -529,9 +534,8 @@ describe('<book-navigator>', () => {
|
|
|
529
534
|
const elSpy = sinon.spy(el.manageContextMenuVisibility);
|
|
530
535
|
await el.elementUpdated;
|
|
531
536
|
|
|
532
|
-
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
|
|
533
|
-
|
|
534
|
-
);
|
|
537
|
+
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(false);
|
|
538
|
+
expect(window.archive_analytics.send_event.called).toEqual(false);
|
|
535
539
|
expect(elSpy.called).toEqual(false);
|
|
536
540
|
|
|
537
541
|
const body = document.querySelector('body');
|
|
@@ -550,14 +554,13 @@ describe('<book-navigator>', () => {
|
|
|
550
554
|
|
|
551
555
|
// analytics fires
|
|
552
556
|
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
|
|
553
|
-
|
|
557
|
+
false
|
|
554
558
|
);
|
|
559
|
+
expect(window.archive_analytics.send_event.called).toEqual(true);
|
|
555
560
|
// we prevent default
|
|
556
561
|
expect(preventDefaultSpy.called).toEqual(true);
|
|
557
562
|
});
|
|
558
563
|
it('watches on `img.BRpageimage`', async () => {
|
|
559
|
-
window.archive_analytics = { send_event_no_sampling: sinon.fake() };
|
|
560
|
-
|
|
561
564
|
const el = fixtureSync(container());
|
|
562
565
|
const brStub = {
|
|
563
566
|
options: { restricted: true },
|
|
@@ -568,9 +571,8 @@ describe('<book-navigator>', () => {
|
|
|
568
571
|
|
|
569
572
|
await el.elementUpdated;
|
|
570
573
|
|
|
571
|
-
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
|
|
572
|
-
|
|
573
|
-
);
|
|
574
|
+
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(false);
|
|
575
|
+
expect(window.archive_analytics.send_event.called).toEqual(false);
|
|
574
576
|
|
|
575
577
|
const body = document.querySelector('body');
|
|
576
578
|
// const element stub for img.BRpageimage
|
|
@@ -586,14 +588,13 @@ describe('<book-navigator>', () => {
|
|
|
586
588
|
imgBRpageimage.dispatchEvent(contextMenuEvent);
|
|
587
589
|
|
|
588
590
|
// analytics fires
|
|
589
|
-
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
|
|
591
|
+
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(false);
|
|
592
|
+
expect(window.archive_analytics.send_event.called).toEqual(true);
|
|
590
593
|
// we prevent default
|
|
591
594
|
expect(preventDefaultSpy.called).toEqual(true);
|
|
592
595
|
});
|
|
593
596
|
});
|
|
594
597
|
it('Allows unrestricted books access to context menu', async () => {
|
|
595
|
-
window.archive_analytics = { send_event_no_sampling: sinon.fake() };
|
|
596
|
-
|
|
597
598
|
const el = fixtureSync(container());
|
|
598
599
|
const brStub = {
|
|
599
600
|
options: { restricted: false },
|
|
@@ -607,6 +608,8 @@ describe('<book-navigator>', () => {
|
|
|
607
608
|
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
|
|
608
609
|
false
|
|
609
610
|
);
|
|
611
|
+
expect(window.archive_analytics.send_event.called).toEqual(false);
|
|
612
|
+
|
|
610
613
|
|
|
611
614
|
const body = document.querySelector('body');
|
|
612
615
|
// const element stub for img.BRpageimage
|
|
@@ -622,7 +625,8 @@ describe('<book-navigator>', () => {
|
|
|
622
625
|
imgBRpageimage.dispatchEvent(contextMenuEvent);
|
|
623
626
|
|
|
624
627
|
// analytics fires
|
|
625
|
-
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(
|
|
628
|
+
expect(window.archive_analytics.send_event_no_sampling.called).toEqual(false);
|
|
629
|
+
expect(window.archive_analytics.send_event.called).toEqual(true);
|
|
626
630
|
// we do not prevent default
|
|
627
631
|
expect(preventDefaultSpy.called).toEqual(false);
|
|
628
632
|
});
|