@internetarchive/bookreader 5.0.0-66 → 5.0.0-68
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.css +68 -918
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +118 -128
- package/BookReader/ia-bookreader-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.autoplay.js +1 -1
- package/BookReader/plugins/plugin.autoplay.js.map +1 -1
- package/BookReader/plugins/plugin.chapters.js +24 -1
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReaderDemo/BookReaderDemo.css +0 -18
- package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
- package/BookReaderDemo/demo-autoplay.html +0 -2
- package/BookReaderDemo/demo-fullscreen-mobile.html +1 -4
- package/BookReaderDemo/demo-fullscreen.html +0 -3
- package/BookReaderDemo/demo-iiif.js +0 -1
- package/BookReaderDemo/demo-internetarchive.html +41 -3
- package/BookReaderDemo/demo-vendor-fullscreen.html +0 -3
- package/BookReaderDemo/immersion-1up.html +0 -1
- package/BookReaderDemo/immersion-mode.html +0 -3
- package/BookReaderDemo/toggle_controls.html +1 -1
- package/CHANGELOG.md +10 -0
- package/index.html +1 -1
- package/package.json +9 -9
- package/src/BookNavigator/book-navigator.js +4 -3
- package/src/BookReader/Mode1Up.js +1 -1
- package/src/BookReader/Mode1UpLit.js +1 -1
- package/src/BookReader/Navbar/Navbar.js +2 -2
- package/src/BookReader/events.js +0 -1
- package/src/BookReader.js +1 -2
- package/src/css/BookReader.scss +1 -5
- package/src/css/_BRnav.scss +5 -10
- package/src/css/_BRsearch.scss +6 -2
- package/src/css/_controls.scss +3 -2
- package/src/plugins/plugin.autoplay.js +1 -2
- package/src/plugins/plugin.chapters.js +201 -169
- package/src/plugins/search/plugin.search.js +0 -3
- package/src/plugins/tts/plugin.tts.js +1 -1
- package/tests/e2e/base.test.js +3 -11
- package/tests/e2e/helpers/base.js +26 -26
- package/tests/e2e/helpers/rightToLeft.js +4 -4
- package/tests/e2e/helpers/{desktopSearch.js → search.js} +19 -19
- package/tests/e2e/models/Navigation.js +16 -42
- package/tests/e2e/viewmode.test.js +3 -3
- package/tests/jest/plugins/plugin.chapters.test.js +92 -76
- package/tests/jest/plugins/search/plugin.search.view.test.js +0 -1
- package/webpack.config.js +0 -1
- package/BookReader/plugins/plugin.mobile_nav.js +0 -2
- package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
- package/src/css/_MobileNav.scss +0 -194
- package/src/plugins/plugin.mobile_nav.js +0 -288
- package/tests/e2e/helpers/mobileSearch.js +0 -85
- package/tests/jest/plugins/plugin.mobile_nav.test.js +0 -66
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
/* global BookReader */
|
|
2
|
+
import { css, html, LitElement, nothing } from "lit";
|
|
3
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
4
|
+
import { ifDefined } from 'lit/directives/if-defined.js';
|
|
5
|
+
import { styleMap } from 'lit/directives/style-map.js';
|
|
6
|
+
import '@internetarchive/icon-toc/icon-toc';
|
|
7
|
+
/** @typedef {import('@/src/BookNavigator/book-navigator.js').BookNavigator} BookNavigator */
|
|
8
|
+
|
|
2
9
|
/**
|
|
3
10
|
* Plugin for chapter markers in BookReader. Fetches from openlibrary.org
|
|
4
11
|
* Could be forked, or extended to alter behavior
|
|
@@ -10,153 +17,111 @@ jQuery.extend(BookReader.defaultOptions, {
|
|
|
10
17
|
bookId: '',
|
|
11
18
|
});
|
|
12
19
|
|
|
13
|
-
/** @override Extend the constructor to add search properties */
|
|
14
|
-
BookReader.prototype.setup = (function (super_) {
|
|
15
|
-
return function (options) {
|
|
16
|
-
super_.call(this, options);
|
|
17
|
-
|
|
18
|
-
this.olHost = options.olHost;
|
|
19
|
-
this.enableChaptersPlugin = options.enableChaptersPlugin;
|
|
20
|
-
this.bookId = options.bookId;
|
|
21
|
-
};
|
|
22
|
-
})(BookReader.prototype.setup);
|
|
23
|
-
|
|
24
20
|
/** @override Extend to call Open Library for TOC */
|
|
25
21
|
BookReader.prototype.init = (function(super_) {
|
|
26
22
|
return function() {
|
|
27
23
|
super_.call(this);
|
|
28
|
-
if (this.enableChaptersPlugin && this.ui !== 'embed') {
|
|
29
|
-
this.
|
|
30
|
-
}
|
|
31
|
-
if (this.enableMobileNav) {
|
|
32
|
-
this.bind(BookReader.eventNames.mobileNavOpen,
|
|
33
|
-
() => {
|
|
34
|
-
this.updateTOCState(this.firstIndex, this._tocEntries);
|
|
35
|
-
if ($('table-contents-list').parent().hasClass('mm-opened')) {
|
|
36
|
-
this.updateTOCState(this.firstIndex, this._tocEntries);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
$(".BRmobileMenu__tableContents").on("click", () => {
|
|
41
|
-
this.updateTOCState(this.firstIndex, this._tocEntries);
|
|
42
|
-
});
|
|
24
|
+
if (this.options.enableChaptersPlugin && this.ui !== 'embed') {
|
|
25
|
+
this._chapterInit();
|
|
43
26
|
}
|
|
44
27
|
};
|
|
45
28
|
})(BookReader.prototype.init);
|
|
46
29
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
BookReader.
|
|
55
|
-
const uiStringPage = 'Page'; // i18n
|
|
56
|
-
const percentThrough = BookReader.util.cssPercentage(pageIndex, this.book.getNumLeafs() - 1);
|
|
57
|
-
const jumpToChapter = (event) => {
|
|
58
|
-
this.jumpToIndex($(event.delegateTarget).data('pageIndex'));
|
|
59
|
-
$('.current-chapter').removeClass('current-chapter');
|
|
60
|
-
$(event.delegateTarget).addClass('current-chapter');
|
|
61
|
-
};
|
|
62
|
-
const title = `${chapterTitle} | `;
|
|
63
|
-
const pageStr = `${uiStringPage} ${pageNumber}`;
|
|
64
|
-
|
|
65
|
-
//adding items to mobile table of contents
|
|
66
|
-
const mobileChapter = $(`<li></li>`).append($(`<span class='BRTOCElementTitle'></span>`).text(title))
|
|
67
|
-
.append($(`<span class='BRTOCElementPage'></span>`).text(pageStr));
|
|
68
|
-
mobileChapter.addClass('BRtable-contents-el')
|
|
69
|
-
.appendTo(this.$('.table-contents-list'))
|
|
70
|
-
.data({ pageIndex });
|
|
71
|
-
|
|
72
|
-
//adds .BRchapters to the slider only if pageIndex exists
|
|
73
|
-
if (pageIndex != undefined) {
|
|
74
|
-
$(`<div></div>`)
|
|
75
|
-
.append($('<div />').text(title + pageStr))
|
|
76
|
-
.addClass('BRchapter')
|
|
77
|
-
.css({ left: percentThrough })
|
|
78
|
-
.appendTo(this.$('.BRnavline'))
|
|
79
|
-
.data({ pageIndex })
|
|
80
|
-
.on("mouseenter", event => {
|
|
81
|
-
// remove hover effect from other markers then turn on just for this
|
|
82
|
-
const marker = event.currentTarget;
|
|
83
|
-
const tooltip = marker.querySelector('div');
|
|
84
|
-
const tooltipOffset = tooltip.getBoundingClientRect();
|
|
85
|
-
const targetOffset = marker.getBoundingClientRect();
|
|
86
|
-
const boxSizeAdjust = parseInt(getComputedStyle(tooltip).paddingLeft) * 2;
|
|
87
|
-
if (tooltipOffset.x - boxSizeAdjust < 0) {
|
|
88
|
-
tooltip.style.setProperty('transform', `translateX(-${targetOffset.left - boxSizeAdjust}px)`);
|
|
89
|
-
}
|
|
90
|
-
this.$('.BRsearch,.BRchapter').removeClass('front');
|
|
91
|
-
$(event.target).addClass('front');
|
|
92
|
-
})
|
|
93
|
-
.on("mouseleave", event => $(event.target).removeClass('front'))
|
|
94
|
-
.on('click', jumpToChapter);
|
|
95
|
-
|
|
96
|
-
//adding clickable properties to mobile chapters
|
|
97
|
-
mobileChapter.bind('click', jumpToChapter)
|
|
98
|
-
.addClass('chapter-clickable')
|
|
99
|
-
.attr("data-event-click-tracking","BRTOCPanel|GoToChapter");
|
|
30
|
+
BookReader.prototype._chapterInit = async function() {
|
|
31
|
+
const olEdition = await this.getOpenLibraryRecord(this.options.olHost, this.options.bookId);
|
|
32
|
+
if (olEdition?.table_of_contents?.length) {
|
|
33
|
+
this._tocEntries = olEdition.table_of_contents.map(rawTOCEntry => (
|
|
34
|
+
Object.assign({}, rawTOCEntry, {pageIndex: this.book.getPageIndex(rawTOCEntry.pagenum)})
|
|
35
|
+
));
|
|
36
|
+
this._chaptersRender(this._tocEntries);
|
|
37
|
+
this.bind(BookReader.eventNames.pageChanged, () => this._chaptersUpdateCurrent());
|
|
100
38
|
}
|
|
101
|
-
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/*
|
|
105
|
-
* Remove all chapters.
|
|
106
|
-
*/
|
|
107
|
-
BookReader.prototype.removeChapters = function() {
|
|
108
|
-
this.$('.BRnavpos .BRchapter').remove();
|
|
109
39
|
};
|
|
110
40
|
|
|
111
41
|
/**
|
|
112
42
|
* Update the table of contents based on array of TOC entries.
|
|
113
|
-
* @param {TocEntry[]} tocEntries
|
|
114
43
|
*/
|
|
115
|
-
BookReader.prototype.
|
|
116
|
-
this.
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
44
|
+
BookReader.prototype._chaptersRender = function() {
|
|
45
|
+
const shell = /** @type {BookNavigator} */(this.shell);
|
|
46
|
+
shell.menuProviders['chapters'] = {
|
|
47
|
+
id: 'chapters',
|
|
48
|
+
icon: html`<ia-icon-toc style="width: var(--iconWidth); height: var(--iconHeight);"></ia-icon-toc>`,
|
|
49
|
+
label: 'Table of Contents',
|
|
50
|
+
component: html`<br-chapters-panel
|
|
51
|
+
.contents="${this._tocEntries}"
|
|
52
|
+
.jumpToPage="${(pageIndex) => {
|
|
53
|
+
this._chaptersUpdateCurrent(pageIndex);
|
|
54
|
+
this.jumpToIndex(pageIndex);
|
|
55
|
+
}}"
|
|
56
|
+
@connected="${(e) => {
|
|
57
|
+
this._chaptersPanel = e.target;
|
|
58
|
+
this._chaptersUpdateCurrent();
|
|
59
|
+
}}"
|
|
60
|
+
/>`,
|
|
61
|
+
};
|
|
62
|
+
shell.updateMenuContents();
|
|
63
|
+
for (const tocEntry of this._tocEntries) {
|
|
64
|
+
this._chaptersRenderMarker(tocEntry);
|
|
122
65
|
}
|
|
123
|
-
this._tocEntries = tocEntries;
|
|
124
|
-
$('.table-contents-list').children().each((i, el) => {
|
|
125
|
-
tocEntries[i].mobileHTML = el;
|
|
126
|
-
});
|
|
127
66
|
};
|
|
128
67
|
|
|
129
68
|
/**
|
|
130
69
|
* @typedef {Object} TocEntry
|
|
131
|
-
* Table of contents entry as defined
|
|
70
|
+
* Table of contents entry as defined by Open Library, with some extra values for internal use
|
|
132
71
|
* @property {string} pagenum
|
|
133
72
|
* @property {number} level
|
|
134
73
|
* @property {string} label
|
|
135
|
-
* @property {{type: '/type/toc_item'}} type
|
|
136
74
|
* @property {string} title
|
|
137
|
-
* @property {
|
|
138
|
-
* @property {number} pageIndex
|
|
139
|
-
|
|
75
|
+
* @property {number} pageIndex - Added
|
|
140
76
|
*
|
|
141
77
|
* @example {
|
|
142
78
|
* "pagenum": "17",
|
|
143
79
|
* "level": 1,
|
|
144
80
|
* "label": "CHAPTER I",
|
|
145
|
-
* "type": {"key": "/type/toc_item"},
|
|
146
81
|
* "title": "THE COUNTRY AND THE MISSION"
|
|
147
82
|
* }
|
|
148
83
|
*/
|
|
149
84
|
|
|
150
85
|
/**
|
|
151
|
-
* @param {TocEntry}
|
|
86
|
+
* @param {TocEntry} tocEntry
|
|
152
87
|
*/
|
|
153
|
-
BookReader.prototype.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
88
|
+
BookReader.prototype._chaptersRenderMarker = function(tocEntry) {
|
|
89
|
+
if (tocEntry.pageIndex == undefined) return;
|
|
90
|
+
|
|
91
|
+
//creates a string with non-void tocEntry.label and tocEntry.title
|
|
92
|
+
const chapterStr = [tocEntry.label, tocEntry.title]
|
|
157
93
|
.filter(x => x)
|
|
158
94
|
.join(' ');
|
|
159
|
-
|
|
95
|
+
|
|
96
|
+
const percentThrough = BookReader.util.cssPercentage(tocEntry.pageIndex, this.book.getNumLeafs() - 1);
|
|
97
|
+
$(`<div></div>`)
|
|
98
|
+
.append(
|
|
99
|
+
$('<div />')
|
|
100
|
+
.text(chapterStr)
|
|
101
|
+
.append($('<div class="BRchapterPage" />').text(`Page ${tocEntry.pagenum}`))
|
|
102
|
+
)
|
|
103
|
+
.addClass('BRchapter')
|
|
104
|
+
.css({ left: percentThrough })
|
|
105
|
+
.appendTo(this.$('.BRnavline'))
|
|
106
|
+
.on("mouseenter", event => {
|
|
107
|
+
// remove hover effect from other markers then turn on just for this
|
|
108
|
+
const marker = event.currentTarget;
|
|
109
|
+
const tooltip = marker.querySelector('div');
|
|
110
|
+
const tooltipOffset = tooltip.getBoundingClientRect();
|
|
111
|
+
const targetOffset = marker.getBoundingClientRect();
|
|
112
|
+
const boxSizeAdjust = parseInt(getComputedStyle(tooltip).paddingLeft) * 2;
|
|
113
|
+
if (tooltipOffset.x - boxSizeAdjust < 0) {
|
|
114
|
+
tooltip.style.setProperty('transform', `translateX(-${targetOffset.left - boxSizeAdjust}px)`);
|
|
115
|
+
}
|
|
116
|
+
this.$('.BRsearch,.BRchapter').removeClass('front');
|
|
117
|
+
$(event.target).addClass('front');
|
|
118
|
+
})
|
|
119
|
+
.on("mouseleave", event => $(event.target).removeClass('front'))
|
|
120
|
+
.on('click', () => {
|
|
121
|
+
this._chaptersUpdateCurrent(tocEntry.pageIndex);
|
|
122
|
+
this.jumpToIndex(tocEntry.pageIndex);
|
|
123
|
+
});
|
|
124
|
+
|
|
160
125
|
this.$('.BRchapter, .BRsearch').each((i, el) => {
|
|
161
126
|
const $el = $(el);
|
|
162
127
|
$el
|
|
@@ -166,79 +131,146 @@ BookReader.prototype.addChapterFromEntry = function(tocEntryObject) {
|
|
|
166
131
|
};
|
|
167
132
|
|
|
168
133
|
/**
|
|
169
|
-
* getOpenLibraryRecord
|
|
170
|
-
*
|
|
171
|
-
* The bookreader is designed to call openlibrary API and constructs the
|
|
172
|
-
* "Return book" button using the response.
|
|
173
|
-
*
|
|
174
134
|
* This makes a call to OL API and calls the given callback function with the
|
|
175
135
|
* response from the API.
|
|
136
|
+
*
|
|
137
|
+
* @param {string} olHost
|
|
138
|
+
* @param {string} ocaid
|
|
176
139
|
*/
|
|
177
|
-
BookReader.prototype.getOpenLibraryRecord = async function () {
|
|
140
|
+
BookReader.prototype.getOpenLibraryRecord = async function (olHost, ocaid) {
|
|
178
141
|
// Try looking up by ocaid first, then by source_record
|
|
179
|
-
const baseURL = `${
|
|
180
|
-
const fetchUrlByBookId = `${baseURL}&ocaid=${
|
|
181
|
-
|
|
182
|
-
/*
|
|
183
|
-
* Update Chapter markers based on received record from Open Library.
|
|
184
|
-
* Notes that Open Library record is used for extra metadata, and also for lending
|
|
185
|
-
*/
|
|
186
|
-
const setUpChapterMarkers = (olObject) => {
|
|
187
|
-
if (olObject && olObject.table_of_contents) {
|
|
188
|
-
// XXX check here that TOC is valid
|
|
189
|
-
this.updateTOC(olObject.table_of_contents);
|
|
190
|
-
}
|
|
191
|
-
};
|
|
142
|
+
const baseURL = `${olHost}/query.json?type=/type/edition&*=`;
|
|
143
|
+
const fetchUrlByBookId = `${baseURL}&ocaid=${ocaid}`;
|
|
192
144
|
|
|
193
145
|
let data = await $.ajax({ url: fetchUrlByBookId, dataType: 'jsonp' });
|
|
194
146
|
|
|
195
147
|
if (!data || !data.length) {
|
|
196
148
|
// try sourceid
|
|
197
|
-
data = await $.ajax({ url: `${baseURL}&source_records=ia:${
|
|
149
|
+
data = await $.ajax({ url: `${baseURL}&source_records=ia:${ocaid}`, dataType: 'jsonp' });
|
|
198
150
|
}
|
|
199
151
|
|
|
200
|
-
|
|
201
|
-
setUpChapterMarkers(data[0]);
|
|
202
|
-
}
|
|
152
|
+
return data?.[0];
|
|
203
153
|
};
|
|
204
154
|
|
|
205
|
-
// Extend buildMobileDrawerElement with table of contents list
|
|
206
|
-
BookReader.prototype.buildMobileDrawerElement = (function (super_) {
|
|
207
|
-
return function () {
|
|
208
|
-
const $el = super_.call(this);
|
|
209
|
-
if (this.enableMobileNav && this.options.enableChaptersPlugin) {
|
|
210
|
-
$el.find('.BRmobileMenu__moreInfoRow').after($(`
|
|
211
|
-
<li class="BRmobileMenu__tableContents" data-event-click-tracking="BRSidebar|TOCPanel">
|
|
212
|
-
<span>
|
|
213
|
-
<span class="DrawerIconWrapper">
|
|
214
|
-
<img class="DrawerIcon" src="${this.imagesBaseURL}icon_toc.svg" alt="toc-icon"/>
|
|
215
|
-
</span>
|
|
216
|
-
Table of Contents
|
|
217
|
-
</span>
|
|
218
|
-
<div>
|
|
219
|
-
<ol class="table-contents-list">
|
|
220
|
-
</ol>
|
|
221
|
-
</div>
|
|
222
|
-
</li>`).hide());
|
|
223
|
-
}
|
|
224
|
-
return $el;
|
|
225
|
-
};
|
|
226
|
-
})(BookReader.prototype.buildMobileDrawerElement);
|
|
227
|
-
|
|
228
155
|
/**
|
|
229
|
-
* highlights the current chapter based on current page
|
|
230
156
|
* @private
|
|
231
|
-
*
|
|
232
|
-
* @param {
|
|
157
|
+
* Highlights the current chapter based on current page
|
|
158
|
+
* @param {PageIndex} curIndex
|
|
233
159
|
*/
|
|
234
|
-
BookReader.prototype.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
const
|
|
239
|
-
|
|
240
|
-
(
|
|
241
|
-
|
|
242
|
-
|
|
160
|
+
BookReader.prototype._chaptersUpdateCurrent = function(
|
|
161
|
+
curIndex = (this.mode == 2 ? Math.max(...this.displayedIndices) : this.firstIndex)
|
|
162
|
+
) {
|
|
163
|
+
const tocEntriesIndexed = this._tocEntries.filter((el) => el.pageIndex != undefined).reverse();
|
|
164
|
+
const currChapter = tocEntriesIndexed[
|
|
165
|
+
// subtract one so that 2up shows the right label
|
|
166
|
+
tocEntriesIndexed.findIndex((chapter) => chapter.pageIndex <= curIndex)
|
|
167
|
+
];
|
|
168
|
+
if (this._chaptersPanel) {
|
|
169
|
+
this._chaptersPanel.currentChapter = currChapter;
|
|
243
170
|
}
|
|
244
171
|
};
|
|
172
|
+
|
|
173
|
+
@customElement('br-chapters-panel')
|
|
174
|
+
export class BRChaptersPanel extends LitElement {
|
|
175
|
+
/** @type {TocEntry[]} */
|
|
176
|
+
@property({ type: Array })
|
|
177
|
+
contents = [];
|
|
178
|
+
|
|
179
|
+
/** @type {TocEntry?} */
|
|
180
|
+
@property({ type: Object })
|
|
181
|
+
currentChapter = {};
|
|
182
|
+
|
|
183
|
+
/** @type {(pageIndex: PageIndex) => void} */
|
|
184
|
+
jumpToPage = () => {};
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {TocEntry[]} contents
|
|
188
|
+
*/
|
|
189
|
+
constructor(contents) {
|
|
190
|
+
super();
|
|
191
|
+
this.contents = contents;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
render() {
|
|
195
|
+
return html`
|
|
196
|
+
<ol>
|
|
197
|
+
${this.contents.map(tocEntry => this.renderTOCEntry(tocEntry))}
|
|
198
|
+
</ol>
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* @param {TocEntry} tocEntry
|
|
204
|
+
*/
|
|
205
|
+
renderTOCEntry(tocEntry) {
|
|
206
|
+
const chapterTitle = [tocEntry.label, tocEntry.title]
|
|
207
|
+
.filter(x => x)
|
|
208
|
+
.join(' ');
|
|
209
|
+
const clickable = tocEntry.pageIndex != undefined;
|
|
210
|
+
// note the click-tracking won't work...
|
|
211
|
+
return html`
|
|
212
|
+
<li
|
|
213
|
+
class="
|
|
214
|
+
BRtable-contents-el
|
|
215
|
+
${clickable ? 'clickable' : ''}
|
|
216
|
+
${tocEntry == this.currentChapter ? 'current' : ''}
|
|
217
|
+
"
|
|
218
|
+
style="${styleMap({marginLeft: (tocEntry.level - 1) * 10 + 'px'})}"
|
|
219
|
+
data-event-click-tracking="${ifDefined(clickable ? "BRTOCPanel|GoToChapter" : undefined)}"
|
|
220
|
+
@click="${() => this.jumpToPage(tocEntry.pageIndex)}"
|
|
221
|
+
>
|
|
222
|
+
${chapterTitle}
|
|
223
|
+
${tocEntry.pagenum ? html`
|
|
224
|
+
<br />
|
|
225
|
+
<span class="BRTOCElementPage">Page ${tocEntry.pagenum}</span>
|
|
226
|
+
` : nothing}
|
|
227
|
+
</li>`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
connectedCallback() {
|
|
231
|
+
super.connectedCallback();
|
|
232
|
+
this.dispatchEvent(new CustomEvent('connected'));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
updated(changedProperties) {
|
|
236
|
+
if (changedProperties.has('currentChapter')) {
|
|
237
|
+
this.shadowRoot.querySelector('li.current')?.scrollIntoView({
|
|
238
|
+
block: 'nearest',
|
|
239
|
+
behavior: 'smooth',
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static get styles() {
|
|
245
|
+
return css`
|
|
246
|
+
ol {
|
|
247
|
+
padding: 0;
|
|
248
|
+
margin: 0;
|
|
249
|
+
margin-right: 5px;
|
|
250
|
+
}
|
|
251
|
+
li {
|
|
252
|
+
padding: 10px;
|
|
253
|
+
overflow: hidden;
|
|
254
|
+
border-radius: 4px;
|
|
255
|
+
}
|
|
256
|
+
li.clickable {
|
|
257
|
+
font-weight: normal;
|
|
258
|
+
cursor: pointer;
|
|
259
|
+
transition: background-color 0.2s;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
li.clickable:not(.current):hover {
|
|
263
|
+
background-color: rgba(255,255,255, 0.05);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
li.current {
|
|
267
|
+
background-color: rgba(255,255,255,0.9);
|
|
268
|
+
color: #333;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
.BRTOCElementPage {
|
|
272
|
+
font-size: 0.85em;
|
|
273
|
+
opacity: .8;
|
|
274
|
+
}`;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -2,9 +2,6 @@
|
|
|
2
2
|
/* global BookReader */
|
|
3
3
|
/**
|
|
4
4
|
* Plugin for Archive.org book search
|
|
5
|
-
* NOTE: This script must be loaded AFTER `plugin.mobile_nav.js`
|
|
6
|
-
* as it mutates mobile nav drawer
|
|
7
|
-
*
|
|
8
5
|
* Events fired at various points throughout search processing are published
|
|
9
6
|
* on the document DOM element. These can be subscribed to using jQuery's event
|
|
10
7
|
* binding method `$.fn.on`. All of the events are prefixed with a BookReader
|
|
@@ -195,7 +195,7 @@ BookReader.prototype.initNavbar = (function (super_) {
|
|
|
195
195
|
$(`<li>
|
|
196
196
|
<button class="BRicon read js-tooltip" title="${tooltips.readAloud}">
|
|
197
197
|
<div class="icon icon-read-aloud"></div>
|
|
198
|
-
<span class="
|
|
198
|
+
<span class="BRtooltip">${tooltips.readAloud}</span>
|
|
199
199
|
</button>
|
|
200
200
|
</li>`).insertBefore($el.find('.BRcontrols .BRicon.zoom_out').closest('li'));
|
|
201
201
|
}
|
package/tests/e2e/base.test.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { runBaseTests } from './helpers/base';
|
|
2
2
|
import BookReader from './models/BookReader';
|
|
3
|
-
import {
|
|
4
|
-
// import { runMobileSearchTests } from './helpers/mobileSearch';
|
|
3
|
+
import { runSearchTests } from './helpers/search';
|
|
5
4
|
import params from './helpers/params';
|
|
6
5
|
|
|
7
6
|
const ocaids = params.ocaids || [
|
|
@@ -22,14 +21,7 @@ ocaids.forEach(ocaid => {
|
|
|
22
21
|
runBaseTests(new BookReader());
|
|
23
22
|
|
|
24
23
|
|
|
25
|
-
fixture `
|
|
24
|
+
fixture `Search Tests for: ${ocaid}`
|
|
26
25
|
.page `${url}`;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// Todo: deprecated, will remove once mmenu is removed.
|
|
30
|
-
// fixture `Mobile Search Tests for: ${ocaid}`
|
|
31
|
-
// .page `${url}`
|
|
32
|
-
// runMobileSearchTests(new BookReader());
|
|
33
|
-
|
|
34
|
-
|
|
26
|
+
runSearchTests(new BookReader());
|
|
35
27
|
});
|
|
@@ -53,14 +53,14 @@ export function runBaseTests (br) {
|
|
|
53
53
|
test('nav menu displays properly', async t => {
|
|
54
54
|
const { nav } = br;
|
|
55
55
|
|
|
56
|
-
await t.expect(nav.
|
|
57
|
-
await t.expect(nav.
|
|
58
|
-
await t.expect(nav.
|
|
59
|
-
await t.expect(nav.
|
|
60
|
-
await t.expect(nav.
|
|
61
|
-
await t.expect(nav.
|
|
62
|
-
await t.expect(nav.
|
|
63
|
-
await t.expect(nav.
|
|
56
|
+
await t.expect(nav.goLeft.visible).ok();
|
|
57
|
+
await t.expect(nav.goRight.visible).ok();
|
|
58
|
+
await t.expect(nav.mode1Up.visible).ok();
|
|
59
|
+
await t.expect(nav.mode2Up.visible).ok();
|
|
60
|
+
await t.expect(nav.modeThumb.visible).ok();
|
|
61
|
+
await t.expect(nav.zoomIn.visible).ok();
|
|
62
|
+
await t.expect(nav.zoomOut.visible).ok();
|
|
63
|
+
await t.expect(nav.fullScreen.visible).ok();
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
test("Canonical URL has no initial parameters", async t => {
|
|
@@ -87,7 +87,7 @@ export function runBaseTests (br) {
|
|
|
87
87
|
const initialUrl = await getUrl();
|
|
88
88
|
|
|
89
89
|
// Set Cookie by page navigation, wait for cookie
|
|
90
|
-
await t.click(nav.
|
|
90
|
+
await t.click(nav.goNext);
|
|
91
91
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
92
92
|
|
|
93
93
|
// reload canonical URL, wait for URL change
|
|
@@ -109,9 +109,9 @@ export function runBaseTests (br) {
|
|
|
109
109
|
const { nav, BRcontainer} = br;
|
|
110
110
|
|
|
111
111
|
// Go to next page, so we can go previous if at front cover
|
|
112
|
-
await t.click(nav.
|
|
112
|
+
await t.click(nav.goNext);
|
|
113
113
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
114
|
-
await t.click(nav.
|
|
114
|
+
await t.click(nav.goNext);
|
|
115
115
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
116
116
|
|
|
117
117
|
const onLoadBrState = BRcontainer.child(0);
|
|
@@ -119,7 +119,7 @@ export function runBaseTests (br) {
|
|
|
119
119
|
const origImg1Src = await initialImages.nth(0).getAttribute('src');
|
|
120
120
|
const origImg2Src = await initialImages.nth(-1).getAttribute('src');
|
|
121
121
|
|
|
122
|
-
await t.click(nav.
|
|
122
|
+
await t.click(nav.goPrev);
|
|
123
123
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
124
124
|
|
|
125
125
|
const nextBrState = Selector('.BRcontainer').child(0);
|
|
@@ -144,7 +144,7 @@ export function runBaseTests (br) {
|
|
|
144
144
|
// Note: this will fail on a R to L book if at front cover
|
|
145
145
|
const { nav, BRcontainer} = br;
|
|
146
146
|
// Flip away from cover
|
|
147
|
-
await t.click(nav.
|
|
147
|
+
await t.click(nav.goNext);
|
|
148
148
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
149
149
|
|
|
150
150
|
const onLoadBrState = BRcontainer.child(0);
|
|
@@ -152,7 +152,7 @@ export function runBaseTests (br) {
|
|
|
152
152
|
const origImg1Src = await initialImages.nth(0).getAttribute('src');
|
|
153
153
|
const origImg2Src = await initialImages.nth(-1).getAttribute('src');
|
|
154
154
|
|
|
155
|
-
await t.click(nav.
|
|
155
|
+
await t.click(nav.goNext);
|
|
156
156
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
157
157
|
|
|
158
158
|
const nextBrState = Selector('.BRcontainer').child(0);
|
|
@@ -173,28 +173,28 @@ export function runBaseTests (br) {
|
|
|
173
173
|
test('Clicking `page flip buttons` updates location', async t => {
|
|
174
174
|
const { nav } = br;
|
|
175
175
|
// Page navigation creates params
|
|
176
|
-
await t.click(nav.
|
|
176
|
+
await t.click(nav.goNext);
|
|
177
177
|
await t.expect(isPageInUrl()).eql(true);
|
|
178
178
|
await t.expect(isModeInUrl('2up')).eql(true);
|
|
179
179
|
|
|
180
|
-
await t.click(nav.
|
|
180
|
+
await t.click(nav.goPrev);
|
|
181
181
|
await t.expect(isPageInUrl()).eql(true);
|
|
182
182
|
await t.expect(isModeInUrl('2up')).eql(true);
|
|
183
183
|
});
|
|
184
184
|
|
|
185
185
|
test('Clicking `2 page view` brings up cur page + caching', async t => {
|
|
186
186
|
const { nav } = br;
|
|
187
|
-
await t.click(nav.
|
|
187
|
+
await t.click(nav.mode2Up);
|
|
188
188
|
await t.expect(Selector('.BRpagecontainer.BRpage-visible').count).eql(1);
|
|
189
189
|
await t.expect(Selector('.BRpagecontainer').count).eql(3);
|
|
190
190
|
});
|
|
191
191
|
|
|
192
192
|
test('Clicking `1 page view` brings up 1 at a time', async t => {
|
|
193
193
|
const { nav } = br;
|
|
194
|
-
await t.click(nav.
|
|
194
|
+
await t.click(nav.mode1Up);
|
|
195
195
|
|
|
196
196
|
// Flip away from cover
|
|
197
|
-
await t.click(nav.
|
|
197
|
+
await t.click(nav.goNext);
|
|
198
198
|
await t.wait(PAGE_FLIP_WAIT_TIME);
|
|
199
199
|
|
|
200
200
|
// we usually pre-fetch the page in question & the 2 after it
|
|
@@ -203,7 +203,7 @@ export function runBaseTests (br) {
|
|
|
203
203
|
|
|
204
204
|
test('Clicking `thumbnail view` brings up all of the page thumbnails', async t => {
|
|
205
205
|
const { nav } = br;
|
|
206
|
-
await t.click(nav.
|
|
206
|
+
await t.click(nav.modeThumb);
|
|
207
207
|
await t.expect(Selector('.BRpagecontainer').count).gte(3);
|
|
208
208
|
});
|
|
209
209
|
|
|
@@ -213,12 +213,12 @@ export function runBaseTests (br) {
|
|
|
213
213
|
|
|
214
214
|
await t.expect(br.BRcontainer.visible).ok();
|
|
215
215
|
await t.expect(page.visible).ok();
|
|
216
|
-
await t.expect(nav.
|
|
216
|
+
await t.expect(nav.zoomOut.visible).ok();
|
|
217
217
|
|
|
218
218
|
const initialBookHeight = await page.getBoundingClientRectProperty('height');
|
|
219
219
|
const initialBookWidth = await page.getBoundingClientRectProperty('width');
|
|
220
220
|
|
|
221
|
-
await t.click(nav.
|
|
221
|
+
await t.click(nav.zoomOut);
|
|
222
222
|
|
|
223
223
|
const zoomOutBookHeight = await page.getBoundingClientRectProperty('height');
|
|
224
224
|
const zoomOutBookWidth = await page.getBoundingClientRectProperty('width');
|
|
@@ -233,12 +233,12 @@ export function runBaseTests (br) {
|
|
|
233
233
|
|
|
234
234
|
await t.expect(br.BRcontainer.visible).ok();
|
|
235
235
|
await t.expect(page.visible).ok();
|
|
236
|
-
await t.expect(nav.
|
|
236
|
+
await t.expect(nav.zoomIn.visible).ok();
|
|
237
237
|
|
|
238
238
|
const initialBookHeight = await page.getBoundingClientRectProperty('height');
|
|
239
239
|
const initialBookWidth = await page.getBoundingClientRectProperty('width');
|
|
240
240
|
|
|
241
|
-
await t.click(nav.
|
|
241
|
+
await t.click(nav.zoomIn);
|
|
242
242
|
|
|
243
243
|
const zoomInBookHeight = await page.getBoundingClientRectProperty('height');
|
|
244
244
|
const zoomIntBookWidth = await page.getBoundingClientRectProperty('width');
|
|
@@ -253,10 +253,10 @@ export function runBaseTests (br) {
|
|
|
253
253
|
|
|
254
254
|
// initial in-page
|
|
255
255
|
await t.expect(BRcontainer.getBoundingClientRectProperty('width')).lte(windowWidth);
|
|
256
|
-
await t.click(nav.
|
|
256
|
+
await t.click(nav.fullScreen);
|
|
257
257
|
// full screen
|
|
258
258
|
await t.expect(BRcontainer.getBoundingClientRectProperty('width')).eql(windowWidth);
|
|
259
|
-
await t.click(nav.
|
|
259
|
+
await t.click(nav.fullScreen);
|
|
260
260
|
// in-page
|
|
261
261
|
await t.expect(BRcontainer.getBoundingClientRectProperty('width')).lte(windowWidth);
|
|
262
262
|
});
|
|
@@ -4,20 +4,20 @@ const getPageUrl = ClientFunction(() => window.location.href);
|
|
|
4
4
|
export function runRightToLeftTests (br) {
|
|
5
5
|
test('Right to Left - correct initialization in two-page view', async t => {
|
|
6
6
|
const { nav, BRcontainer} = br;
|
|
7
|
-
await t.click(nav.
|
|
7
|
+
await t.click(nav.mode2Up);
|
|
8
8
|
|
|
9
9
|
//checking right leaf edge is not in tree
|
|
10
10
|
await t.expect(BRcontainer.find('.br-mode-2up__leafs--left').count).eql(1);
|
|
11
11
|
await t.expect(BRcontainer.find('.br-mode-2up__leafs--right').count).eql(0);
|
|
12
12
|
|
|
13
13
|
//checks slider is in correct position
|
|
14
|
-
await t.expect(nav.
|
|
14
|
+
await t.expect(nav.sliderRange.getStyleProperty('width')).eql('0px');
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
test('Right to Left - assuring flipping left goes to next page', async t => {
|
|
18
18
|
const { nav } = br;
|
|
19
|
-
await t.click(nav.
|
|
20
|
-
await t.click(nav.
|
|
19
|
+
await t.click(nav.mode2Up);
|
|
20
|
+
await t.click(nav.goLeft);
|
|
21
21
|
await t.expect(getPageUrl()).match(/page\/n1/);
|
|
22
22
|
});
|
|
23
23
|
}
|