@internetarchive/bookreader 5.0.0-61 → 5.0.0-63

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.
@@ -2,11 +2,13 @@
2
2
  * Custom overrides for BookReader Demo.
3
3
  */
4
4
  html {
5
+ /** This must be set because the nav menu uses rem and sets the fonts really big? */
5
6
  font-size: 10px;
6
7
  font-family: sans-serif;
7
8
  }
8
9
 
9
10
  body {
11
+ font-size: 16px;
10
12
  background-color: #939598;
11
13
  margin: 0px;
12
14
  }
@@ -72,6 +72,97 @@
72
72
  }
73
73
  </style>
74
74
  <section class="demos">
75
+ <details class="demo">
76
+ <summary>Test books</summary>
77
+ <ul>
78
+ <li>
79
+ Misc English book
80
+ <ul>
81
+ <li>
82
+ <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=countofmontecris00duma_7">
83
+ <i>The Count of Monte-Cristo</i> by Alexandre Dumas
84
+ </a>
85
+ </li>
86
+ <li>
87
+ <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=driitaleofdaring00bachuoft">
88
+ <i>D’ri and I</i> by Irving Bacheller
89
+ </a>
90
+ </li>
91
+ </ul>
92
+ </li>
93
+ <li>
94
+ Misc non-English book (<a href="https://archive.org/search?query=%21language%3Aeng&sort=-week&and%5B%5D=lending%3A%22is_readable%22&and%5B%5D=mediatype%3A%22texts%22">Search for more</a>)
95
+ <ul>
96
+ <li>
97
+ French: <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=lecomtedemontecr01dumauoft">
98
+ <i>Le Comte de Monte-Cristo</i> by Alexandre Dumas
99
+ </a>
100
+ </li>
101
+ </ul>
102
+ </li>
103
+ <li>
104
+ Right-to-left book (<a href="https://archive.org/search?query=page-progression%3Arl&sort=-week&and%5B%5D=lending%3A%22is_readable%22&and%5B%5D=mediatype%3A%22texts%22">Search for more</a>)
105
+ <ul>
106
+ <li>
107
+ <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=gendaitankashu00meijuoft">
108
+ <i>Gendai tanka shu</i> by Meiji, Emperor of Japan
109
+ </a>
110
+ </li>
111
+ </ul>
112
+ </li>
113
+ <li>
114
+ Newspaper with columns
115
+ <ul>
116
+ <li>
117
+ <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=Crusader-Vol_28_Nos_1-20_Sept_1986-April_1987">
118
+ Crusader-Vol_28_Nos_1-20_Sept_1986-April_1987
119
+ </a>
120
+ </li>
121
+ </ul>
122
+ </li>
123
+ <li>
124
+ Text Selection
125
+ <ul>
126
+ <li>
127
+ OCR with spaces inside each <code>&lt;WORD&gt;</code> element
128
+ <ul>
129
+ <li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=theworksofplato01platiala">
130
+ <i>The Works of Plato</i> by Plato
131
+ </a></li>
132
+ </ul>
133
+ </li>
134
+ <li>
135
+ Weird font-sizes/indents
136
+ <ul>
137
+ <li><a href="/BookReaderDemo/demo-internetarchive.html?ocaid=cihm_58393#page/n3">
138
+ Microfilm poster thing
139
+ </a></li>
140
+ </ul>
141
+ </li>
142
+ <li>
143
+ Book with short lines
144
+ <ul>
145
+ <li>
146
+ <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=countofmontecris00duma_7#page/261/mode/2up">
147
+ <i>The Count of Monte-Cristo</i>, p.261, second last line
148
+ </a>
149
+ </li>
150
+ </ul>
151
+ </li>
152
+ <li>
153
+ Book with double-spaced lines
154
+ <ul>
155
+ <li>
156
+ <a href="/BookReaderDemo/demo-internetarchive.html?ocaid=calnevpipelineex02unit#page/n93/mode/2up">
157
+ calnevpipelineex02unit
158
+ </a>
159
+ </li>
160
+ </ul>
161
+ </li>
162
+ </ul>
163
+ </li>
164
+ </ul>
165
+ </details>
75
166
  <div class="demo">
76
167
  <button id="toggle-loggedin">Toggle Logged in view</button>
77
168
  <p>Features behind signed in gate: Bookmarks</p>
package/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 5.0.0-63
2
+ Fix: Don't limit autoFit zoom to real world size @cdrini
3
+ Dev: Update test deps @cdrini
4
+
5
+ # 5.0.0-62
6
+ - Fix: Make text selection work in Safari 15.4+ @cdrini
7
+ - Fix: Rewrite/improvements to text selection UX @cdrini
8
+ - Switches from SVG text layer to HTML text layer
9
+
1
10
  # 5.0.0-61
2
11
  - Fix: Mode2up preview pages hanging on first click @cdrini
3
12
  - Dev: Add analytics event for text layer page selection @cdrini
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-61",
3
+ "version": "5.0.0-63",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,12 +42,12 @@
42
42
  },
43
43
  "devDependencies": {
44
44
  "@babel/core": "7.17.9",
45
- "@babel/eslint-parser": "7.21.3",
45
+ "@babel/eslint-parser": "7.21.8",
46
46
  "@babel/plugin-proposal-class-properties": "7.16.7",
47
47
  "@babel/plugin-proposal-decorators": "7.17.9",
48
48
  "@babel/preset-env": "7.16.11",
49
- "@open-wc/testing-helpers": "^2.2.0",
50
- "@types/jest": "^29.5.0",
49
+ "@open-wc/testing-helpers": "^2.2.1",
50
+ "@types/jest": "29.5.2",
51
51
  "@webcomponents/webcomponentsjs": "^2.6.0",
52
52
  "babel-loader": "8.2.5",
53
53
  "codecov": "^3.8.3",
@@ -72,10 +72,10 @@
72
72
  "node-fetch": "3.2.10",
73
73
  "regenerator-runtime": "0.13.9",
74
74
  "sass": "1.52.1",
75
- "sinon": "15.0.3",
75
+ "sinon": "15.1.0",
76
76
  "soundmanager2": "2.97.20170602",
77
77
  "svgo": "2.8.0",
78
- "testcafe": "2.4.0",
78
+ "testcafe": "2.6.2",
79
79
  "testcafe-browser-provider-browserstack": "^1.13.2-alpha.1",
80
80
  "webpack": "5.51.1",
81
81
  "webpack-cli": "4.9.2"
@@ -45,8 +45,8 @@ export class BookModel {
45
45
  heights.push(page.heightInches);
46
46
  }
47
47
 
48
- widths.sort();
49
- heights.sort();
48
+ widths.sort((a, b) => a - b);
49
+ heights.sort((a, b) => a - b);
50
50
 
51
51
  this._medianPageSize = {
52
52
  width: widths[Math.floor(widths.length / 2)],
@@ -445,7 +445,6 @@ export class Mode2UpLit extends LitElement {
445
445
  computeScale(page, autoFit) {
446
446
  if (!page) return 1;
447
447
  const spread = page.spread;
448
- // Default to real size if it fits, otherwise default to full height
449
448
  const bookWidth = this.computePositions(spread.left, spread.right).bookWidth;
450
449
  const bookHeight = this.computePageHeight(spread.left || spread.right);
451
450
  const BOOK_PADDING_PX = 10;
@@ -458,11 +457,11 @@ export class Mode2UpLit extends LitElement {
458
457
 
459
458
  let scale = realScale;
460
459
  if (autoFit == 'width') {
461
- scale = Math.min(widthScale, 1);
460
+ scale = widthScale;
462
461
  } else if (autoFit == 'height') {
463
- scale = Math.min(heightScale, 1);
462
+ scale = heightScale;
464
463
  } else if (autoFit == 'auto') {
465
- scale = Math.min(widthScale, heightScale, 1);
464
+ scale = Math.min(widthScale, heightScale);
466
465
  } else if (autoFit == 'none') {
467
466
  scale = this.scale;
468
467
  } else {
@@ -132,7 +132,7 @@ export class ModeThumb {
132
132
  for (let i = 1; i < this.br.thumbRowBuffer; i++) {
133
133
  if (firstRow - i >= 0) { rowsToDisplay.push(firstRow - i); }
134
134
  }
135
- rowsToDisplay.sort();
135
+ rowsToDisplay.sort((a, b) => a - b);
136
136
 
137
137
  // Create the thumbnail divs and images (lazy loaded)
138
138
  for (const row of rowsToDisplay) {
@@ -94,6 +94,18 @@ export function createSVGPageLayer(page, className) {
94
94
  return svg;
95
95
  }
96
96
 
97
+ /**
98
+ * @param {PageModel} page
99
+ * @param {string} className
100
+ */
101
+ export function createDIVPageLayer(page, className) {
102
+ const div = document.createElement("div");
103
+ div.style.width = `${page.width}px`;
104
+ div.style.height = `${page.height}px`;
105
+ div.setAttribute('class', `BRPageLayer ${className}`);
106
+ return div;
107
+ }
108
+
97
109
  /**
98
110
  * @param {{ l: number, r: number, b: number, t: number }} box
99
111
  */
@@ -8,8 +8,6 @@ export const EVENTS = {
8
8
  stop: 'stop',
9
9
  resize: 'resize',
10
10
  userAction: 'userAction', // event to know if user is actively reading
11
- // nav events:
12
- navToggled: 'navToggled',
13
11
  // menu click events
14
12
  fullscreenToggled: 'fullscreenToggled',
15
13
  zoomOut: 'zoomOut',
@@ -0,0 +1,43 @@
1
+ // @ts-check
2
+ export class SelectionObserver {
3
+ selecting = false;
4
+ startedInSelector = false;
5
+ /** @type {HTMLElement} */
6
+ target = null;
7
+
8
+ /**
9
+ * @param {string} selector
10
+ * @param {function('started' | 'cleared', HTMLElement): any} handler
11
+ */
12
+ constructor(selector, handler) {
13
+ this.selector = selector;
14
+ this.handler = handler;
15
+ }
16
+
17
+ attach() {
18
+ // We can't just use selectstart, because safari on iOS just
19
+ // randomly decides when to fire it 😤
20
+ // document.addEventListener("selectstart", this._onSelectStart);
21
+ // This has to be on document :/
22
+ document.addEventListener("selectionchange", this._onSelectionChange);
23
+ }
24
+
25
+ detach() {
26
+ document.removeEventListener("selectionchange", this._onSelectionChange);
27
+ }
28
+
29
+ _onSelectionChange = () => {
30
+ const sel = window.getSelection();
31
+
32
+ if (!this.selecting && sel.toString()) {
33
+ this.selecting = true;
34
+ this.target = $(sel.anchorNode).closest(this.selector)[0];
35
+ this.handler('started', this.target);
36
+ }
37
+
38
+ if (this.selecting && (sel.isCollapsed || !sel.toString() || !$(sel.anchorNode).closest(this.selector)[0])) {
39
+ this.selecting = false;
40
+ this.handler('cleared', this.target);
41
+ }
42
+ };
43
+ }
package/src/BookReader.js CHANGED
@@ -1412,10 +1412,6 @@ BookReader.prototype.bindNavigationHandlers = function() {
1412
1412
  this.trigger(BookReader.eventNames.userAction);
1413
1413
  });
1414
1414
 
1415
- jIcons.filter('.fit').bind('fit', function() {
1416
- // XXXmang implement autofit zoom
1417
- });
1418
-
1419
1415
  for (const control in navigationControls) {
1420
1416
  jIcons.filter(`.${control}`).on('click.bindNavigationHandlers', () => {
1421
1417
  navigationControls[control]();
@@ -1488,268 +1484,8 @@ BookReader.prototype.bindNavigationHandlers = function() {
1488
1484
  self.$('.BRnavCntl').animate({opacity:.75},250);
1489
1485
  }
1490
1486
  });
1491
-
1492
- this.initSwipeData();
1493
-
1494
- $(document).off('mousemove.navigation', this.el);
1495
- $(document).on(
1496
- 'mousemove.navigation',
1497
- this.el,
1498
- { 'br': this },
1499
- this.navigationMousemoveHandler
1500
- );
1501
-
1502
- $(document).off('mousedown.swipe', '.BRpageimage');
1503
- $(document).on(
1504
- 'mousedown.swipe',
1505
- '.BRpageimage',
1506
- { 'br': this },
1507
- this.swipeMousedownHandler
1508
- );
1509
-
1510
- this.bindMozTouchHandlers();
1511
- };
1512
-
1513
- /**
1514
- * Unbind navigation handlers
1515
- */
1516
- BookReader.prototype.unbindNavigationHandlers = function() {
1517
- $(document).off('mousemove.navigation', this.el);
1518
- };
1519
-
1520
- /**
1521
- * Handle mousemove related to navigation. Bind at #BookReader level to allow autohide.
1522
- */
1523
- BookReader.prototype.navigationMousemoveHandler = function(event) {
1524
- // $$$ possibly not great to be calling this for every mousemove
1525
- if (event.data['br'].uiAutoHide) {
1526
- // 77px is an approximate height of the Internet Archive Top Nav
1527
- // 75 & 76 (pixels) provide used in this context is checked against the IA top nav height
1528
- const navkey = $(document).height() - 75;
1529
- if ((event.pageY < 76) || (event.pageY > navkey)) {
1530
- // inside or near navigation elements
1531
- event.data['br'].hideNavigation();
1532
- } else {
1533
- event.data['br'].showNavigation();
1534
- }
1535
- }
1536
- };
1537
-
1538
- BookReader.prototype.initSwipeData = function(clientX, clientY) {
1539
- /*
1540
- * Based on the really quite awesome "Today's Guardian" at http://guardian.gyford.com/
1541
- */
1542
- this._swipe = {
1543
- mightBeSwiping: false,
1544
- didSwipe: false,
1545
- mightBeDraggin: false,
1546
- didDrag: false,
1547
- startTime: (new Date).getTime(),
1548
- startX: clientX,
1549
- startY: clientY,
1550
- lastX: clientX,
1551
- lastY: clientY,
1552
- deltaX: 0,
1553
- deltaY: 0,
1554
- deltaT: 0
1555
- };
1556
- };
1557
-
1558
- BookReader.prototype.swipeMousedownHandler = function(event) {
1559
- const self = event.data['br'];
1560
-
1561
- // We should be the last bubble point for the page images
1562
- // Disable image drag and select, but keep right-click
1563
- if (event.which == 3) {
1564
- return !self.protected;
1565
- }
1566
-
1567
- $(event.target).on('mouseout.swipe',
1568
- { 'br': self},
1569
- self.swipeMouseupHandler
1570
- ).on('mouseup.swipe',
1571
- { 'br': self},
1572
- self.swipeMouseupHandler
1573
- ).on('mousemove.swipe',
1574
- { 'br': self },
1575
- self.swipeMousemoveHandler
1576
- );
1577
-
1578
- self.initSwipeData(event.clientX, event.clientY);
1579
- self._swipe.mightBeSwiping = true;
1580
- self._swipe.mightBeDragging = true;
1581
-
1582
- event.preventDefault();
1583
- event.returnValue = false;
1584
- event.cancelBubble = true;
1585
- return false;
1586
- };
1587
-
1588
- BookReader.prototype.swipeMousemoveHandler = function(event) {
1589
- const self = event.data['br'];
1590
- const _swipe = self._swipe;
1591
- if (! _swipe.mightBeSwiping) {
1592
- return;
1593
- }
1594
-
1595
- // Update swipe data
1596
- _swipe.deltaX = event.clientX - _swipe.startX;
1597
- _swipe.deltaY = event.clientY - _swipe.startY;
1598
- _swipe.deltaT = (new Date).getTime() - _swipe.startTime;
1599
-
1600
- const absX = Math.abs(_swipe.deltaX);
1601
- const absY = Math.abs(_swipe.deltaY);
1602
-
1603
- // Minimum distance in the amount of tim to trigger the swipe
1604
- const minSwipeLength = Math.min(self.refs.$br.width() / 5, 80);
1605
- const maxSwipeTime = 400;
1606
-
1607
- // Check for horizontal swipe
1608
- if (absX > absY && (absX > minSwipeLength) && _swipe.deltaT < maxSwipeTime) {
1609
- _swipe.mightBeSwiping = false; // only trigger once
1610
- _swipe.didSwipe = true;
1611
- if (self.mode == self.constMode2up) {
1612
- if (_swipe.deltaX < 0) {
1613
- self.right();
1614
- } else {
1615
- self.left();
1616
- }
1617
- }
1618
- }
1619
-
1620
- if ( _swipe.deltaT > maxSwipeTime && !_swipe.didSwipe) {
1621
- if (_swipe.mightBeDragging) {
1622
- // Dragging
1623
- _swipe.didDrag = true;
1624
- self.refs.$brContainer
1625
- .scrollTop(self.refs.$brContainer.scrollTop() - event.clientY + _swipe.lastY)
1626
- .scrollLeft(self.refs.$brContainer.scrollLeft() - event.clientX + _swipe.lastX);
1627
- }
1628
- }
1629
- _swipe.lastX = event.clientX;
1630
- _swipe.lastY = event.clientY;
1631
-
1632
- event.preventDefault();
1633
- event.returnValue = false;
1634
- event.cancelBubble = true;
1635
- return false;
1636
- };
1637
-
1638
- BookReader.prototype.swipeMouseupHandler = function(event) {
1639
- const _swipe = event.data['br']._swipe;
1640
- _swipe.mightBeSwiping = false;
1641
- _swipe.mightBeDragging = false;
1642
-
1643
- $(event.target).off('mouseout.swipe').off('mouseup.swipe').off('mousemove.swipe');
1644
-
1645
- if (_swipe.didSwipe || _swipe.didDrag) {
1646
- // Swallow event if completed swipe gesture
1647
- event.preventDefault();
1648
- event.returnValue = false;
1649
- event.cancelBubble = true;
1650
- return false;
1651
- }
1652
- return true;
1653
- };
1654
-
1655
- BookReader.prototype.bindMozTouchHandlers = function() {
1656
- const self = this;
1657
-
1658
- // Currently only want touch handlers in 2up
1659
- this.refs.$br
1660
- .on('MozTouchDown', function(event) {
1661
- if (this.mode == self.constMode2up) {
1662
- event.preventDefault();
1663
- }
1664
- })
1665
- .on('MozTouchMove', function(event) {
1666
- if (this.mode == self.constMode2up) {
1667
- event.preventDefault();
1668
- }
1669
- })
1670
- .on('MozTouchUp', function(event) {
1671
- if (this.mode == self.constMode2up) {
1672
- event.preventDefault();
1673
- }
1674
- });
1675
- };
1676
-
1677
- /**
1678
- * Returns true if the navigation elements are currently visible
1679
- * @return {boolean}
1680
- */
1681
- BookReader.prototype.navigationIsVisible = function() {
1682
- // $$$ doesn't account for transitioning states, nav must be fully visible to return true
1683
- const toolpos = this.refs.$BRtoolbar.position();
1684
- const tooltop = toolpos.top;
1685
- return tooltop == 0;
1686
1487
  };
1687
1488
 
1688
- /**
1689
- * Main controller that sets navigation into view.
1690
- * Defaults to SHOW the navigation chrome
1691
- */
1692
- BookReader.prototype.setNavigationView = function brSetNavigationView(hide) {
1693
- const animationLength = this.constNavAnimationDuration;
1694
- const animationType = 'linear';
1695
- const resizePageContainer = function resizePageContainer () {
1696
- /* main page container fills whole container */
1697
- if (this.constMode2up !== this.mode) {
1698
- const animate = true;
1699
- this.resizeBRcontainer(animate);
1700
- }
1701
- this.trigger(BookReader.eventNames.navToggled);
1702
- }.bind(this);
1703
-
1704
- let toolbarHeight = 0;
1705
- let navbarHeight = 0;
1706
- if (hide) {
1707
- toolbarHeight = this.getToolBarHeight() * -1;
1708
- navbarHeight = this.getFooterHeight() * -1;
1709
-
1710
- this.refs.$BRtoolbar.addClass('js-menu-hide');
1711
- this.refs.$BRfooter.addClass('js-menu-hide');
1712
- } else {
1713
- this.refs.$BRtoolbar.removeClass('js-menu-hide');
1714
- this.refs.$BRfooter.removeClass('js-menu-hide');
1715
- }
1716
-
1717
- this.refs.$BRtoolbar.animate(
1718
- { top: toolbarHeight },
1719
- animationLength,
1720
- animationType,
1721
- resizePageContainer
1722
- );
1723
- this.refs.$BRfooter.animate(
1724
- { bottom: navbarHeight },
1725
- animationLength,
1726
- animationType,
1727
- resizePageContainer
1728
- );
1729
- };
1730
- /**
1731
- * Hide navigation elements, if visible
1732
- */
1733
- BookReader.prototype.hideNavigation = function() {
1734
- // Check if navigation is showing
1735
- if (this.navigationIsVisible()) {
1736
- const hide = true;
1737
- this.setNavigationView(hide);
1738
- }
1739
- };
1740
-
1741
- /**
1742
- * Show navigation elements
1743
- */
1744
- BookReader.prototype.showNavigation = function() {
1745
- // Check if navigation is hidden
1746
- if (!this.navigationIsVisible()) {
1747
- this.setNavigationView();
1748
- }
1749
- };
1750
-
1751
-
1752
-
1753
1489
  /**************************/
1754
1490
  /** BookModel extensions **/
1755
1491
  /**************************/
@@ -43,6 +43,8 @@
43
43
  position: relative;
44
44
  overflow: hidden;
45
45
  background: $brColorPlaceholderBg;
46
+ overflow: hidden;
47
+ overflow: clip;
46
48
  img {
47
49
  position: absolute;
48
50
  background: transparent;