Skip to content

Commit 6866e2c

Browse files
author
hiive
committed
- futurepress#1410: Fix Navigation.get() ID lookup — strip # before tocById lookup - futurepress#1393: Fix encoded URI substitution — try decodeURIComponent before regex - futurepress#1285: Parse fallback attribute in manifest items (EPUB spec compliance) - futurepress#1265: Treat page numbers as strings — fixes non-numeric page labels (Roman numerals) - futurepress#1339: Fix TypeScript types — currentLocation() returns Location not DisplayedLocation - futurepress#1407: Ensure at least one location per section — fixes picture-only EPUBs
1 parent c26747d commit 6866e2c

7 files changed

Lines changed: 70 additions & 37 deletions

File tree

dist/index.mjs

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,6 +1635,11 @@ function replaceLinks(contents, fn) {
16351635
function substitute(content, urls, replacements) {
16361636
urls.forEach(function(url, i) {
16371637
if (url && replacements[i]) {
1638+
try {
1639+
let decoded = decodeURIComponent(url);
1640+
content = content.replace(new RegExp(decoded.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), "g"), replacements[i]);
1641+
} catch (e) {
1642+
}
16381643
url = url.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
16391644
content = content.replace(new RegExp(url, "g"), replacements[i]);
16401645
}
@@ -2521,14 +2526,15 @@ var Locations = class {
25212526
var len = node.length;
25222527
var dist;
25232528
var pos = 0;
2524-
if (node.textContent.trim().length === 0) {
2525-
return false;
2526-
}
2527-
if (counter == 0) {
2529+
if (counter === 0 && range === void 0) {
25282530
range = this.createRange();
25292531
range.startContainer = node;
25302532
range.startOffset = 0;
25312533
}
2534+
if (node.textContent.trim().length === 0) {
2535+
prev = node;
2536+
return false;
2537+
}
25322538
dist = _break - counter;
25332539
if (dist > len) {
25342540
counter += len;
@@ -2954,13 +2960,14 @@ var Packaging = class {
29542960
var selected = qsa(manifestXml, "item");
29552961
var items = Array.prototype.slice.call(selected);
29562962
items.forEach(function(item) {
2957-
var id = item.getAttribute("id"), href = item.getAttribute("href") || "", type2 = item.getAttribute("media-type") || "", overlay = item.getAttribute("media-overlay") || "", properties = item.getAttribute("properties") || "";
2963+
var id = item.getAttribute("id"), href = item.getAttribute("href") || "", type2 = item.getAttribute("media-type") || "", overlay = item.getAttribute("media-overlay") || "", properties = item.getAttribute("properties") || "", fallback = item.getAttribute("fallback") || "";
29582964
manifest[id] = {
29592965
"href": href,
29602966
// "url" : href,
29612967
"type": type2,
29622968
"overlay": overlay,
2963-
"properties": properties.length ? properties.split(" ") : []
2969+
"properties": properties.length ? properties.split(" ") : [],
2970+
"fallback": fallback
29642971
};
29652972
});
29662973
return manifest;
@@ -3211,7 +3218,8 @@ var Navigation = class {
32113218
return this.toc;
32123219
}
32133220
if (target.indexOf("#") === 0) {
3214-
index = this.tocById[target.substring(1)];
3221+
target = target.substring(1);
3222+
index = this.tocById[target];
32153223
} else if (target in this.tocByHref) {
32163224
index = this.tocByHref[target];
32173225
}
@@ -3836,6 +3844,7 @@ var PageList = class {
38363844
constructor(xml) {
38373845
this.pages = [];
38383846
this.locations = [];
3847+
this.hrefMap = {};
38393848
this.epubcfi = new epubcfi_default();
38403849
this.firstPage = 0;
38413850
this.lastPage = 0;
@@ -3908,7 +3917,7 @@ var PageList = class {
39083917
var pageText = navLabelText.textContent;
39093918
var content = qs(item, "content");
39103919
var href = content.getAttribute("src");
3911-
var page = parseInt(pageText, 10);
3920+
var page = pageText;
39123921
return {
39133922
"href": href,
39143923
"page": page
@@ -3921,7 +3930,7 @@ var PageList = class {
39213930
* @return {object} pageListItem
39223931
*/
39233932
item(item) {
3924-
var content = qs(item, "a"), href = content.getAttribute("href") || "", text = content.textContent || "", page = parseInt(text), isCfi = href.indexOf("epubcfi"), split, packageUrl, cfi;
3933+
var content = qs(item, "a"), href = content.getAttribute("href") || "", text = content.textContent || "", page = text, isCfi = href.indexOf("epubcfi"), split, packageUrl, cfi;
39253934
if (isCfi != -1) {
39263935
split = href.split("#");
39273936
packageUrl = split[0];
@@ -3947,18 +3956,19 @@ var PageList = class {
39473956
process(pageList) {
39483957
pageList.forEach(function(item) {
39493958
this.pages.push(item.page);
3959+
this.hrefMap[item.page] = item.href;
39503960
if (item.cfi) {
39513961
this.locations.push(item.cfi);
39523962
}
39533963
}, this);
3954-
this.firstPage = parseInt(this.pages[0]);
3955-
this.lastPage = parseInt(this.pages[this.pages.length - 1]);
3964+
this.firstPage = this.pages[0];
3965+
this.lastPage = this.pages[this.pages.length - 1];
39563966
this.totalPages = this.lastPage - this.firstPage;
39573967
}
39583968
/**
39593969
* Get a PageList result from a EpubCFI
39603970
* @param {string} cfi EpubCFI String
3961-
* @return {number} page
3971+
* @return {string} page
39623972
*/
39633973
pageFromCfi(cfi) {
39643974
var pg = -1;
@@ -3980,20 +3990,25 @@ var PageList = class {
39803990
}
39813991
/**
39823992
* Get an EpubCFI from a Page List Item
3983-
* @param {string | number} pg
3993+
* @param {string} pg
39843994
* @return {string} cfi
39853995
*/
39863996
cfiFromPage(pg) {
39873997
var cfi = -1;
3988-
if (typeof pg != "number") {
3989-
pg = parseInt(pg);
3990-
}
39913998
var index = this.pages.indexOf(pg);
39923999
if (index != -1) {
39934000
cfi = this.locations[index];
39944001
}
39954002
return cfi;
39964003
}
4004+
/**
4005+
* Get the href for a page
4006+
* @param {string} pg
4007+
* @return {string} href
4008+
*/
4009+
hrefFromPage(pg) {
4010+
return this.hrefMap[pg];
4011+
}
39974012
/**
39984013
* Get a Page from Book percentage
39994014
* @param {number} percent

src/locations.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,17 +104,18 @@ class Locations {
104104
var dist;
105105
var pos = 0;
106106

107-
if (node.textContent.trim().length === 0) {
108-
return false; // continue
109-
}
110-
111107
// Start range
112-
if (counter == 0) {
108+
if (counter === 0 && range === undefined) {
113109
range = this.createRange();
114110
range.startContainer = node;
115111
range.startOffset = 0;
116112
}
117113

114+
if (node.textContent.trim().length === 0) {
115+
prev = node;
116+
return false; // continue
117+
}
118+
118119
dist = _break - counter;
119120

120121
// Node is smaller than a break,

src/navigation.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ class Navigation {
8888
}
8989

9090
if(target.indexOf("#") === 0) {
91-
index = this.tocById[target.substring(1)];
91+
target = target.substring(1);
92+
index = this.tocById[target];
9293
} else if(target in this.tocByHref){
9394
index = this.tocByHref[target];
9495
}

src/packaging.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,14 +126,16 @@ class Packaging {
126126
href = item.getAttribute("href") || "",
127127
type = item.getAttribute("media-type") || "",
128128
overlay = item.getAttribute("media-overlay") || "",
129-
properties = item.getAttribute("properties") || "";
129+
properties = item.getAttribute("properties") || "",
130+
fallback = item.getAttribute("fallback") || "";
130131

131132
manifest[id] = {
132133
"href" : href,
133134
// "url" : href,
134135
"type" : type,
135136
"overlay" : overlay,
136-
"properties" : properties.length ? properties.split(" ") : []
137+
"properties" : properties.length ? properties.split(" ") : [],
138+
"fallback" : fallback
137139
};
138140

139141
});

src/pagelist.js

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class PageList {
1515
constructor(xml) {
1616
this.pages = [];
1717
this.locations = [];
18+
this.hrefMap = {};
1819
this.epubcfi = new EpubCFI();
1920

2021
this.firstPage = 0;
@@ -106,7 +107,7 @@ class PageList {
106107
var content = qs(item, "content");
107108

108109
var href = content.getAttribute("src");
109-
var page = parseInt(pageText, 10);
110+
var page = pageText;
110111

111112
return {
112113
"href": href,
@@ -124,7 +125,7 @@ class PageList {
124125
var content = qs(item, "a"),
125126
href = content.getAttribute("href") || "",
126127
text = content.textContent || "",
127-
page = parseInt(text),
128+
page = text,
128129
isCfi = href.indexOf("epubcfi"),
129130
split,
130131
packageUrl,
@@ -156,19 +157,20 @@ class PageList {
156157
process(pageList){
157158
pageList.forEach(function(item){
158159
this.pages.push(item.page);
160+
this.hrefMap[item.page] = item.href;
159161
if (item.cfi) {
160162
this.locations.push(item.cfi);
161163
}
162164
}, this);
163-
this.firstPage = parseInt(this.pages[0]);
164-
this.lastPage = parseInt(this.pages[this.pages.length-1]);
165+
this.firstPage = this.pages[0];
166+
this.lastPage = this.pages[this.pages.length-1];
165167
this.totalPages = this.lastPage - this.firstPage;
166168
}
167169

168170
/**
169171
* Get a PageList result from a EpubCFI
170172
* @param {string} cfi EpubCFI String
171-
* @return {number} page
173+
* @return {string} page
172174
*/
173175
pageFromCfi(cfi){
174176
var pg = -1;
@@ -205,16 +207,11 @@ class PageList {
205207

206208
/**
207209
* Get an EpubCFI from a Page List Item
208-
* @param {string | number} pg
210+
* @param {string} pg
209211
* @return {string} cfi
210212
*/
211213
cfiFromPage(pg){
212214
var cfi = -1;
213-
// check that pg is an int
214-
if(typeof pg != "number"){
215-
pg = parseInt(pg);
216-
}
217-
218215
// check if the cfi is in the page list
219216
// Pages could be unsorted.
220217
var index = this.pages.indexOf(pg);
@@ -225,6 +222,15 @@ class PageList {
225222
return cfi;
226223
}
227224

225+
/**
226+
* Get the href for a page
227+
* @param {string} pg
228+
* @return {string} href
229+
*/
230+
hrefFromPage(pg) {
231+
return this.hrefMap[pg];
232+
}
233+
228234
/**
229235
* Get a Page from Book percentage
230236
* @param {number} percent

src/utils/replacements.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ export function replaceLinks(contents, fn) {
128128
export function substitute(content, urls, replacements) {
129129
urls.forEach(function(url, i){
130130
if (url && replacements[i]) {
131+
// When manifest href is URI-encoded but the content source is not,
132+
// try replacing the decoded form first
133+
try {
134+
let decoded = decodeURIComponent(url);
135+
content = content.replace(new RegExp(decoded.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), "g"), replacements[i]);
136+
} catch (e) {
137+
// decodeURIComponent can throw on malformed URIs — ignore
138+
}
131139
// Account for special characters in the file name.
132140
// See https://stackoverflow.com/a/6318729.
133141
url = url.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");

types/rendition.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ export default class Rendition {
7575

7676
clear(): void;
7777

78-
currentLocation(): DisplayedLocation;
79-
currentLocation(): Promise<DisplayedLocation>;
78+
currentLocation(): Location;
79+
currentLocation(): Promise<Location>;
8080

8181
destroy(): void;
8282

0 commit comments

Comments
 (0)