@internetarchive/bookreader 5.0.0-28-remove-url-defaults → 5.0.0-30-b
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/.husky/_/husky.sh +30 -0
- package/BookReader/BookReader.css +1 -1
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +1458 -0
- package/BookReader/{bookreader-component-bundle.js.LICENSE.txt → ia-bookreader-bundle.js.LICENSE.txt} +12 -0
- package/BookReader/ia-bookreader-bundle.js.map +1 -0
- package/BookReader/plugins/plugin.search.js +1 -1
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReaderDemo/BookReaderDemo.css +14 -1
- package/BookReaderDemo/IADemoBr.js +107 -0
- package/BookReaderDemo/demo-internetarchive.html +64 -99
- package/CHANGELOG.md +4 -0
- package/package.json +9 -6
- package/src/BookNavigator/assets/ia-logo.js +17 -0
- package/src/BookNavigator/book-navigator.js +528 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +2 -1
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +20 -8
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +84 -51
- package/src/BookNavigator/downloads/downloads-provider.js +5 -9
- package/src/BookNavigator/downloads/downloads.js +1 -0
- package/src/BookNavigator/search/search-provider.js +15 -8
- package/src/BookNavigator/sharing.js +27 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +9 -8
- package/src/BookNavigator/volumes/volumes-provider.js +3 -4
- package/src/BookReader/options.js +6 -0
- package/src/BookReader.js +20 -8
- package/src/css/_BRComponent.scss +1 -1
- package/src/ia-bookreader/ia-bookreader.js +209 -0
- package/src/plugins/search/plugin.search.js +9 -9
- package/{src → stat}/BookNavigator/BookModel.js +0 -0
- package/{src → stat}/BookNavigator/BookNavigator.js +109 -102
- package/stat/BookNavigator/assets/bookmark-colors.js +15 -0
- package/stat/BookNavigator/assets/button-base.js +61 -0
- package/stat/BookNavigator/assets/ia-logo.js +17 -0
- package/stat/BookNavigator/assets/icon_checkmark.js +6 -0
- package/stat/BookNavigator/assets/icon_close.js +3 -0
- package/stat/BookNavigator/assets/icon_sort_asc.js +5 -0
- package/stat/BookNavigator/assets/icon_sort_desc.js +5 -0
- package/stat/BookNavigator/assets/icon_sort_neutral.js +5 -0
- package/stat/BookNavigator/assets/icon_volumes.js +11 -0
- package/stat/BookNavigator/bookmarks/bookmark-button.js +64 -0
- package/stat/BookNavigator/bookmarks/bookmark-edit.js +215 -0
- package/stat/BookNavigator/bookmarks/bookmarks-list.js +285 -0
- package/stat/BookNavigator/bookmarks/bookmarks-loginCTA.js +28 -0
- package/stat/BookNavigator/bookmarks/bookmarks-provider.js +56 -0
- package/stat/BookNavigator/bookmarks/ia-bookmarks.js +523 -0
- package/{src → stat}/BookNavigator/br-fullscreen-mgr.js +1 -2
- package/stat/BookNavigator/delete-modal-actions.js +49 -0
- package/stat/BookNavigator/downloads/downloads-provider.js +72 -0
- package/stat/BookNavigator/downloads/downloads.js +139 -0
- package/stat/BookNavigator/provider-config.js +0 -0
- package/stat/BookNavigator/search/a-search-result.js +55 -0
- package/stat/BookNavigator/search/search-provider.js +180 -0
- package/stat/BookNavigator/search/search-results.js +360 -0
- package/{src/ItemNavigator/providers → stat/BookNavigator}/sharing.js +3 -5
- package/stat/BookNavigator/visual-adjustments/visual-adjustments-provider.js +94 -0
- package/stat/BookNavigator/visual-adjustments/visual-adjustments.js +280 -0
- package/stat/BookNavigator/volumes/volumes-provider.js +83 -0
- package/stat/BookNavigator/volumes/volumes.js +178 -0
- package/stat/BookReader/BookModel.js +518 -0
- package/stat/BookReader/DebugConsole.js +54 -0
- package/stat/BookReader/DragScrollable.js +233 -0
- package/stat/BookReader/ImageCache.js +116 -0
- package/stat/BookReader/Mode1Up.js +102 -0
- package/stat/BookReader/Mode1UpLit.js +434 -0
- package/stat/BookReader/Mode2Up.js +1372 -0
- package/stat/BookReader/ModeSmoothZoom.js +177 -0
- package/stat/BookReader/ModeThumb.js +344 -0
- package/stat/BookReader/Navbar/Navbar.js +310 -0
- package/stat/BookReader/PageContainer.js +120 -0
- package/stat/BookReader/ReduceSet.js +26 -0
- package/stat/BookReader/Toolbar/Toolbar.js +384 -0
- package/stat/BookReader/events.js +20 -0
- package/stat/BookReader/options.js +324 -0
- package/stat/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/stat/BookReader/utils/classes.js +36 -0
- package/stat/BookReader/utils.js +240 -0
- package/stat/BookReader.js +2550 -0
- package/{src → stat}/BookReaderComponent/BookReaderComponent.js +15 -10
- package/stat/assets/icons/1up.svg +12 -0
- package/stat/assets/icons/2up.svg +15 -0
- package/stat/assets/icons/advance.svg +26 -0
- package/stat/assets/icons/chevron-right.svg +1 -0
- package/stat/assets/icons/close-circle-dark.svg +1 -0
- package/stat/assets/icons/close-circle.svg +1 -0
- package/stat/assets/icons/fullscreen.svg +17 -0
- package/stat/assets/icons/fullscreen_exit.svg +17 -0
- package/stat/assets/icons/hamburger.svg +15 -0
- package/stat/assets/icons/left-arrow.svg +12 -0
- package/stat/assets/icons/magnify-minus.svg +16 -0
- package/stat/assets/icons/magnify-plus.svg +17 -0
- package/stat/assets/icons/magnify.svg +15 -0
- package/stat/assets/icons/pause.svg +23 -0
- package/stat/assets/icons/play.svg +22 -0
- package/stat/assets/icons/playback-speed.svg +34 -0
- package/stat/assets/icons/read-aloud.svg +22 -0
- package/stat/assets/icons/review.svg +22 -0
- package/stat/assets/icons/thumbnails.svg +17 -0
- package/stat/assets/icons/voice.svg +1 -0
- package/stat/assets/icons/volume-full.svg +22 -0
- package/stat/assets/images/BRicons.png +0 -0
- package/stat/assets/images/BRicons.svg +94 -0
- package/stat/assets/images/BRicons_ia.png +0 -0
- package/stat/assets/images/back_pages.png +0 -0
- package/stat/assets/images/book_bottom_icon.png +0 -0
- package/stat/assets/images/book_down_icon.png +0 -0
- package/stat/assets/images/book_left_icon.png +0 -0
- package/stat/assets/images/book_leftmost_icon.png +0 -0
- package/stat/assets/images/book_right_icon.png +0 -0
- package/stat/assets/images/book_rightmost_icon.png +0 -0
- package/stat/assets/images/book_top_icon.png +0 -0
- package/stat/assets/images/book_up_icon.png +0 -0
- package/stat/assets/images/books_graphic.svg +177 -0
- package/stat/assets/images/booksplit.png +0 -0
- package/stat/assets/images/control_pause_icon.png +0 -0
- package/stat/assets/images/control_play_icon.png +0 -0
- package/stat/assets/images/embed_icon.png +0 -0
- package/stat/assets/images/icon-home-ia.png +0 -0
- package/stat/assets/images/icon_OL-logo-xs.png +0 -0
- package/stat/assets/images/icon_alert-xs.png +0 -0
- package/stat/assets/images/icon_book.svg +12 -0
- package/stat/assets/images/icon_bookmark.svg +12 -0
- package/stat/assets/images/icon_close-pop.png +0 -0
- package/stat/assets/images/icon_download.png +0 -0
- package/stat/assets/images/icon_gear.svg +14 -0
- package/stat/assets/images/icon_hamburger.svg +20 -0
- package/stat/assets/images/icon_home.png +0 -0
- package/stat/assets/images/icon_home.svg +21 -0
- package/stat/assets/images/icon_home_ia.png +0 -0
- package/stat/assets/images/icon_indicator.png +0 -0
- package/stat/assets/images/icon_info.svg +11 -0
- package/stat/assets/images/icon_one_page.svg +8 -0
- package/stat/assets/images/icon_pause.svg +1 -0
- package/stat/assets/images/icon_play.svg +1 -0
- package/stat/assets/images/icon_playback-rate.svg +15 -0
- package/stat/assets/images/icon_return.png +0 -0
- package/stat/assets/images/icon_search_button.svg +8 -0
- package/stat/assets/images/icon_share.svg +9 -0
- package/stat/assets/images/icon_skip-ahead.svg +6 -0
- package/stat/assets/images/icon_skip-back.svg +13 -0
- package/stat/assets/images/icon_speaker.svg +18 -0
- package/stat/assets/images/icon_speaker_open.svg +10 -0
- package/stat/assets/images/icon_thumbnails.svg +12 -0
- package/stat/assets/images/icon_toc.svg +5 -0
- package/stat/assets/images/icon_two_pages.svg +9 -0
- package/stat/assets/images/icon_zoomer.png +0 -0
- package/stat/assets/images/loading.gif +0 -0
- package/stat/assets/images/logo_icon.png +0 -0
- package/stat/assets/images/marker_chap-off.png +0 -0
- package/stat/assets/images/marker_chap-off.svg +11 -0
- package/stat/assets/images/marker_chap-off_ia.png +0 -0
- package/stat/assets/images/marker_chap-on.png +0 -0
- package/stat/assets/images/marker_chap-on.svg +11 -0
- package/stat/assets/images/marker_srch-on.svg +11 -0
- package/stat/assets/images/marker_srchchap-off.png +0 -0
- package/stat/assets/images/marker_srchchap-on.png +0 -0
- package/stat/assets/images/nav_control-dn.png +0 -0
- package/stat/assets/images/nav_control-dn_ia.png +0 -0
- package/stat/assets/images/nav_control-up.png +0 -0
- package/stat/assets/images/nav_control-up_ia.png +0 -0
- package/stat/assets/images/nav_control.png +0 -0
- package/stat/assets/images/one_page_mode_icon.png +0 -0
- package/stat/assets/images/paper-badge.png +0 -0
- package/stat/assets/images/print_icon.png +0 -0
- package/stat/assets/images/progressbar.gif +0 -0
- package/stat/assets/images/right_edges.png +0 -0
- package/stat/assets/images/slider.png +0 -0
- package/stat/assets/images/slider_ia.png +0 -0
- package/stat/assets/images/thumbnail_mode_icon.png +0 -0
- package/stat/assets/images/transparent.png +0 -0
- package/stat/assets/images/two_page_mode_icon.png +0 -0
- package/stat/assets/images/zoom_in_icon.png +0 -0
- package/stat/assets/images/zoom_out_icon.png +0 -0
- package/stat/css/BookReader.scss +89 -0
- package/stat/css/_BRBookmarks.scss +29 -0
- package/stat/css/_BRComponent.scss +13 -0
- package/stat/css/_BRfloat.scss +197 -0
- package/stat/css/_BRicon.scss +48 -0
- package/stat/css/_BRmain.scss +251 -0
- package/stat/css/_BRnav.scss +359 -0
- package/stat/css/_BRpages.scss +139 -0
- package/stat/css/_BRsearch.scss +226 -0
- package/stat/css/_BRtoolbar.scss +84 -0
- package/stat/css/_BRvendor.scss +5 -0
- package/stat/css/_MobileNav.scss +194 -0
- package/stat/css/_TextSelection.scss +32 -0
- package/stat/css/_colorbox.scss +52 -0
- package/stat/css/_controls.scss +253 -0
- package/stat/css/_icons.scss +121 -0
- package/stat/jquery-wrapper.js +4 -0
- package/stat/plugins/plugin.archive_analytics.js +86 -0
- package/stat/plugins/plugin.autoplay.js +129 -0
- package/stat/plugins/plugin.chapters.js +248 -0
- package/stat/plugins/plugin.iframe.js +48 -0
- package/stat/plugins/plugin.mobile_nav.js +288 -0
- package/stat/plugins/plugin.resume.js +68 -0
- package/stat/plugins/plugin.text_selection.js +291 -0
- package/stat/plugins/plugin.url.js +198 -0
- package/stat/plugins/plugin.vendor-fullscreen.js +247 -0
- package/stat/plugins/search/plugin.search.js +439 -0
- package/stat/plugins/search/view.js +439 -0
- package/stat/plugins/tts/AbstractTTSEngine.js +249 -0
- package/stat/plugins/tts/FestivalTTSEngine.js +169 -0
- package/stat/plugins/tts/PageChunk.js +107 -0
- package/stat/plugins/tts/PageChunkIterator.js +163 -0
- package/stat/plugins/tts/WebTTSEngine.js +357 -0
- package/stat/plugins/tts/plugin.tts.js +357 -0
- package/stat/plugins/tts/tooltip_dict.js +15 -0
- package/stat/plugins/tts/utils.js +91 -0
- package/stat/util/browserSniffing.js +30 -0
- package/stat/util/debouncer.js +26 -0
- package/stat/util/docCookies.js +67 -0
- package/stat/util/strings.js +34 -0
- package/tests/e2e/viewmode.test.js +30 -30
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +64 -52
- package/tests/karma/BookNavigator/book-navigator.test.js +413 -108
- package/tests/karma/BookNavigator/bookmarks/bookmark-button.test.js +44 -0
- package/tests/karma/BookNavigator/downloads/downloads-provider.test.js +6 -3
- package/tests/karma/BookNavigator/search/search-provider.test.js +106 -6
- package/tests/karma/BookNavigator/search/search-results.test.js +0 -2
- package/tests/karma/BookNavigator/sharing/sharing-provider.test.js +29 -20
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +41 -17
- package/webpack.config.js +1 -1
- package/.nvmrc +0 -1
- package/BookReader/bookreader-component-bundle.js +0 -1436
- package/BookReader/bookreader-component-bundle.js.map +0 -1
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/ItemNavigator/ItemNavigator.js +0 -377
@@ -0,0 +1,357 @@
|
|
1
|
+
/* global BookReader */
|
2
|
+
/**
|
3
|
+
* Plugin for Text to Speech in BookReader
|
4
|
+
*/
|
5
|
+
import FestivalTTSEngine from './FestivalTTSEngine.js';
|
6
|
+
import WebTTSEngine from './WebTTSEngine.js';
|
7
|
+
import { toISO6391, approximateWordCount } from './utils.js';
|
8
|
+
import { en as tooltips } from './tooltip_dict.js';
|
9
|
+
import { renderBoxesInPageContainerLayer } from '../../BookReader/PageContainer.js';
|
10
|
+
/** @typedef {import('./PageChunk.js').default} PageChunk */
|
11
|
+
/** @typedef {import("./AbstractTTSEngine.js").default} AbstractTTSEngine */
|
12
|
+
|
13
|
+
// Default options for TTS
|
14
|
+
jQuery.extend(BookReader.defaultOptions, {
|
15
|
+
server: 'ia600609.us.archive.org',
|
16
|
+
bookPath: '',
|
17
|
+
enableTtsPlugin: true,
|
18
|
+
});
|
19
|
+
|
20
|
+
// Extend the constructor to add TTS properties
|
21
|
+
BookReader.prototype.setup = (function (super_) {
|
22
|
+
return function (options) {
|
23
|
+
super_.call(this, options);
|
24
|
+
|
25
|
+
if (this.options.enableTtsPlugin) {
|
26
|
+
/** @type { {[pageIndex: number]: Array<{ l: number, r: number, t: number, b: number }>} } */
|
27
|
+
this._ttsBoxesByIndex = {};
|
28
|
+
|
29
|
+
let TTSEngine = WebTTSEngine.isSupported() ? WebTTSEngine :
|
30
|
+
FestivalTTSEngine.isSupported() ? FestivalTTSEngine :
|
31
|
+
null;
|
32
|
+
|
33
|
+
if (/_forceTTSEngine=(festival|web)/.test(location.toString())) {
|
34
|
+
const engineName = location.toString().match(/_forceTTSEngine=(festival|web)/)[1];
|
35
|
+
TTSEngine = { festival: FestivalTTSEngine, web: WebTTSEngine }[engineName];
|
36
|
+
}
|
37
|
+
|
38
|
+
if (TTSEngine) {
|
39
|
+
/** @type {AbstractTTSEngine} */
|
40
|
+
this.ttsEngine = new TTSEngine({
|
41
|
+
server: options.server,
|
42
|
+
bookPath: options.bookPath,
|
43
|
+
bookLanguage: toISO6391(options.bookLanguage),
|
44
|
+
onLoadingStart: this.showProgressPopup.bind(this, 'Loading audio...'),
|
45
|
+
onLoadingComplete: this.removeProgressPopup.bind(this),
|
46
|
+
onDone: this.ttsStop.bind(this),
|
47
|
+
beforeChunkPlay: this.ttsBeforeChunkPlay.bind(this),
|
48
|
+
afterChunkPlay: this.ttsSendChunkFinishedAnalyticsEvent.bind(this),
|
49
|
+
});
|
50
|
+
}
|
51
|
+
}
|
52
|
+
};
|
53
|
+
})(BookReader.prototype.setup);
|
54
|
+
|
55
|
+
BookReader.prototype.init = (function(super_) {
|
56
|
+
return function() {
|
57
|
+
if (this.options.enableTtsPlugin) {
|
58
|
+
// Bind to events
|
59
|
+
|
60
|
+
this.bind(BookReader.eventNames.PostInit, () => {
|
61
|
+
this.$('.BRicon.read').click(() => {
|
62
|
+
this.ttsToggle();
|
63
|
+
return false;
|
64
|
+
});
|
65
|
+
if (this.ttsEngine) {
|
66
|
+
this.ttsEngine.init();
|
67
|
+
if (/[?&]_autoReadAloud=show/.test(location.toString())) {
|
68
|
+
this.ttsStart(false); // false flag is to initiate read aloud controls
|
69
|
+
}
|
70
|
+
}
|
71
|
+
});
|
72
|
+
|
73
|
+
// This is fired when the hash changes by one of the other plugins!
|
74
|
+
// i.e. it will fire every time the page changes -_-
|
75
|
+
// this.bind(BookReader.eventNames.stop, function(e, br) {
|
76
|
+
// this.ttsStop();
|
77
|
+
// }.bind(this));
|
78
|
+
}
|
79
|
+
super_.call(this);
|
80
|
+
};
|
81
|
+
})(BookReader.prototype.init);
|
82
|
+
|
83
|
+
/** @override */
|
84
|
+
BookReader.prototype._createPageContainer = (function (super_) {
|
85
|
+
return function (index) {
|
86
|
+
const pageContainer = super_.call(this, index);
|
87
|
+
if (this.options.enableTtsPlugin && pageContainer.page && index in this._ttsBoxesByIndex) {
|
88
|
+
const pageIndex = pageContainer.page.index;
|
89
|
+
renderBoxesInPageContainerLayer('ttsHiliteLayer', this._ttsBoxesByIndex[pageIndex], pageContainer.page, pageContainer.$container[0]);
|
90
|
+
}
|
91
|
+
return pageContainer;
|
92
|
+
};
|
93
|
+
})(BookReader.prototype._createPageContainer);
|
94
|
+
|
95
|
+
// Extend buildMobileDrawerElement
|
96
|
+
BookReader.prototype.buildMobileDrawerElement = (function (super_) {
|
97
|
+
return function () {
|
98
|
+
const $el = super_.call(this);
|
99
|
+
if (this.options.enableTtsPlugin && this.ttsEngine) {
|
100
|
+
$el.find('.BRmobileMenu__moreInfoRow').after($(`
|
101
|
+
<li>
|
102
|
+
<span>
|
103
|
+
<span class="DrawerIconWrapper"><img class="DrawerIcon" src="${this.imagesBaseURL}icon_speaker_open.svg" alt="info-speaker"/></span>
|
104
|
+
Read Aloud
|
105
|
+
</span>
|
106
|
+
<div>
|
107
|
+
<span class="larger">Press to toggle read aloud</span>
|
108
|
+
<br/>
|
109
|
+
<button class="BRicon read"></button>
|
110
|
+
</div>
|
111
|
+
</li>`));
|
112
|
+
}
|
113
|
+
return $el;
|
114
|
+
};
|
115
|
+
})(BookReader.prototype.buildMobileDrawerElement);
|
116
|
+
|
117
|
+
// Extend initNavbar
|
118
|
+
BookReader.prototype.initNavbar = (function (super_) {
|
119
|
+
return function () {
|
120
|
+
const $el = super_.call(this);
|
121
|
+
if (this.options.enableTtsPlugin && this.ttsEngine) {
|
122
|
+
this.refs.$BRReadAloudToolbar = $(`
|
123
|
+
<ul class="read-aloud">
|
124
|
+
<li>
|
125
|
+
<select class="playback-speed" name="playback-speed" title="${tooltips.playbackSpeed}">
|
126
|
+
<option value="0.25">0.25x</option>
|
127
|
+
<option value="0.5">0.5x</option>
|
128
|
+
<option value="0.75">0.75x</option>
|
129
|
+
<option value="1.0" selected>1.0x</option>
|
130
|
+
<option value="1.25">1.25x</option>
|
131
|
+
<option value="1.5">1.5x</option>
|
132
|
+
<option value="1.75">1.75x</option>
|
133
|
+
<option value="2">2x</option>
|
134
|
+
</select>
|
135
|
+
</li>
|
136
|
+
<li>
|
137
|
+
<button type="button" name="review" title="${tooltips.review}">
|
138
|
+
<div class="icon icon-review"></div>
|
139
|
+
</button>
|
140
|
+
</li>
|
141
|
+
<li>
|
142
|
+
<button type="button" name="play" title="${tooltips.play}">
|
143
|
+
<div class="icon icon-play"></div>
|
144
|
+
<div class="icon icon-pause"></div>
|
145
|
+
</button>
|
146
|
+
</li>
|
147
|
+
<li>
|
148
|
+
<button type="button" name="advance" title="${tooltips.advance}">
|
149
|
+
<div class="icon icon-advance"></div>
|
150
|
+
</button>
|
151
|
+
</li>
|
152
|
+
<li>
|
153
|
+
<select class="playback-voices" name="playback-voice" style="display: none" title="Change read aloud voices">
|
154
|
+
</select>
|
155
|
+
</li>
|
156
|
+
</ul>
|
157
|
+
`);
|
158
|
+
|
159
|
+
$el.find('.BRcontrols').prepend(this.refs.$BRReadAloudToolbar);
|
160
|
+
|
161
|
+
const renderVoicesMenu = (voicesMenu) => {
|
162
|
+
voicesMenu.empty();
|
163
|
+
if (this.ttsEngine.getVoices().length > 1) {
|
164
|
+
voicesMenu.append(this.ttsEngine.getVoices().map(
|
165
|
+
voice =>
|
166
|
+
$(`<option value="${voice.voiceURI}">${voice.lang} - ${voice.name}</option>`)));
|
167
|
+
voicesMenu.val(this.ttsEngine.voice.voiceURI);
|
168
|
+
voicesMenu.show();
|
169
|
+
} else {
|
170
|
+
voicesMenu.hide();
|
171
|
+
}
|
172
|
+
};
|
173
|
+
const voicesMenu = this.refs.$BRReadAloudToolbar.find('[name=playback-voice]');
|
174
|
+
renderVoicesMenu(voicesMenu);
|
175
|
+
voicesMenu.on("change", ev => this.ttsEngine.setVoice(voicesMenu.val()));
|
176
|
+
this.ttsEngine.events.on('pause resume start', () => this.ttsUpdateState());
|
177
|
+
this.ttsEngine.events.on('voiceschanged', () => renderVoicesMenu(voicesMenu));
|
178
|
+
this.refs.$BRReadAloudToolbar.find('[name=play]').on("click", this.ttsPlayPause.bind(this));
|
179
|
+
this.refs.$BRReadAloudToolbar.find('[name=advance]').on("click", this.ttsJumpForward.bind(this));
|
180
|
+
this.refs.$BRReadAloudToolbar.find('[name=review]').on("click", this.ttsJumpBackward.bind(this));
|
181
|
+
const $rateSelector = this.refs.$BRReadAloudToolbar.find('select[name="playback-speed"]');
|
182
|
+
$rateSelector.on("change", ev => this.ttsEngine.setPlaybackRate(parseFloat($rateSelector.val())));
|
183
|
+
$(`<li>
|
184
|
+
<button class="BRicon read js-tooltip" title="${tooltips.readAloud}">
|
185
|
+
<div class="icon icon-read-aloud"></div>
|
186
|
+
<span class="tooltip">${tooltips.readAloud}</span>
|
187
|
+
</button>
|
188
|
+
</li>`).insertBefore($el.find('.BRcontrols .BRicon.zoom_out').closest('li'));
|
189
|
+
}
|
190
|
+
return $el;
|
191
|
+
};
|
192
|
+
})(BookReader.prototype.initNavbar);
|
193
|
+
|
194
|
+
// ttsToggle()
|
195
|
+
//______________________________________________________________________________
|
196
|
+
BookReader.prototype.ttsToggle = function () {
|
197
|
+
if (this.autoStop) this.autoStop();
|
198
|
+
if (this.ttsEngine.playing) {
|
199
|
+
this.ttsStop();
|
200
|
+
} else {
|
201
|
+
this.ttsStart();
|
202
|
+
}
|
203
|
+
};
|
204
|
+
|
205
|
+
// ttsStart(
|
206
|
+
//______________________________________________________________________________
|
207
|
+
BookReader.prototype.ttsStart = function (startTTSEngine = true) {
|
208
|
+
if (this.constModeThumb == this.mode)
|
209
|
+
this.switchMode(this.constMode1up);
|
210
|
+
|
211
|
+
this.refs.$BRReadAloudToolbar.addClass('visible');
|
212
|
+
this.$('.BRicon.read').addClass('unread active');
|
213
|
+
this.ttsSendAnalyticsEvent('Start');
|
214
|
+
if (startTTSEngine)
|
215
|
+
this.ttsEngine.start(this.currentIndex(), this.getNumLeafs());
|
216
|
+
};
|
217
|
+
|
218
|
+
BookReader.prototype.ttsJumpForward = function () {
|
219
|
+
if (this.ttsEngine.paused) {
|
220
|
+
this.ttsEngine.resume();
|
221
|
+
}
|
222
|
+
this.ttsEngine.jumpForward();
|
223
|
+
};
|
224
|
+
|
225
|
+
BookReader.prototype.ttsJumpBackward = function () {
|
226
|
+
if (this.ttsEngine.paused) {
|
227
|
+
this.ttsEngine.resume();
|
228
|
+
}
|
229
|
+
this.ttsEngine.jumpBackward();
|
230
|
+
};
|
231
|
+
|
232
|
+
BookReader.prototype.ttsUpdateState = function() {
|
233
|
+
const isPlaying = !(this.ttsEngine.paused || !this.ttsEngine.playing);
|
234
|
+
this.$('.read-aloud [name=play]').toggleClass('playing', isPlaying);
|
235
|
+
};
|
236
|
+
|
237
|
+
BookReader.prototype.ttsPlayPause = function() {
|
238
|
+
if (!this.ttsEngine.playing) {
|
239
|
+
this.ttsToggle();
|
240
|
+
} else {
|
241
|
+
this.ttsEngine.togglePlayPause();
|
242
|
+
this.ttsUpdateState(this.ttsEngine.paused);
|
243
|
+
}
|
244
|
+
};
|
245
|
+
|
246
|
+
// ttsStop()
|
247
|
+
//______________________________________________________________________________
|
248
|
+
BookReader.prototype.ttsStop = function () {
|
249
|
+
this.refs.$BRReadAloudToolbar.removeClass('visible');
|
250
|
+
this.$('.BRicon.read').removeClass('unread active');
|
251
|
+
this.ttsSendAnalyticsEvent('Stop');
|
252
|
+
this.ttsEngine.stop();
|
253
|
+
this.ttsRemoveHilites();
|
254
|
+
this.removeProgressPopup();
|
255
|
+
};
|
256
|
+
|
257
|
+
/**
|
258
|
+
* @param {PageChunk} chunk
|
259
|
+
* @return {PromiseLike<void>} returns once the flip is done
|
260
|
+
*/
|
261
|
+
BookReader.prototype.ttsBeforeChunkPlay = async function(chunk) {
|
262
|
+
await this.ttsMaybeFlipToIndex(chunk.leafIndex);
|
263
|
+
this.ttsHighlightChunk(chunk);
|
264
|
+
// This appears not to work; ttsMaybeFlipToIndex causes a scroll to the top of
|
265
|
+
// the active page :/ Disabling cause the extra scroll just adds an odd jitter.
|
266
|
+
// this.ttsScrollToChunk(chunk);
|
267
|
+
};
|
268
|
+
|
269
|
+
/**
|
270
|
+
* @param {PageChunk} chunk
|
271
|
+
*/
|
272
|
+
BookReader.prototype.ttsSendChunkFinishedAnalyticsEvent = function(chunk) {
|
273
|
+
this.ttsSendAnalyticsEvent('ChunkFinished-Words', approximateWordCount(chunk.text));
|
274
|
+
};
|
275
|
+
|
276
|
+
/**
|
277
|
+
* Flip the page if the provided leaf index is not visible
|
278
|
+
* @param {Number} leafIndex
|
279
|
+
* @return {PromiseLike<void>} resolves once the flip animation has completed
|
280
|
+
*/
|
281
|
+
BookReader.prototype.ttsMaybeFlipToIndex = function (leafIndex) {
|
282
|
+
const in2PageMode = this.constMode2up == this.mode;
|
283
|
+
let resolve = null;
|
284
|
+
const promise = new Promise(res => resolve = res);
|
285
|
+
|
286
|
+
if (!in2PageMode) {
|
287
|
+
this.jumpToIndex(leafIndex);
|
288
|
+
resolve();
|
289
|
+
} else {
|
290
|
+
const leafVisible = leafIndex == this.twoPage.currentIndexR || leafIndex == this.twoPage.currentIndexL;
|
291
|
+
if (leafVisible) {
|
292
|
+
resolve();
|
293
|
+
} else {
|
294
|
+
this.animationFinishedCallback = resolve;
|
295
|
+
const mustGoNext = leafIndex > Math.max(this.twoPage.currentIndexR, this.twoPage.currentIndexL);
|
296
|
+
if (mustGoNext) this.next();
|
297
|
+
else this.prev();
|
298
|
+
promise.then(this.ttsMaybeFlipToIndex.bind(this, leafIndex));
|
299
|
+
}
|
300
|
+
}
|
301
|
+
|
302
|
+
return promise;
|
303
|
+
};
|
304
|
+
|
305
|
+
/**
|
306
|
+
* @param {PageChunk} chunk
|
307
|
+
*/
|
308
|
+
BookReader.prototype.ttsHighlightChunk = function(chunk) {
|
309
|
+
// The poorly-named variable leafIndex
|
310
|
+
const pageIndex = chunk.leafIndex;
|
311
|
+
|
312
|
+
this.ttsRemoveHilites();
|
313
|
+
|
314
|
+
// group by index; currently only possible to have chunks on one page :/
|
315
|
+
this._ttsBoxesByIndex = {
|
316
|
+
[pageIndex]: chunk.lineRects.map(([l, b, r, t]) => ({l, r, b, t}))
|
317
|
+
};
|
318
|
+
|
319
|
+
// update any already created pages
|
320
|
+
for (const [pageIndexString, boxes] of Object.entries(this._ttsBoxesByIndex)) {
|
321
|
+
const pageIndex = parseFloat(pageIndexString);
|
322
|
+
const page = this._models.book.getPage(pageIndex);
|
323
|
+
const pageContainers = this.getActivePageContainerElementsForIndex(pageIndex);
|
324
|
+
pageContainers.forEach(container => renderBoxesInPageContainerLayer('ttsHiliteLayer', boxes, page, container));
|
325
|
+
}
|
326
|
+
};
|
327
|
+
|
328
|
+
/**
|
329
|
+
* @param {PageChunk} chunk
|
330
|
+
*/
|
331
|
+
BookReader.prototype.ttsScrollToChunk = function(chunk) {
|
332
|
+
if (this.constMode1up != this.mode) return;
|
333
|
+
|
334
|
+
$(`.pagediv${chunk.leafIndex} .ttsHiliteLayer rect`)[0]?.scrollIntoView();
|
335
|
+
};
|
336
|
+
|
337
|
+
// ttsRemoveHilites()
|
338
|
+
//______________________________________________________________________________
|
339
|
+
BookReader.prototype.ttsRemoveHilites = function () {
|
340
|
+
$(this.getActivePageContainerElements()).find('.ttsHiliteLayer').remove();
|
341
|
+
this._ttsBoxesByIndex = {};
|
342
|
+
};
|
343
|
+
|
344
|
+
/**
|
345
|
+
* @private
|
346
|
+
* Send an analytics event with an optional value. Also attaches the book's language.
|
347
|
+
* @param {string} action
|
348
|
+
* @param {number} [value]
|
349
|
+
*/
|
350
|
+
BookReader.prototype.ttsSendAnalyticsEvent = function(action, value) {
|
351
|
+
if (this.archiveAnalyticsSendEvent) {
|
352
|
+
const extraValues = {};
|
353
|
+
const mediaLanguage = this.ttsEngine.opts.bookLanguage;
|
354
|
+
if (mediaLanguage) extraValues.mediaLanguage = mediaLanguage;
|
355
|
+
this.archiveAnalyticsSendEvent('BRReadAloud', action, value, extraValues);
|
356
|
+
}
|
357
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
export const en = {
|
2
|
+
advance: 'Advance 10 seconds',
|
3
|
+
play: 'Play',
|
4
|
+
playbackSpeed: 'Playback speed',
|
5
|
+
readAloud: 'Read this book aloud',
|
6
|
+
review: 'Review 10 seconds',
|
7
|
+
};
|
8
|
+
|
9
|
+
export const es = {
|
10
|
+
advance: 'Avance 10 segundos',
|
11
|
+
play: 'Jugar',
|
12
|
+
playbackSpeed: 'Velocidad de reproducción',
|
13
|
+
readAloud: 'Lee este libro en voz alta',
|
14
|
+
review: 'Revisar 10 segundos',
|
15
|
+
};
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import langs from 'iso-language-codes/js/data.js';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Convert a EventTarget style event into a promise
|
5
|
+
* @param {EventTarget} target
|
6
|
+
* @param {string} eventType
|
7
|
+
* @return {Promise<Event>}
|
8
|
+
*/
|
9
|
+
export function promisifyEvent(target, eventType) {
|
10
|
+
return new Promise(res => {
|
11
|
+
const resolver = ev => {
|
12
|
+
target.removeEventListener(eventType, resolver);
|
13
|
+
res(ev);
|
14
|
+
};
|
15
|
+
target.addEventListener(eventType, resolver);
|
16
|
+
});
|
17
|
+
}
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Use regex to approximate word count in a string
|
21
|
+
* @param {string} text
|
22
|
+
* @return {number}
|
23
|
+
*/
|
24
|
+
export function approximateWordCount(text) {
|
25
|
+
const m = text.match(/\S+/g);
|
26
|
+
return m ? m.length : 0;
|
27
|
+
}
|
28
|
+
|
29
|
+
/**
|
30
|
+
* Waits the provided number of ms and then resolves with a promise
|
31
|
+
* @param {number} ms
|
32
|
+
* @return {Promise}
|
33
|
+
*/
|
34
|
+
export function sleep(ms) {
|
35
|
+
return new Promise(res => setTimeout(res, ms));
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* Checks whether the current browser is on android
|
40
|
+
* @param {string} [userAgent]
|
41
|
+
* @return {boolean}
|
42
|
+
*/
|
43
|
+
export function isAndroid(userAgent = navigator.userAgent) {
|
44
|
+
return /android/i.test(userAgent);
|
45
|
+
}
|
46
|
+
|
47
|
+
/**
|
48
|
+
* @typedef {string} ISO6391
|
49
|
+
* Language code in ISO 639-1 format. e.g. en, fr, zh
|
50
|
+
**/
|
51
|
+
|
52
|
+
/** Each lang is an array, with each index mapping to a different property */
|
53
|
+
const COLUMN_TO_LANG_INDEX = {
|
54
|
+
'Name': 0,
|
55
|
+
'Endonym': 1,
|
56
|
+
'ISO 639-1': 2,
|
57
|
+
'ISO 639-2/T': 3,
|
58
|
+
'ISO 639-2/B': 4
|
59
|
+
};
|
60
|
+
|
61
|
+
/**
|
62
|
+
* @param {string} language in some format
|
63
|
+
* @return {ISO6391?}
|
64
|
+
*/
|
65
|
+
export function toISO6391(language) {
|
66
|
+
if (!language) return null;
|
67
|
+
language = language.toLowerCase();
|
68
|
+
|
69
|
+
return searchForISO6391(language, ['ISO 639-1']) ||
|
70
|
+
searchForISO6391(language, ['ISO 639-2/B']) ||
|
71
|
+
searchForISO6391(language, ['ISO 639-2/T', 'Endonym', 'Name']);
|
72
|
+
}
|
73
|
+
|
74
|
+
/**
|
75
|
+
* Searches for the given long in the given columns.
|
76
|
+
* @param {string} language
|
77
|
+
* @param {Array<keyof COLUMN_TO_LANG_INDEX>} columnsToSearch
|
78
|
+
* @return {ISO6391?}
|
79
|
+
*/
|
80
|
+
function searchForISO6391(language, columnsToSearch) {
|
81
|
+
for (let i = 0; i < langs.length; i++) {
|
82
|
+
for (let colI = 0; colI < columnsToSearch.length; colI++) {
|
83
|
+
const column = columnsToSearch[colI];
|
84
|
+
const columnValue = langs[i][COLUMN_TO_LANG_INDEX[column]];
|
85
|
+
if (columnValue.split(', ').map(x => x.toLowerCase()).indexOf(language) != -1) {
|
86
|
+
return langs[i][COLUMN_TO_LANG_INDEX['ISO 639-1']];
|
87
|
+
}
|
88
|
+
}
|
89
|
+
}
|
90
|
+
return null;
|
91
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
/**
|
3
|
+
* Checks whether the current browser is a Chrome/Chromium browser
|
4
|
+
* Code from https://stackoverflow.com/a/4565120/2317712
|
5
|
+
* @param {string} [userAgent]
|
6
|
+
* @param {string} [vendor]
|
7
|
+
* @return {boolean}
|
8
|
+
*/
|
9
|
+
export function isChrome(userAgent = navigator.userAgent, vendor = navigator.vendor) {
|
10
|
+
return /chrome/i.test(userAgent) && /google inc/i.test(vendor);
|
11
|
+
}
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Checks whether the current browser is firefox
|
15
|
+
* @param {string} [userAgent]
|
16
|
+
* @return {boolean}
|
17
|
+
*/
|
18
|
+
export function isFirefox(userAgent = navigator.userAgent) {
|
19
|
+
return /firefox/i.test(userAgent);
|
20
|
+
}
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Checks whether the current browser is safari
|
24
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#Browser_Name
|
25
|
+
* @param {string} [userAgent]
|
26
|
+
* @return {boolean}
|
27
|
+
*/
|
28
|
+
export function isSafari(userAgent = navigator.userAgent) {
|
29
|
+
return /safari/i.test(userAgent) && !/chrome|chromium/i.test(userAgent);
|
30
|
+
}
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/**
|
2
|
+
* Wait until some time has passed before executing a callback.
|
3
|
+
*
|
4
|
+
* @param {Function} callback
|
5
|
+
* @param {Number} threshhold - in milliseconds
|
6
|
+
* @param {*} context - will be bound to callback as its "this" value
|
7
|
+
*/
|
8
|
+
class Debouncer {
|
9
|
+
constructor(callback, threshhold = 250, context = undefined) {
|
10
|
+
this.callback = callback;
|
11
|
+
this.threshhold = threshhold;
|
12
|
+
this.context = context;
|
13
|
+
this.deferTimeout = undefined;
|
14
|
+
}
|
15
|
+
|
16
|
+
execute() {
|
17
|
+
clearTimeout(this.deferTimeout);
|
18
|
+
this.deferTimeout = setTimeout(this.executeCallback.bind(this), this.threshhold);
|
19
|
+
}
|
20
|
+
|
21
|
+
executeCallback() {
|
22
|
+
this.callback.apply(this.context);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
export { Debouncer as default };
|
@@ -0,0 +1,67 @@
|
|
1
|
+
/**
|
2
|
+
* Helper module use to get, set and remove item from cookie
|
3
|
+
*
|
4
|
+
* See more:
|
5
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/document.cookie
|
6
|
+
* https://developer.mozilla.org/User:fusionchess
|
7
|
+
* https://github.com/madmurphy/cookies.js
|
8
|
+
* This framework is released under the GNU Public License, version 3 or later.
|
9
|
+
* http://www.gnu.org/licenses/gpl-3.0-standalone.html
|
10
|
+
*/
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Get specific key's value stored in cookie
|
14
|
+
*
|
15
|
+
* @param {string} sKey
|
16
|
+
*
|
17
|
+
* @returns {string|null}
|
18
|
+
*/
|
19
|
+
export function getItem(sKey) {
|
20
|
+
if (!sKey) return null;
|
21
|
+
|
22
|
+
return decodeURIComponent(
|
23
|
+
// eslint-disable-next-line no-useless-escape
|
24
|
+
document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null;
|
25
|
+
}
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Set specific key's value in cookie
|
29
|
+
*
|
30
|
+
* @param {string} sKey cookie name
|
31
|
+
* @param {string} sValue cookie value
|
32
|
+
* @param {string} [vEnd] expire|max-age
|
33
|
+
* @param {string} [sPath] path of current item
|
34
|
+
* @param {string} [sDomain] domain name
|
35
|
+
* @param {boolean} [bSecure]
|
36
|
+
*
|
37
|
+
* @returns {true}
|
38
|
+
*/
|
39
|
+
export function setItem(sKey, sValue, vEnd, sPath, sDomain, bSecure) {
|
40
|
+
document.cookie = encodeURIComponent(sKey) + '=' + encodeURIComponent(sValue)
|
41
|
+
+ (vEnd ? `; expires=${vEnd.toUTCString()}` : '')
|
42
|
+
+ (sDomain ? `; domain=${sDomain}` : '')
|
43
|
+
+ (sPath ? `; path=${sPath}` : '')
|
44
|
+
+ (bSecure ? `; secure` : '');
|
45
|
+
|
46
|
+
return true;
|
47
|
+
}
|
48
|
+
|
49
|
+
/**
|
50
|
+
* BROKEN Remove specific key's value from cookie
|
51
|
+
* @fixme hasItem isn't even implemented! This will always error.
|
52
|
+
* @param {string} sKey cookie name
|
53
|
+
* @param {string} [sPath] path of current item
|
54
|
+
* @param {string} [sDomain]
|
55
|
+
*
|
56
|
+
* @returns {boolean}
|
57
|
+
*/
|
58
|
+
export function removeItem(sKey, sPath, sDomain) {
|
59
|
+
// eslint-disable-next-line
|
60
|
+
if (!hasItem(sKey)) return false;
|
61
|
+
|
62
|
+
document.cookie = encodeURIComponent(sKey) + `=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
|
63
|
+
+ (sDomain ? `; domain=${sDomain}` : '')
|
64
|
+
+ (sPath ? `; path=${sPath}` : '');
|
65
|
+
|
66
|
+
return true;
|
67
|
+
}
|
@@ -0,0 +1,34 @@
|
|
1
|
+
/**
|
2
|
+
* @typedef {String} StringWithVars
|
3
|
+
* A template string with {{foo}} style variables
|
4
|
+
* Also supports filters, like {{bookPath|urlencode}} (See APPLY_FILTERS for the
|
5
|
+
* supported list of filters)
|
6
|
+
**/
|
7
|
+
|
8
|
+
/**
|
9
|
+
* @param {StringWithVars|String} template
|
10
|
+
* @param { {[varName: string]: { toString: () => string} } } vars
|
11
|
+
* @param { {[varName: string]: { toString: () => string} } } [overrides]
|
12
|
+
*/
|
13
|
+
export function applyVariables(template, vars, overrides = {}, possibleFilters = APPLY_FILTERS) {
|
14
|
+
return template?.replace(/\{\{([^}]*?)\}\}/g, ($0, $1) => {
|
15
|
+
if (!$1) return $0;
|
16
|
+
/** @type {string} */
|
17
|
+
const expression = $1;
|
18
|
+
const [varName, ...filterNames] = expression.split('|').map(x => x.trim());
|
19
|
+
const defined = varName in overrides || varName in vars;
|
20
|
+
|
21
|
+
// If it's not defined, don't expand it at all
|
22
|
+
if (!defined) return $0;
|
23
|
+
|
24
|
+
const value = varName in overrides ? overrides[varName]
|
25
|
+
: varName in vars ? vars[varName] : null;
|
26
|
+
const filters = filterNames.map(n => possibleFilters[n]);
|
27
|
+
return filters.reduce((acc, cur) => cur(acc), value && value.toString());
|
28
|
+
});
|
29
|
+
}
|
30
|
+
|
31
|
+
/** @type { {[filterName: String]:( string => string)} } */
|
32
|
+
export const APPLY_FILTERS = {
|
33
|
+
urlencode: encodeURIComponent,
|
34
|
+
};
|