@internetarchive/bookreader 5.0.0-8 → 5.0.0-80
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/.eslintrc.js +17 -15
- package/.github/workflows/node.js.yml +73 -10
- package/.github/workflows/npm-publish.yml +6 -20
- package/.testcaferc.js +10 -0
- package/BookReader/BookReader.css +398 -1133
- package/BookReader/BookReader.js +1 -1
- package/BookReader/BookReader.js.LICENSE.txt +20 -20
- package/BookReader/BookReader.js.map +1 -1
- package/BookReader/ia-bookreader-bundle.js +1782 -0
- package/BookReader/ia-bookreader-bundle.js.LICENSE.txt +7 -0
- package/BookReader/ia-bookreader-bundle.js.map +1 -0
- package/BookReader/icons/1up.svg +1 -1
- package/BookReader/icons/2up.svg +1 -1
- package/BookReader/icons/advance.svg +1 -1
- package/BookReader/icons/chevron-right.svg +1 -1
- package/BookReader/icons/close-circle-dark.svg +1 -1
- package/BookReader/icons/close-circle.svg +1 -1
- package/BookReader/icons/fullscreen.svg +1 -1
- package/BookReader/icons/fullscreen_exit.svg +1 -1
- package/BookReader/icons/hamburger.svg +1 -1
- package/BookReader/icons/left-arrow.svg +1 -1
- package/BookReader/icons/magnify-minus.svg +1 -1
- package/BookReader/icons/magnify-plus.svg +1 -1
- package/BookReader/icons/magnify.svg +1 -1
- package/BookReader/icons/pause.svg +1 -1
- package/BookReader/icons/play.svg +1 -1
- package/BookReader/icons/playback-speed.svg +1 -1
- package/BookReader/icons/read-aloud.svg +1 -1
- package/BookReader/icons/review.svg +1 -1
- package/BookReader/icons/thumbnails.svg +1 -1
- package/BookReader/icons/voice.svg +1 -0
- package/BookReader/icons/volume-full.svg +1 -1
- package/BookReader/images/BRicons.svg +3 -3
- package/BookReader/images/books_graphic.svg +1 -1
- package/BookReader/images/icon_book.svg +1 -1
- package/BookReader/images/icon_bookmark.svg +1 -1
- package/BookReader/images/icon_gear.svg +1 -1
- package/BookReader/images/icon_hamburger.svg +1 -1
- package/BookReader/images/icon_home.svg +1 -1
- package/BookReader/images/icon_info.svg +1 -1
- package/BookReader/images/icon_one_page.svg +1 -1
- package/BookReader/images/icon_pause.svg +1 -1
- package/BookReader/images/icon_play.svg +1 -1
- package/BookReader/images/icon_playback-rate.svg +1 -1
- package/BookReader/images/icon_search_button.svg +1 -1
- package/BookReader/images/icon_share.svg +1 -1
- package/BookReader/images/icon_skip-ahead.svg +1 -1
- package/BookReader/images/icon_skip-back.svg +1 -1
- package/BookReader/images/icon_speaker.svg +1 -1
- package/BookReader/images/icon_speaker_open.svg +1 -1
- package/BookReader/images/icon_thumbnails.svg +1 -1
- package/BookReader/images/icon_toc.svg +1 -1
- package/BookReader/images/icon_two_pages.svg +1 -1
- package/BookReader/images/marker_chap-off.svg +1 -1
- package/BookReader/images/marker_chap-on.svg +1 -1
- package/BookReader/images/marker_srch-on.svg +1 -1
- package/BookReader/jquery-3.js +2 -0
- package/BookReader/jquery-3.js.LICENSE.txt +24 -0
- package/BookReader/plugins/plugin.archive_analytics.js +1 -1
- package/BookReader/plugins/plugin.archive_analytics.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 +25 -1
- package/BookReader/plugins/plugin.chapters.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.chapters.js.map +1 -1
- package/BookReader/plugins/plugin.iframe.js +1 -1
- package/BookReader/plugins/plugin.iframe.js.map +1 -1
- package/BookReader/plugins/plugin.iiif.js +2 -0
- package/BookReader/plugins/plugin.iiif.js.map +1 -0
- package/BookReader/plugins/plugin.resume.js +1 -1
- package/BookReader/plugins/plugin.resume.js.map +1 -1
- package/BookReader/plugins/plugin.search.js +2 -1
- package/BookReader/plugins/plugin.search.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.search.js.map +1 -1
- package/BookReader/plugins/plugin.text_selection.js +2 -1
- package/BookReader/plugins/plugin.text_selection.js.LICENSE.txt +1 -0
- package/BookReader/plugins/plugin.text_selection.js.map +1 -1
- package/BookReader/plugins/plugin.tts.js +1 -1
- package/BookReader/plugins/plugin.tts.js.LICENSE.txt +2 -0
- package/BookReader/plugins/plugin.tts.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js +1 -1
- package/BookReader/plugins/plugin.vendor-fullscreen.js.map +1 -1
- package/BookReader/webcomponents-bundle.js +3 -0
- package/BookReader/webcomponents-bundle.js.LICENSE.txt +9 -0
- package/BookReader/webcomponents-bundle.js.map +1 -0
- package/BookReaderDemo/BookReaderDemo.css +18 -19
- package/BookReaderDemo/BookReaderJSAdvanced.js +0 -3
- package/BookReaderDemo/BookReaderJSAutoplay.js +4 -1
- package/BookReaderDemo/BookReaderJSSimple.js +1 -0
- package/BookReaderDemo/IADemoBr.js +147 -0
- package/BookReaderDemo/demo-advanced.html +2 -2
- package/BookReaderDemo/demo-autoplay.html +2 -3
- package/BookReaderDemo/demo-embed-iframe-src.html +2 -1
- package/BookReaderDemo/demo-fullscreen-mobile.html +3 -5
- package/BookReaderDemo/demo-fullscreen.html +2 -4
- package/BookReaderDemo/demo-iiif.html +99 -12
- package/BookReaderDemo/demo-internetarchive.html +214 -18
- package/BookReaderDemo/demo-multiple.html +2 -1
- package/BookReaderDemo/demo-preview-pages.html +2 -1
- package/BookReaderDemo/demo-simple.html +2 -1
- package/BookReaderDemo/demo-vendor-fullscreen.html +2 -4
- package/BookReaderDemo/ia-multiple-volumes-manifest.js +170 -0
- package/BookReaderDemo/immersion-1up.html +2 -2
- package/BookReaderDemo/immersion-mode.html +2 -4
- package/BookReaderDemo/toggle_controls.html +3 -2
- package/BookReaderDemo/view_mode.html +2 -1
- package/BookReaderDemo/viewmode-cycle.html +2 -3
- package/CHANGELOG.md +536 -33
- package/README.md +14 -1
- package/babel.config.js +20 -0
- package/codecov.yml +6 -0
- package/index.html +4 -1
- package/jsconfig.json +19 -0
- package/netlify.toml +9 -0
- package/package.json +70 -60
- package/renovate.json +52 -0
- package/scripts/preversion.js +0 -1
- package/src/BookNavigator/assets/bookmark-colors.js +1 -1
- package/src/BookNavigator/assets/button-base.js +4 -2
- package/src/BookNavigator/assets/ia-logo.js +17 -0
- package/src/BookNavigator/assets/icon_checkmark.js +1 -1
- package/src/BookNavigator/assets/icon_close.js +1 -1
- package/src/BookNavigator/book-navigator.js +590 -0
- package/src/BookNavigator/bookmarks/bookmark-button.js +3 -2
- package/src/BookNavigator/bookmarks/bookmark-edit.js +3 -4
- package/src/BookNavigator/bookmarks/bookmarks-list.js +2 -3
- package/src/BookNavigator/bookmarks/bookmarks-loginCTA.js +3 -8
- package/src/BookNavigator/bookmarks/bookmarks-provider.js +27 -17
- package/src/BookNavigator/bookmarks/ia-bookmarks.js +116 -67
- package/src/BookNavigator/delete-modal-actions.js +1 -1
- package/src/BookNavigator/downloads/downloads-provider.js +36 -21
- package/src/BookNavigator/downloads/downloads.js +41 -25
- package/src/BookNavigator/search/search-provider.js +49 -27
- package/src/BookNavigator/search/search-results.js +23 -9
- package/src/BookNavigator/sharing.js +27 -0
- package/src/BookNavigator/viewable-files.js +95 -0
- package/src/BookNavigator/visual-adjustments/visual-adjustments-provider.js +11 -10
- package/src/BookNavigator/visual-adjustments/visual-adjustments.js +3 -3
- package/src/BookReader/BookModel.js +64 -34
- package/src/BookReader/DragScrollable.js +233 -0
- package/src/BookReader/Mode1Up.js +56 -351
- package/src/BookReader/Mode1UpLit.js +388 -0
- package/src/BookReader/Mode2Up.js +73 -1318
- package/src/BookReader/Mode2UpLit.js +776 -0
- package/src/BookReader/ModeCoordinateSpace.js +29 -0
- package/src/BookReader/ModeSmoothZoom.js +312 -0
- package/src/BookReader/ModeThumb.js +18 -12
- package/src/BookReader/Navbar/Navbar.js +14 -40
- package/src/BookReader/PageContainer.js +81 -6
- package/src/BookReader/ReduceSet.js +1 -1
- package/src/BookReader/Toolbar/Toolbar.js +10 -37
- package/src/BookReader/events.js +2 -3
- package/src/BookReader/options.js +27 -2
- package/src/BookReader/utils/HTMLDimensionsCacher.js +44 -0
- package/src/BookReader/utils/ScrollClassAdder.js +31 -0
- package/src/BookReader/utils/SelectionObserver.js +45 -0
- package/src/BookReader/utils.js +118 -13
- package/src/BookReader.js +427 -1061
- package/src/assets/icons/magnify-minus.svg +3 -7
- package/src/assets/icons/magnify-plus.svg +3 -7
- package/src/assets/icons/voice.svg +1 -0
- package/src/css/BookReader.scss +1 -5
- package/src/css/_BRBookmarks.scss +1 -1
- package/src/css/_BRComponent.scss +1 -1
- package/src/css/_BRmain.scss +16 -3
- package/src/css/_BRnav.scss +12 -39
- package/src/css/_BRpages.scss +149 -40
- package/src/css/_BRsearch.scss +68 -25
- package/src/css/_BRtoolbar.scss +5 -5
- package/src/css/_TextSelection.scss +87 -27
- package/src/css/_colorbox.scss +2 -2
- package/src/css/_controls.scss +20 -7
- package/src/css/_icons.scss +1 -1
- package/src/ia-bookreader/ia-bookreader.js +224 -0
- package/src/plugins/plugin.archive_analytics.js +3 -3
- package/src/plugins/plugin.autoplay.js +5 -11
- package/src/plugins/plugin.chapters.js +237 -191
- package/src/plugins/plugin.iiif.js +151 -0
- package/src/plugins/plugin.resume.js +3 -3
- package/src/plugins/plugin.text_selection.js +464 -134
- package/src/plugins/plugin.vendor-fullscreen.js +4 -4
- package/src/plugins/search/plugin.search.js +142 -125
- package/src/plugins/search/utils.js +43 -0
- package/src/plugins/search/view.js +33 -58
- package/src/plugins/tts/AbstractTTSEngine.js +71 -40
- package/src/plugins/tts/FestivalTTSEngine.js +13 -14
- package/src/plugins/tts/PageChunk.js +15 -21
- package/src/plugins/tts/PageChunkIterator.js +8 -12
- package/src/plugins/tts/WebTTSEngine.js +87 -71
- package/src/plugins/tts/plugin.tts.js +96 -127
- package/src/plugins/tts/utils.js +15 -25
- package/src/plugins/url/UrlPlugin.js +191 -0
- package/src/plugins/{plugin.url.js → url/plugin.url.js} +45 -16
- package/src/util/browserSniffing.js +22 -0
- package/src/util/docCookies.js +21 -2
- package/tests/e2e/README.md +37 -0
- package/tests/e2e/autoplay.test.js +2 -2
- package/tests/e2e/base.test.js +8 -16
- package/tests/e2e/helpers/base.js +53 -48
- package/tests/e2e/helpers/debug.js +1 -1
- package/tests/e2e/helpers/params.js +17 -0
- package/tests/e2e/helpers/rightToLeft.js +8 -14
- package/tests/e2e/helpers/search.js +73 -0
- package/tests/e2e/models/Navigation.js +20 -37
- package/tests/e2e/rightToLeft.test.js +4 -5
- package/tests/e2e/viewmode.test.js +40 -33
- package/tests/jest/BookNavigator/book-navigator.test.js +661 -0
- package/tests/jest/BookNavigator/bookmarks/bookmark-button.test.js +43 -0
- package/tests/{karma → jest}/BookNavigator/bookmarks/bookmark-edit.test.js +25 -26
- package/tests/{karma → jest}/BookNavigator/bookmarks/bookmarks-list.test.js +41 -42
- package/tests/jest/BookNavigator/bookmarks/ia-bookmarks.test.js +45 -0
- package/tests/jest/BookNavigator/downloads/downloads-provider.test.js +67 -0
- package/tests/jest/BookNavigator/downloads/downloads.test.js +53 -0
- package/tests/jest/BookNavigator/search/search-provider.test.js +167 -0
- package/tests/{karma → jest}/BookNavigator/search/search-results.test.js +109 -60
- package/tests/jest/BookNavigator/sharing/sharing-provider.test.js +49 -0
- package/tests/jest/BookNavigator/viewable-files/viewable-files-provider.test.js +80 -0
- package/tests/jest/BookNavigator/visual-adjustments.test.js +200 -0
- package/tests/{BookReader → jest/BookReader}/BookModel.test.js +74 -14
- package/tests/jest/BookReader/BookReaderPublicFunctions.test.js +193 -0
- package/tests/{BookReader → jest/BookReader}/ImageCache.test.js +4 -4
- package/tests/jest/BookReader/Mode1UpLit.test.js +73 -0
- package/tests/jest/BookReader/Mode2Up.test.js +98 -0
- package/tests/jest/BookReader/Mode2UpLit.test.js +190 -0
- package/tests/jest/BookReader/ModeCoordinateSpace.test.js +16 -0
- package/tests/jest/BookReader/ModeSmoothZoom.test.js +218 -0
- package/tests/jest/BookReader/ModeThumb.test.js +71 -0
- package/tests/{BookReader → jest/BookReader}/Navbar/Navbar.test.js +10 -10
- package/tests/{BookReader → jest/BookReader}/PageContainer.test.js +88 -6
- package/tests/{BookReader → jest/BookReader}/ReduceSet.test.js +1 -1
- package/tests/{BookReader → jest/BookReader}/Toolbar/Toolbar.test.js +2 -2
- package/tests/jest/BookReader/utils/HTMLDimensionsCacher.test.js +59 -0
- package/tests/jest/BookReader/utils/ScrollClassAdder.test.js +49 -0
- package/tests/jest/BookReader/utils/SelectionObserver.test.js +57 -0
- package/tests/{BookReader → jest/BookReader}/utils/classes.test.js +1 -1
- package/tests/jest/BookReader/utils.test.js +229 -0
- package/tests/jest/BookReader.keyboard.test.js +190 -0
- package/tests/{BookReader.options.test.js → jest/BookReader.options.test.js} +9 -1
- package/tests/{BookReader.test.js → jest/BookReader.test.js} +26 -37
- package/tests/{plugins → jest/plugins}/plugin.archive_analytics.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.autoplay.test.js +4 -4
- package/tests/jest/plugins/plugin.chapters.test.js +195 -0
- package/tests/{plugins → jest/plugins}/plugin.iframe.test.js +2 -2
- package/tests/{plugins → jest/plugins}/plugin.resume.test.js +3 -3
- package/tests/jest/plugins/plugin.text_selection.test.js +317 -0
- package/tests/{plugins → jest/plugins}/plugin.vendor-fullscreen.test.js +2 -2
- package/tests/{plugins → jest/plugins}/search/plugin.search.test.js +25 -47
- package/tests/{plugins → jest/plugins}/search/plugin.search.view.test.js +39 -6
- package/tests/jest/plugins/search/utils.js +25 -0
- package/tests/jest/plugins/search/utils.test.js +29 -0
- package/tests/{plugins → jest/plugins}/tts/AbstractTTSEngine.test.js +29 -9
- package/tests/{plugins → jest/plugins}/tts/FestivalTTSEngine.test.js +4 -4
- package/tests/{plugins → jest/plugins}/tts/PageChunk.test.js +1 -1
- package/tests/{plugins → jest/plugins}/tts/PageChunkIterator.test.js +3 -3
- package/tests/{plugins → jest/plugins}/tts/WebTTSEngine.test.js +47 -1
- package/tests/{plugins → jest/plugins}/tts/utils.test.js +1 -60
- package/tests/jest/plugins/url/UrlPlugin.test.js +198 -0
- package/tests/{plugins → jest/plugins/url}/plugin.url.test.js +53 -14
- package/tests/jest/setup.js +3 -0
- package/tests/{util → jest/util}/browserSniffing.test.js +1 -1
- package/tests/jest/util/docCookies.test.js +24 -0
- package/tests/{util → jest/util}/strings.test.js +1 -1
- package/tests/{utils.js → jest/utils.js} +38 -0
- package/webpack.config.js +12 -6
- package/.babelrc +0 -12
- package/.dependabot/config.yml +0 -6
- package/.testcaferc.json +0 -5
- package/BookReader/bookreader-component-bundle.js +0 -1450
- package/BookReader/bookreader-component-bundle.js.LICENSE.txt +0 -38
- package/BookReader/bookreader-component-bundle.js.map +0 -1
- package/BookReader/jquery-1.10.1.js +0 -2
- package/BookReader/jquery-1.10.1.js.LICENSE.txt +0 -24
- package/BookReader/plugins/plugin.menu_toggle.js +0 -2
- package/BookReader/plugins/plugin.menu_toggle.js.map +0 -1
- package/BookReader/plugins/plugin.mobile_nav.js +0 -2
- package/BookReader/plugins/plugin.mobile_nav.js.map +0 -1
- package/BookReaderDemo/IIIFBookReader.js +0 -207
- package/BookReaderDemo/bookreader-template-bundle.js +0 -7178
- package/BookReaderDemo/demo-iiif.js +0 -26
- package/BookReaderDemo/demo-plugin-menu-toggle.html +0 -34
- package/karma.conf.js +0 -23
- package/src/BookNavigator/BookModel.js +0 -14
- package/src/BookNavigator/BookNavigator.js +0 -446
- package/src/BookNavigator/assets/book-loader.js +0 -27
- package/src/BookNavigator/br-fullscreen-mgr.js +0 -83
- package/src/BookNavigator/search/a-search-result.js +0 -55
- package/src/BookReader/DebugConsole.js +0 -54
- package/src/BookReaderComponent/BookReaderComponent.js +0 -112
- package/src/ItemNavigator/ItemNavigator.js +0 -376
- package/src/ItemNavigator/providers/sharing.js +0 -29
- package/src/css/_MobileNav.scss +0 -194
- package/src/dragscrollable-br.js +0 -261
- package/src/lit-wrapper.js +0 -2
- package/src/plugins/menu_toggle/plugin.menu_toggle.js +0 -324
- package/src/plugins/plugin.mobile_nav.js +0 -287
- package/tests/BookReader/BookReaderPublicFunctions.test.js +0 -171
- package/tests/BookReader/DebugConsole.test.js +0 -25
- package/tests/BookReader/Mode1Up.test.js +0 -164
- package/tests/BookReader/Mode2Up.test.js +0 -247
- package/tests/BookReader/utils.test.js +0 -109
- package/tests/e2e/helpers/desktopSearch.js +0 -72
- package/tests/e2e/helpers/mobileSearch.js +0 -85
- package/tests/e2e/ia-production/ia-prod-base.js +0 -17
- package/tests/karma/BookNavigator/book-navigator.test.js +0 -132
- package/tests/karma/BookNavigator/search/search-provider.test.js +0 -23
- package/tests/karma/BookNavigator/visual-adjustments.test.js +0 -201
- package/tests/plugins/menu_toggle/plugin.menu_toggle.test.js +0 -68
- package/tests/plugins/plugin.chapters.test.js +0 -130
- package/tests/plugins/plugin.mobile_nav.test.js +0 -66
- package/tests/plugins/plugin.text_selection.test.js +0 -203
- package/tests/util/docCookies.test.js +0 -15
@@ -0,0 +1,317 @@
|
|
1
|
+
import sinon from 'sinon';
|
2
|
+
|
3
|
+
import '@/src/BookReader.js';
|
4
|
+
import {
|
5
|
+
BookreaderWithTextSelection,
|
6
|
+
Cache,
|
7
|
+
genMap,
|
8
|
+
lookAroundWindow,
|
9
|
+
zip,
|
10
|
+
} from '@/src/plugins/plugin.text_selection.js';
|
11
|
+
|
12
|
+
|
13
|
+
/** @type {BookReader} */
|
14
|
+
|
15
|
+
// djvu.xml book infos copied from https://ia803103.us.archive.org/14/items/goodytwoshoes00newyiala/goodytwoshoes00newyiala_djvu.xml
|
16
|
+
const FAKE_XML_1WORD = `
|
17
|
+
<OBJECT data="file://localhost//tmp/derive/goodytwoshoes00newyiala//goodytwoshoes00newyiala.djvu" height="3192" type="image/x.djvu" usemap="goodytwoshoes00newyiala_0001.djvu" width="2454">
|
18
|
+
<PARAGRAPH>
|
19
|
+
<LINE>
|
20
|
+
<WORD coords="1216,2768,1256,2640">test</WORD>
|
21
|
+
</LINE>
|
22
|
+
</PARAGRAPH>
|
23
|
+
</OBJECT>`;
|
24
|
+
const FAKE_XML_MULT_WORDS = `
|
25
|
+
<OBJECT data="file://localhost//tmp/derive/goodytwoshoes00newyiala//goodytwoshoes00newyiala.djvu" height="3192" type="image/x.djvu" usemap="goodytwoshoes00newyiala_0001.djvu" width="2454">
|
26
|
+
<PARAGRAPH>
|
27
|
+
<LINE>
|
28
|
+
<WORD coords="1216,2768,1256,2640">test1</WORD>
|
29
|
+
<WORD coords="1400,2768,1500,2640">test2</WORD>
|
30
|
+
<WORD coords="1600,2768,1700,2640">test3</WORD>
|
31
|
+
</LINE>
|
32
|
+
</PARAGRAPH>
|
33
|
+
</OBJECT>`;
|
34
|
+
const FAKE_XML_MULT_LINES = `
|
35
|
+
<OBJECT data="file://localhost//tmp/derive/goodytwoshoes00newyiala//goodytwoshoes00newyiala.djvu" height="3192" type="image/x.djvu" usemap="goodytwoshoes00newyiala_0001.djvu" width="2454">
|
36
|
+
<PARAGRAPH>
|
37
|
+
<LINE>
|
38
|
+
<WORD coords="119,2050,230,2014" x-confidence="29">way </WORD>
|
39
|
+
<WORD coords="230,2038,320,2002" x-confidence="30">can </WORD>
|
40
|
+
<WORD coords="320,2039,433,2002" x-confidence="28">false </WORD>
|
41
|
+
<WORD coords="433,2051,658,2003" x-confidence="29">judgment </WORD>
|
42
|
+
<WORD coords="658,2039,728,2002" x-confidence="30">be </WORD>
|
43
|
+
<WORD coords="658,2039,728,2002" x-confidence="30">-</WORD>
|
44
|
+
<WORD coords="728,2039,939,2001" x-confidence="29">formed. </WORD>
|
45
|
+
<WORD coords="939,2039,1087,2001" x-confidence="29">There </WORD>
|
46
|
+
<WORD coords="1087,2039,1187,2002" x-confidence="29">still </WORD>
|
47
|
+
<WORD coords="1187,2038,1370,2003" x-confidence="29">remains </WORD>
|
48
|
+
<WORD coords="1370,2037,1433,2014" x-confidence="28">an-</WORD>
|
49
|
+
</LINE>
|
50
|
+
<LINE>
|
51
|
+
<WORD coords="244,2099,370,2063" x-confidence="29">other mode </WORD>
|
52
|
+
<WORD coords="370,2100,427,2064" x-confidence="29">in </WORD>
|
53
|
+
<WORD coords="427,2100,566,2063" x-confidence="29">which </WORD>
|
54
|
+
<WORD coords="566,2100,670,2063" x-confidence="29">false </WORD>
|
55
|
+
<WORD coords="670,2112,907,2063" x-confidence="29">judgments </WORD>
|
56
|
+
<WORD coords="907,2112,1006,2064" x-confidence="29">may </WORD>
|
57
|
+
<WORD coords="1006,2100,1071,2063" x-confidence="29">be </WORD>
|
58
|
+
<WORD coords="1071,2100,1266,2062" x-confidence="29">formed. </WORD>
|
59
|
+
<WORD coords="1266,2110,1435,2062" x-confidence="29">Suppose</WORD>
|
60
|
+
</LINE>
|
61
|
+
<LINE>
|
62
|
+
<WORD coords="118,2160,217,2123" x-confidence="29">that </WORD>
|
63
|
+
<WORD coords="217,2160,289,2124" x-confidence="29">we </WORD>
|
64
|
+
<WORD coords="289,2160,400,2124" x-confidence="29">have </WORD>
|
65
|
+
<WORD coords="400,2160,456,2124" x-confidence="30">in </WORD>
|
66
|
+
<WORD coords="456,2161,542,2136" x-confidence="29">our </WORD>
|
67
|
+
<WORD coords="542,2161,660,2124" x-confidence="29">souls </WORD>
|
68
|
+
<WORD coords="660,2160,700,2136" x-confidence="29">a </WORD>
|
69
|
+
<WORD coords="700,2160,847,2129" x-confidence="28">waxen </WORD>
|
70
|
+
<WORD coords="847,2160,983,2123" x-confidence="29">tablet </WORD>
|
71
|
+
<WORD coords="983,2160,1045,2124" x-confidence="29">of </WORD>
|
72
|
+
<WORD coords="1045,2160,1211,2124" x-confidence="29">various </WORD>
|
73
|
+
<WORD coords="1211,2171,1398,2122" x-confidence="29">qualities </WORD>
|
74
|
+
<WORD coords="1398,2157,1434,2122" x-confidence="29">lastWord</WORD>
|
75
|
+
</LINE>
|
76
|
+
</PARAGRAPH>
|
77
|
+
</OBJECT>`;
|
78
|
+
const FAKE_XML_5COORDS = `
|
79
|
+
<OBJECT data="file://localhost//tmp/derive/goodytwoshoes00newyiala//goodytwoshoes00newyiala.djvu" height="3192" type="image/x.djvu" usemap="goodytwoshoes00newyiala_0001.djvu" width="2454">
|
80
|
+
<PARAGRAPH>
|
81
|
+
<LINE>
|
82
|
+
<WORD coords="1216,2768,1256,2640,2690">test</WORD>
|
83
|
+
</LINE>
|
84
|
+
</PARAGRAPH>
|
85
|
+
</OBJECT>`;
|
86
|
+
const FAKE_XML_EMPTY = '';
|
87
|
+
|
88
|
+
describe("Generic tests", () => {
|
89
|
+
document.body.innerHTML = '<div id="BookReader">';
|
90
|
+
const br = new BookreaderWithTextSelection({
|
91
|
+
data: [
|
92
|
+
[
|
93
|
+
{ width: 800, height: 1200,
|
94
|
+
uri: '//archive.org/download/BookReader/img/page001.jpg' },
|
95
|
+
],
|
96
|
+
[
|
97
|
+
{ width: 800, height: 1200,
|
98
|
+
uri: '//archive.org/download/BookReader/img/page002.jpg' },
|
99
|
+
{ width: 800, height: 1200,
|
100
|
+
uri: '//archive.org/download/BookReader/img/page003.jpg' },
|
101
|
+
],
|
102
|
+
[
|
103
|
+
{ width: 800, height: 1200,
|
104
|
+
uri: '//archive.org/download/BookReader/img/page004.jpg' },
|
105
|
+
{ width: 800, height: 1200,
|
106
|
+
uri: '//archive.org/download/BookReader/img/page005.jpg' },
|
107
|
+
]
|
108
|
+
],
|
109
|
+
});
|
110
|
+
br.init();
|
111
|
+
|
112
|
+
afterEach(() => {
|
113
|
+
sinon.restore();
|
114
|
+
$('.BRtextLayer').remove();
|
115
|
+
});
|
116
|
+
|
117
|
+
test("_createPageContainer overridden function still creates a BRpagecontainer element", () => {
|
118
|
+
const spy = sinon.spy(br.textSelectionPlugin, 'createTextLayer');
|
119
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
120
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_1WORD, "text/xml")));
|
121
|
+
const container = br._createPageContainer(1, {});
|
122
|
+
expect(container).toBeTruthy();
|
123
|
+
expect(spy.callCount).toBe(1);
|
124
|
+
});
|
125
|
+
|
126
|
+
// test loading first object from sample data
|
127
|
+
test("_createPageContainer handles index 0", () => {
|
128
|
+
const spy = sinon.spy(br.textSelectionPlugin, 'createTextLayer');
|
129
|
+
br._createPageContainer(0, {});
|
130
|
+
expect(spy.callCount).toBe(1);
|
131
|
+
});
|
132
|
+
|
133
|
+
// test loading last object from sample data
|
134
|
+
test("_createPageContainer handles index -1", () => {
|
135
|
+
const spy = sinon.spy(br.textSelectionPlugin, 'createTextLayer');
|
136
|
+
br._createPageContainer(-1, {});
|
137
|
+
expect(spy.callCount).toBe(0);
|
138
|
+
});
|
139
|
+
|
140
|
+
test("createTextLayer will render the last page and create text layer properly", async () => {
|
141
|
+
const $container = br.refs.$brContainer;
|
142
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
143
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_1WORD, "text/xml")));
|
144
|
+
const pageIndex = br.data.length - 1;
|
145
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: pageIndex, width: 100, height: 100 }});
|
146
|
+
expect($container.find(".BRtextLayer").length).toBe(1);
|
147
|
+
expect($container.find("p").length).toBe(1);
|
148
|
+
});
|
149
|
+
|
150
|
+
test("createTextLayer will not create text layer if there are too many words", async () => {
|
151
|
+
const $container = br.refs.$brContainer;
|
152
|
+
const xml = FAKE_XML_1WORD.replace(/<WORD.*<\/WORD>/, FAKE_XML_1WORD.match(/<WORD.*<\/WORD>/)[0].repeat(3000));
|
153
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
154
|
+
.returns($(new DOMParser().parseFromString(xml, "text/xml")));
|
155
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 0, width: 100, height: 100 }});
|
156
|
+
expect($container.find(".BRtextLayer").length).toBe(0);
|
157
|
+
expect($container.find("p").length).toBe(0);
|
158
|
+
expect($container.find(".BRwordElement").length).toBe(0);
|
159
|
+
});
|
160
|
+
|
161
|
+
test("createTextLayer creates text layer with paragraph with 1 word element", async () => {
|
162
|
+
const $container = br.refs.$brContainer;
|
163
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
164
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_1WORD, "text/xml")));
|
165
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 1, width: 100, height: 100 }});
|
166
|
+
expect($container.find(".BRtextLayer").length).toBe(1);
|
167
|
+
expect($container.find("p").length).toBe(1);
|
168
|
+
expect($container.find(".BRwordElement").length).toBe(1);
|
169
|
+
expect($container.find(".BRwordElement").text()).toBe("test");
|
170
|
+
});
|
171
|
+
|
172
|
+
test("createTextLayer creates text layer with paragraph with multiple word elements", async () => {
|
173
|
+
const $container = br.refs.$brContainer;
|
174
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
175
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_MULT_WORDS, "text/xml")));
|
176
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 2, width: 100, height: 100 }});
|
177
|
+
expect($container.find(".BRtextLayer").length).toBe(1);
|
178
|
+
expect($container.find("p").length).toBe(1);
|
179
|
+
expect($container.find(".BRwordElement").length).toBe(3);
|
180
|
+
expect($container.find(".BRspace").length).toBe(2);
|
181
|
+
});
|
182
|
+
|
183
|
+
test("createTextLayer creates text layer with paragraph with word with 5 params coordinates", async () => {
|
184
|
+
const $container = br.refs.$brContainer;
|
185
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
186
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_5COORDS, "text/xml")));
|
187
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 3, width: 100, height: 100 }});
|
188
|
+
expect($container.find(".BRtextLayer").length).toBe(1);
|
189
|
+
expect($container.find("p").length).toBe(1);
|
190
|
+
expect($container.find(".BRwordElement").length).toBe(1);
|
191
|
+
});
|
192
|
+
|
193
|
+
test("createTextLayer handles multiple lines", async () => {
|
194
|
+
const $container = br.refs.$brContainer;
|
195
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
196
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_MULT_LINES, "text/xml")));
|
197
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 3, width: 100, height: 100 }});
|
198
|
+
expect($container.find(".BRtextLayer").length).toBe(1);
|
199
|
+
expect($container.find("p").length).toBe(1);
|
200
|
+
expect($container.find(".BRlineElement").length).toBe(3);
|
201
|
+
// Adds space at end of line; except last line/hyphens
|
202
|
+
expect($container.find("p").text()).toMatch(/another/);
|
203
|
+
expect($container.find("p").text()).toMatch(/Suppose /);
|
204
|
+
expect($container.find("p").text()).toMatch(/lastWord$/);
|
205
|
+
expect($container.find("p > br").length).toBe(1);
|
206
|
+
});
|
207
|
+
|
208
|
+
test("createTextLayer repairs trailing hyphens", async () => {
|
209
|
+
const $container = br.refs.$brContainer;
|
210
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
211
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_MULT_LINES, "text/xml")));
|
212
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 3, width: 100, height: 100 }});
|
213
|
+
|
214
|
+
expect($container.find(".BRwordElement--hyphen").length).toBe(1);
|
215
|
+
expect($container.find(".BRwordElement--hyphen").closest(".BRlineElement").text().endsWith(' ')).toBe(false);
|
216
|
+
expect($container.find(".BRwordElement--hyphen").closest(".BRlineElement").text().endsWith('-')).toBe(false);
|
217
|
+
});
|
218
|
+
|
219
|
+
test("createTextLayer can handle empty xml", async () => {
|
220
|
+
const $container = br.refs.$brContainer;
|
221
|
+
sinon.stub(br.textSelectionPlugin, "getPageText")
|
222
|
+
.returns($(new DOMParser().parseFromString(FAKE_XML_EMPTY, "text/xml")));
|
223
|
+
await br.textSelectionPlugin.createTextLayer({ $container, page: { index: 4, width: 100, height: 100 }});
|
224
|
+
expect($container.find(".BRtextLayer").length).toBe(1);
|
225
|
+
expect($container.find("p").length).toBe(0);
|
226
|
+
expect($container.find(".BRwordElement").length).toBe(0);
|
227
|
+
});
|
228
|
+
|
229
|
+
const LONG_PRESS_DURATION = 500;
|
230
|
+
test("calling stopPageFlip does not allow long click to flip the page", () => {
|
231
|
+
const $container = br.refs.$brContainer;
|
232
|
+
br.textSelectionPlugin.stopPageFlip($container);
|
233
|
+
const currIndex = br.currentIndex();
|
234
|
+
$container.find("BRwordElement").trigger("mousedown");
|
235
|
+
// Waits for long press
|
236
|
+
setTimeout(() => {
|
237
|
+
$container.find("BRwordElement").trigger("mousedown");
|
238
|
+
// Waits for flipping animation
|
239
|
+
setTimeout(() => {
|
240
|
+
expect(br.currentIndex()).toBe(currIndex);
|
241
|
+
}, 2000);
|
242
|
+
}, LONG_PRESS_DURATION);
|
243
|
+
});
|
244
|
+
});
|
245
|
+
|
246
|
+
describe("Cache", () => {
|
247
|
+
test('Adding works', () => {
|
248
|
+
const c = new Cache(10);
|
249
|
+
c.add(35);
|
250
|
+
expect(c.entries).toEqual([35]);
|
251
|
+
});
|
252
|
+
|
253
|
+
test('Size does not grow beyond limit', () => {
|
254
|
+
const c = new Cache(2);
|
255
|
+
c.add(35);
|
256
|
+
c.add(32);
|
257
|
+
c.add(12);
|
258
|
+
c.add(11);
|
259
|
+
c.add(112);
|
260
|
+
expect(c.entries).toHaveLength(2);
|
261
|
+
});
|
262
|
+
|
263
|
+
test('Oldest evicted first', () => {
|
264
|
+
const c = new Cache(2);
|
265
|
+
c.add(35);
|
266
|
+
c.add(32);
|
267
|
+
c.add(12);
|
268
|
+
c.add(12);
|
269
|
+
c.add(10);
|
270
|
+
expect(c.entries).toEqual([12, 10]);
|
271
|
+
});
|
272
|
+
});
|
273
|
+
|
274
|
+
describe('genMap', () => {
|
275
|
+
test('handles empty', () => {
|
276
|
+
expect(Array.from(genMap([], x => x ** 2))).toEqual([]);
|
277
|
+
});
|
278
|
+
|
279
|
+
test('handles non-empty', () => {
|
280
|
+
expect(Array.from(genMap([1,2,3], x => x ** 2))).toEqual([1,4,9]);
|
281
|
+
});
|
282
|
+
});
|
283
|
+
|
284
|
+
describe('lookAroundWindow', () => {
|
285
|
+
test('handles empty', () => {
|
286
|
+
expect(Array.from(lookAroundWindow([]))).toEqual([]);
|
287
|
+
});
|
288
|
+
|
289
|
+
test('handles smaller than window', () => {
|
290
|
+
expect(Array.from(lookAroundWindow([1]))).toEqual([[undefined, 1, undefined]]);
|
291
|
+
expect(Array.from(lookAroundWindow([1, 2]))).toEqual([[undefined, 1, 2], [1, 2, undefined]]);
|
292
|
+
expect(Array.from(lookAroundWindow([1, 2, 3]))).toEqual([[undefined, 1, 2], [1, 2, 3], [2, 3, undefined]]);
|
293
|
+
});
|
294
|
+
|
295
|
+
test('handles larger than window', () => {
|
296
|
+
expect(Array.from(lookAroundWindow([1, 2, 3, 4]))).toEqual([
|
297
|
+
[undefined, 1, 2],
|
298
|
+
[1, 2, 3],
|
299
|
+
[2, 3, 4],
|
300
|
+
[3, 4, undefined],
|
301
|
+
]);
|
302
|
+
});
|
303
|
+
});
|
304
|
+
|
305
|
+
describe('zip', () => {
|
306
|
+
test('handles empty', () => {
|
307
|
+
expect(Array.from(zip([], []))).toEqual([]);
|
308
|
+
});
|
309
|
+
|
310
|
+
test('uneven throws error', () => {
|
311
|
+
expect(() => Array.from(zip([1], [2, 3]))).toThrow();
|
312
|
+
});
|
313
|
+
|
314
|
+
test('handles even', () => {
|
315
|
+
expect(Array.from(zip([1, 2], [3, 4]))).toEqual([[1, 3], [2, 4]]);
|
316
|
+
});
|
317
|
+
});
|
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
|
-
import BookReader from '
|
3
|
-
import * as util from '
|
2
|
+
import BookReader from '@/src/BookReader.js';
|
3
|
+
import * as util from '@/src/plugins/plugin.vendor-fullscreen.js';
|
4
4
|
|
5
5
|
let br;
|
6
6
|
beforeEach(() => {
|
@@ -1,9 +1,9 @@
|
|
1
|
+
import BookReader from '@/src/BookReader.js';
|
2
|
+
import '@/src/plugins/search/plugin.search.js';
|
3
|
+
import { deepCopy } from '../../utils.js';
|
4
|
+
import { DUMMY_RESULTS } from './utils.js';
|
1
5
|
|
2
|
-
|
3
|
-
import '../../../src/plugins/plugin.mobile_nav.js';
|
4
|
-
import '../../../src/plugins/search/plugin.search.js';
|
5
|
-
|
6
|
-
jest.mock('../../../src/plugins/search/view.js');
|
6
|
+
jest.mock('@/src/plugins/search/view.js');
|
7
7
|
|
8
8
|
let br;
|
9
9
|
const namespace = 'BookReader:';
|
@@ -18,27 +18,7 @@ beforeEach(() => {
|
|
18
18
|
$.ajax = jest.fn().mockImplementation(() => {
|
19
19
|
// return from:
|
20
20
|
// `https://ia800304.us.archive.org/fulltext/inside.php?item_id=adventuresofoli00dick&doc=adventuresofoli00dick&path=/30/items/adventuresofoli00dick&q=child&callback=<serialized jQ CB>`
|
21
|
-
return Promise.resolve(
|
22
|
-
ia: "adventuresofoli00dick",
|
23
|
-
q: "child",
|
24
|
-
indexed: true,
|
25
|
-
page_count: 644,
|
26
|
-
body_length: 666,
|
27
|
-
leaf0_missing: false,
|
28
|
-
matches: [{
|
29
|
-
text: 'For a long; time after it was ushered into this world of sorrow and trouble, by the parish surgeon, it remained a matter of considerable doubt wliether the {{{child}}} Avould survi^ e to bear any name at all; in which case it is somewhat more than probable that these memoirs would never have appeared; or, if they had, that being comprised within a couple of pages, they would have possessed the inestimable meiit of being the most concise and faithful specimen of biography, extant in the literature of any age or country.',
|
30
|
-
par: [{
|
31
|
-
boxes: [{r: 1221, b: 2121, t: 2075, page: 37, l: 1107}],
|
32
|
-
b: 2535,
|
33
|
-
t: 1942,
|
34
|
-
page_width: 1790,
|
35
|
-
r: 1598,
|
36
|
-
l: 50,
|
37
|
-
page_height: 2940,
|
38
|
-
page: 37
|
39
|
-
}]
|
40
|
-
}]
|
41
|
-
});
|
21
|
+
return Promise.resolve(deepCopy(DUMMY_RESULTS));
|
42
22
|
});
|
43
23
|
|
44
24
|
$.fn.trigger = jest.fn();
|
@@ -102,7 +82,7 @@ describe('Plugin: Search', () => {
|
|
102
82
|
expect(br.search.mock.calls[0][1])
|
103
83
|
.toHaveProperty('goToFirstResult', true);
|
104
84
|
expect(br.search.mock.calls[0][1])
|
105
|
-
.toHaveProperty('suppressFragmentChange',
|
85
|
+
.toHaveProperty('suppressFragmentChange', false);
|
106
86
|
});
|
107
87
|
|
108
88
|
test('calling `search` fires ajax call`', () => {
|
@@ -117,28 +97,30 @@ describe('Plugin: Search', () => {
|
|
117
97
|
expect(triggeredEvents()).toContain(`${namespace}SearchStarted`);
|
118
98
|
});
|
119
99
|
|
120
|
-
test('
|
100
|
+
test('SearchStarted event fires and should go to first result', () => {
|
121
101
|
br.init();
|
122
|
-
|
123
|
-
|
124
|
-
expect(triggeredEvents()).toContain(`${namespace}SearchCallback`);
|
125
|
-
});
|
102
|
+
br.search('foo', { goToFirstResult: true});
|
103
|
+
expect(br.options.goToFirstResult).toBeTruthy();
|
126
104
|
});
|
127
105
|
|
128
|
-
test('
|
106
|
+
test('SearchCallback event fires when AJAX search returns results', async () => {
|
107
|
+
br.init();
|
108
|
+
await br.search('foo');
|
109
|
+
expect(triggeredEvents()).toContain(`${namespace}SearchCallback`);
|
110
|
+
});
|
111
|
+
|
112
|
+
test('SearchCallbackError event fires when AJAX search returns error', async () => {
|
129
113
|
$.ajax = jest.fn().mockImplementation(() => {
|
130
114
|
return Promise.resolve({
|
131
115
|
error: true,
|
132
116
|
});
|
133
117
|
});
|
134
118
|
br.init();
|
135
|
-
|
136
|
-
|
137
|
-
expect(triggeredEvents()).toContain(`${namespace}SearchCallbackError`);
|
138
|
-
});
|
119
|
+
await br.search('foo');
|
120
|
+
expect(triggeredEvents()).toContain(`${namespace}SearchCallbackError`);
|
139
121
|
});
|
140
122
|
|
141
|
-
test('SearchCallbackNotIndexed event fires when AJAX search returns false indexed value', () => {
|
123
|
+
test('SearchCallbackNotIndexed event fires when AJAX search returns false indexed value', async () => {
|
142
124
|
$.ajax = jest.fn().mockImplementation(() => {
|
143
125
|
return Promise.resolve({
|
144
126
|
matches: [],
|
@@ -146,22 +128,18 @@ describe('Plugin: Search', () => {
|
|
146
128
|
});
|
147
129
|
});
|
148
130
|
br.init();
|
149
|
-
|
150
|
-
|
151
|
-
expect(triggeredEvents()).toContain(`${namespace}SearchCallbackBookNotIndexed`);
|
152
|
-
});
|
131
|
+
await br.search('foo');
|
132
|
+
expect(triggeredEvents()).toContain(`${namespace}SearchCallbackBookNotIndexed`);
|
153
133
|
});
|
154
134
|
|
155
|
-
test('SearchCallbackEmpty event fires when AJAX search returns no matches', () => {
|
135
|
+
test('SearchCallbackEmpty event fires when AJAX search returns no matches', async () => {
|
156
136
|
$.ajax = jest.fn().mockImplementation(() => {
|
157
137
|
return Promise.resolve({
|
158
138
|
matches: [],
|
159
139
|
});
|
160
140
|
});
|
161
141
|
br.init();
|
162
|
-
|
163
|
-
|
164
|
-
expect(triggeredEvents()).toContain(`${namespace}SearchCallbackEmpty`);
|
165
|
-
});
|
142
|
+
await br.search('foo');
|
143
|
+
expect(triggeredEvents()).toContain(`${namespace}SearchCallbackEmpty`);
|
166
144
|
});
|
167
145
|
});
|
@@ -1,8 +1,8 @@
|
|
1
1
|
|
2
|
-
import BookReader from '
|
3
|
-
import '
|
4
|
-
import '
|
5
|
-
import '
|
2
|
+
import BookReader from '@/src/BookReader.js';
|
3
|
+
import '@/src/plugins/search/plugin.search.js';
|
4
|
+
import { marshallSearchResults } from '@/src/plugins/search/utils.js';
|
5
|
+
import '@/src/plugins/search/view.js';
|
6
6
|
|
7
7
|
let br;
|
8
8
|
const namespace = 'BookReader:';
|
@@ -27,6 +27,31 @@ const results = {
|
|
27
27
|
}]
|
28
28
|
}]
|
29
29
|
};
|
30
|
+
|
31
|
+
marshallSearchResults(results, () => '', '{{{', '}}}');
|
32
|
+
const resultWithScript = {
|
33
|
+
ia: "adventuresofoli00dick",
|
34
|
+
q: "child",
|
35
|
+
indexed: true,
|
36
|
+
page_count: 644,
|
37
|
+
body_length: 666,
|
38
|
+
leaf0_missing: false,
|
39
|
+
matches: [{
|
40
|
+
text: 'foo bar <script>alert(1);</script> {{{keyword}}} baz',
|
41
|
+
par: [{
|
42
|
+
boxes: [{r: 1221, b: 2121, t: 2075, page: 37, l: 1107}],
|
43
|
+
b: 2535,
|
44
|
+
t: 1942,
|
45
|
+
page_width: 1790,
|
46
|
+
r: 1598,
|
47
|
+
l: 50,
|
48
|
+
page_height: 2940,
|
49
|
+
page: 37
|
50
|
+
}]
|
51
|
+
}]
|
52
|
+
};
|
53
|
+
|
54
|
+
marshallSearchResults(resultWithScript, () => '', '{{{', '}}}');
|
30
55
|
beforeEach(() => {
|
31
56
|
$.ajax = jest.fn().mockImplementation(() => {
|
32
57
|
// return from:
|
@@ -63,14 +88,14 @@ describe('View: Plugin: Search', () => {
|
|
63
88
|
|
64
89
|
expect(br.searchView.dom.searchNavigation).toBeUndefined();
|
65
90
|
|
66
|
-
br.searchView.handleSearchCallback(event, { results, options})
|
91
|
+
br.searchView.handleSearchCallback(event, { results, options});
|
67
92
|
expect(br.searchView.dom.searchNavigation).toBeDefined();
|
68
93
|
});
|
69
94
|
test('has controls', () => {
|
70
95
|
br.init();
|
71
96
|
const event = new CustomEvent(`${namespace}SearchCallback`);
|
72
97
|
const options = { goToFirstResult: false };
|
73
|
-
br.searchView.handleSearchCallback(event, { results, options})
|
98
|
+
br.searchView.handleSearchCallback(event, { results, options});
|
74
99
|
|
75
100
|
const searchResultsNav = document.querySelector('.BRsearch-navigation');
|
76
101
|
expect(searchResultsNav).toBeDefined();
|
@@ -81,6 +106,14 @@ describe('View: Plugin: Search', () => {
|
|
81
106
|
expect(searchResultsNav.querySelector('.prev')).toBeDefined();
|
82
107
|
expect(searchResultsNav.querySelector('.next')).toBeDefined();
|
83
108
|
});
|
109
|
+
test('disallows xss from search results', () => {
|
110
|
+
br.init();
|
111
|
+
const event = new CustomEvent(`${namespace}SearchCallback`);
|
112
|
+
const options = { goToFirstResult: false };
|
113
|
+
br.searchView.handleSearchCallback(event, { results: resultWithScript, options });
|
114
|
+
|
115
|
+
expect(br.searchView.dom.searchNavigation.parent().html()).not.toContain('<script>alert(1);</script>');
|
116
|
+
});
|
84
117
|
|
85
118
|
describe('Click events handlers', () => {
|
86
119
|
it('triggers custom event when toggling side menu', () => {
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { marshallSearchResults } from "@/src/plugins/search/utils";
|
2
|
+
|
3
|
+
export const DUMMY_RESULTS = {
|
4
|
+
ia: "adventuresofoli00dick",
|
5
|
+
q: "child",
|
6
|
+
indexed: true,
|
7
|
+
page_count: 644,
|
8
|
+
body_length: 666,
|
9
|
+
leaf0_missing: false,
|
10
|
+
matches: [{
|
11
|
+
text: 'For a long; time after it was ushered into this world of sorrow and trouble, by the parish surgeon, it remained a matter of considerable doubt wliether the {{{child}}} Avould survi^ e to bear any name at all; in which case it is somewhat more than probable that these memoirs would never have appeared; or, if they had, that being comprised within a couple of pages, they would have possessed the inestimable meiit of being the most concise and faithful specimen of biography, extant in the literature of any age or country.',
|
12
|
+
par: [{
|
13
|
+
boxes: [{r: 1221, b: 2121, t: 2075, page: 37, l: 1107}],
|
14
|
+
b: 2535,
|
15
|
+
t: 1942,
|
16
|
+
page_width: 1790,
|
17
|
+
r: 1598,
|
18
|
+
l: 50,
|
19
|
+
page_height: 2940,
|
20
|
+
page: 37
|
21
|
+
}]
|
22
|
+
}]
|
23
|
+
};
|
24
|
+
|
25
|
+
marshallSearchResults(DUMMY_RESULTS, () => '', '{{{', '}}}');
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { marshallSearchResults, renderMatch } from '@/src/plugins/search/utils.js';
|
2
|
+
import { deepCopy } from '@/tests/jest/utils.js';
|
3
|
+
import { DUMMY_RESULTS } from './utils.js';
|
4
|
+
|
5
|
+
describe('renderMatch', () => {
|
6
|
+
test('Supports custom pre/post tags', () => {
|
7
|
+
const matchText = DUMMY_RESULTS.matches[0].text
|
8
|
+
.replace(/\{\{\{/g, '<IA_FTS_MATCH>')
|
9
|
+
.replace(/\}\}\}/g, '</IA_FTS_MATCH>');
|
10
|
+
const html = renderMatch(matchText, '<IA_FTS_MATCH>', '</IA_FTS_MATCH>');
|
11
|
+
expect(html).toContain('<mark>');
|
12
|
+
expect(html).toContain('</mark>');
|
13
|
+
});
|
14
|
+
});
|
15
|
+
|
16
|
+
describe('marshallSearchResults', () => {
|
17
|
+
test('Adds match index', () => {
|
18
|
+
const results = deepCopy(DUMMY_RESULTS);
|
19
|
+
marshallSearchResults(results, x => x.toString(), '{{{', '}}}');
|
20
|
+
expect(results.matches[0]).toHaveProperty('matchIndex', 0);
|
21
|
+
expect(results.matches[0].par[0].boxes[0]).toHaveProperty('matchIndex', 0);
|
22
|
+
});
|
23
|
+
|
24
|
+
test('Adds display page number', () => {
|
25
|
+
const results = deepCopy(DUMMY_RESULTS);
|
26
|
+
marshallSearchResults(results, x => `n${x}`, '{{{', '}}}');
|
27
|
+
expect(results.matches[0]).toHaveProperty('displayPageNumber', 'n37');
|
28
|
+
});
|
29
|
+
});
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import sinon from 'sinon';
|
2
2
|
import { afterEventLoop } from '../../utils.js';
|
3
|
-
import AbstractTTSEngine from '
|
4
|
-
import PageChunkIterator from '
|
5
|
-
/** @typedef {import('
|
3
|
+
import AbstractTTSEngine from '@/src/plugins/tts/AbstractTTSEngine.js';
|
4
|
+
import PageChunkIterator from '@/src/plugins/tts/PageChunkIterator.js';
|
5
|
+
/** @typedef {import('@/src/plugins/tts/AbstractTTSEngine.js').TTSEngineOptions} TTSEngineOptions */
|
6
6
|
|
7
7
|
// Skipping because it's flaky. Fix in #672
|
8
8
|
describe.skip('AbstractTTSEngine', () => {
|
9
|
-
test('stops playing once done', () => {
|
9
|
+
test('stops playing once done', async () => {
|
10
10
|
class DummyEngine extends AbstractTTSEngine {
|
11
11
|
getVoices() { return []; }
|
12
12
|
}
|
@@ -15,12 +15,12 @@ describe.skip('AbstractTTSEngine', () => {
|
|
15
15
|
const stopStub = sinon.stub(d, 'stop');
|
16
16
|
expect(stopStub.callCount).toBe(0);
|
17
17
|
d.step();
|
18
|
-
|
19
|
-
|
18
|
+
await afterEventLoop();
|
19
|
+
expect(stopStub.callCount).toBe(1);
|
20
20
|
});
|
21
21
|
});
|
22
22
|
|
23
|
-
for (const dummyVoice of [
|
23
|
+
for (const dummyVoice of [dummyVoiceHyphens, dummyVoiceUnderscores]) {
|
24
24
|
describe(`getBestBookVoice with BCP47 ${dummyVoice == dummyVoiceUnderscores ? '+' : '-'} underscores`, () => {
|
25
25
|
const { getBestBookVoice } = AbstractTTSEngine;
|
26
26
|
|
@@ -111,6 +111,26 @@ for (const dummyVoice of [dummyVoice, dummyVoiceUnderscores]) {
|
|
111
111
|
|
112
112
|
expect(getBestBookVoice(voices, 'en', ['en-CA', 'en'])).toBe(voices[0]);
|
113
113
|
});
|
114
|
+
|
115
|
+
test('choose stored language from localStorage', () => {
|
116
|
+
const voices = [
|
117
|
+
dummyVoice({lang: "en-US", voiceURI: "English US", default: true}),
|
118
|
+
dummyVoice({lang: "en-GB", voiceURI: "English GB"}),
|
119
|
+
dummyVoice({lang: "en-CA", voiceURI: "English CA"}),
|
120
|
+
];
|
121
|
+
class DummyEngine extends AbstractTTSEngine {
|
122
|
+
getVoices() { return voices; }
|
123
|
+
}
|
124
|
+
const ttsEngine = new DummyEngine({...DUMMY_TTS_ENGINE_OPTS, bookLanguage: 'en'});
|
125
|
+
// simulates setting default voice on tts startup
|
126
|
+
ttsEngine.updateBestVoice();
|
127
|
+
// simulates user choosing a voice that matches the bookLanguage
|
128
|
+
// voice will be stored in localStorage
|
129
|
+
ttsEngine.setVoice(voices[2].voiceURI);
|
130
|
+
|
131
|
+
// expecting the voice to be selected by getMatchingStoredVoice and returned as best voice
|
132
|
+
expect(getBestBookVoice(voices, 'en', [])).toBe(voices[2]);
|
133
|
+
});
|
114
134
|
});
|
115
135
|
}
|
116
136
|
|
@@ -130,7 +150,7 @@ export const DUMMY_TTS_ENGINE_OPTS = {
|
|
130
150
|
* @param {SpeechSynthesisVoice}
|
131
151
|
* @return {SpeechSynthesisVoice}
|
132
152
|
**/
|
133
|
-
function
|
153
|
+
function dummyVoiceHyphens(overrides) {
|
134
154
|
return Object.assign({
|
135
155
|
default: false,
|
136
156
|
lang: "en-US",
|
@@ -147,7 +167,7 @@ function dummyVoice(overrides) {
|
|
147
167
|
* @return {SpeechSynthesisVoice}
|
148
168
|
**/
|
149
169
|
function dummyVoiceUnderscores(overrides) {
|
150
|
-
const voice =
|
170
|
+
const voice = dummyVoiceHyphens(overrides);
|
151
171
|
voice.lang = voice.lang.replace('-', '_');
|
152
172
|
return voice;
|
153
173
|
}
|