feat(epub): align wxread css and overlay pagination behavior
This commit is contained in:
@@ -153,47 +153,195 @@
|
||||
var absoluteLeft = Math.max(0, rect.left + currentPageOffset());
|
||||
return Math.max(0, Math.floor(absoluteLeft / effectivePageStride()));
|
||||
}
|
||||
function unwrapHighlights() {
|
||||
document.querySelectorAll('mark.ss-reader-highlight').forEach(function(mark) {
|
||||
var parent = mark.parentNode;
|
||||
if (!parent) { return; }
|
||||
while (mark.firstChild) {
|
||||
parent.insertBefore(mark.firstChild, mark);
|
||||
}
|
||||
parent.removeChild(mark);
|
||||
});
|
||||
var storedHighlights = [];
|
||||
var storedSearchPresentation = null;
|
||||
function normalizeHref(rawHref) {
|
||||
if (!rawHref) { return ''; }
|
||||
var value = String(rawHref);
|
||||
value = value.replace(/^.*?:\/\/[^/]+\//, '');
|
||||
value = value.split('#')[0];
|
||||
value = value.replace(/^\/+/, '');
|
||||
try {
|
||||
value = decodeURIComponent(value);
|
||||
} catch (error) {
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function clearHighlights() {
|
||||
storedHighlights = [];
|
||||
}
|
||||
function setHighlights(items) {
|
||||
unwrapHighlights();
|
||||
if (!Array.isArray(items)) { return; }
|
||||
items.forEach(function(item) {
|
||||
if (!item || !item.rangeInfo) { return; }
|
||||
var range = rangeFromInfo(item.rangeInfo);
|
||||
if (!range || range.collapsed) { return; }
|
||||
storedHighlights = Array.isArray(items) ? items.slice() : [];
|
||||
}
|
||||
function setSearchPresentation(payload) {
|
||||
storedSearchPresentation = payload || null;
|
||||
}
|
||||
function textNodes(doc) {
|
||||
if (!doc || !doc.body) { return []; }
|
||||
var walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT, {
|
||||
acceptNode: function(node) {
|
||||
if (!node || !node.nodeValue || !node.nodeValue.trim()) {
|
||||
return NodeFilter.FILTER_REJECT;
|
||||
}
|
||||
var parent = node.parentNode;
|
||||
if (!parent || !parent.nodeName) {
|
||||
return NodeFilter.FILTER_REJECT;
|
||||
}
|
||||
var name = parent.nodeName.toLowerCase();
|
||||
if (['script', 'style', 'noscript'].indexOf(name) >= 0) {
|
||||
return NodeFilter.FILTER_REJECT;
|
||||
}
|
||||
return NodeFilter.FILTER_ACCEPT;
|
||||
}
|
||||
});
|
||||
var nodes = [];
|
||||
while (walker.nextNode()) {
|
||||
nodes.push(walker.currentNode);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
function documentContexts(rootDoc, baseOffsetX, baseOffsetY) {
|
||||
var items = [];
|
||||
if (!rootDoc) { return items; }
|
||||
var offsetX = baseOffsetX || 0;
|
||||
var offsetY = baseOffsetY || 0;
|
||||
items.push({
|
||||
doc: rootDoc,
|
||||
href: normalizeHref(rootDoc.location && rootDoc.location.href),
|
||||
offsetX: offsetX,
|
||||
offsetY: offsetY
|
||||
});
|
||||
var frames = rootDoc.querySelectorAll ? rootDoc.querySelectorAll('iframe') : [];
|
||||
frames.forEach(function(frame) {
|
||||
try {
|
||||
var mark = document.createElement('mark');
|
||||
var style = item.style || 'highlight';
|
||||
mark.className = 'ss-reader-highlight ss-reader-highlight-' + style;
|
||||
mark.style.color = 'inherit';
|
||||
if (style === 'underline') {
|
||||
mark.style.background = 'transparent';
|
||||
mark.style.textDecorationLine = 'underline';
|
||||
mark.style.textDecorationStyle = 'solid';
|
||||
mark.style.textDecorationThickness = '2px';
|
||||
mark.style.textDecorationColor = item.color || '#F8E16C';
|
||||
} else {
|
||||
mark.style.background = item.color || '#F8E16C';
|
||||
}
|
||||
mark.dataset.highlightId = item.id || '';
|
||||
var fragment = range.extractContents();
|
||||
mark.appendChild(fragment);
|
||||
range.insertNode(mark);
|
||||
if (mark.parentNode && mark.parentNode.normalize) {
|
||||
mark.parentNode.normalize();
|
||||
}
|
||||
if (!frame.contentDocument) { return; }
|
||||
var frameRect = frame.getBoundingClientRect();
|
||||
documentContexts(
|
||||
frame.contentDocument,
|
||||
offsetX + frameRect.left,
|
||||
offsetY + frameRect.top
|
||||
).forEach(function(item) {
|
||||
items.push(item);
|
||||
});
|
||||
} catch (error) {
|
||||
}
|
||||
});
|
||||
return items;
|
||||
}
|
||||
function rectPayloadForRange(range, offsetX, offsetY) {
|
||||
if (!range || range.collapsed) { return []; }
|
||||
var rects = [];
|
||||
Array.prototype.forEach.call(range.getClientRects(), function(rect) {
|
||||
if (!rect || rect.width < 0.5 || rect.height < 0.5) { return; }
|
||||
rects.push({
|
||||
x: rect.left + (offsetX || 0),
|
||||
y: rect.top + (offsetY || 0),
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
});
|
||||
});
|
||||
return rects;
|
||||
}
|
||||
function collectHighlightDecorations() {
|
||||
return storedHighlights.map(function(item, index) {
|
||||
if (!item || !item.rangeInfo) { return null; }
|
||||
var range = rangeFromInfo(item.rangeInfo);
|
||||
var rects = rectPayloadForRange(range, 0, 0);
|
||||
if (!rects.length) { return null; }
|
||||
return {
|
||||
key: item.id || ('highlight-' + index),
|
||||
kind: item.style === 'underline' ? 'underline' : 'highlight',
|
||||
color: item.color || '#F8E16C',
|
||||
rangeInfo: item.rangeInfo,
|
||||
rects: rects
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
function collectSearchDecorations() {
|
||||
if (!storedSearchPresentation || !storedSearchPresentation.keyword) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var resources = new Map();
|
||||
if (Array.isArray(storedSearchPresentation.resources)) {
|
||||
storedSearchPresentation.resources.forEach(function(resource) {
|
||||
resources.set(normalizeHref(resource.href), resource);
|
||||
});
|
||||
}
|
||||
|
||||
var keyword = String(storedSearchPresentation.keyword);
|
||||
var lowerKeyword = keyword.toLowerCase();
|
||||
var decorations = [];
|
||||
|
||||
documentContexts(document, 0, 0).forEach(function(context) {
|
||||
var resourceState = resources.get(context.href);
|
||||
if (!resourceState || resourceState.matchCount <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var activeIndex = typeof resourceState.activeLocalMatchIndex === 'number' ? resourceState.activeLocalMatchIndex : null;
|
||||
var currentLocalIndex = 0;
|
||||
var activeRect = null;
|
||||
var contextStartIndex = decorations.length;
|
||||
|
||||
textNodes(context.doc).forEach(function(node) {
|
||||
var source = node.nodeValue || '';
|
||||
var lowerSource = source.toLowerCase();
|
||||
var cursor = 0;
|
||||
|
||||
while (cursor < source.length) {
|
||||
var foundIndex = lowerSource.indexOf(lowerKeyword, cursor);
|
||||
if (foundIndex < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var range = context.doc.createRange();
|
||||
range.setStart(node, foundIndex);
|
||||
range.setEnd(node, foundIndex + keyword.length);
|
||||
var rects = rectPayloadForRange(range, context.offsetX, context.offsetY);
|
||||
if (rects.length) {
|
||||
var isActive = currentLocalIndex === activeIndex;
|
||||
decorations.push({
|
||||
key: context.href + ':' + currentLocalIndex,
|
||||
kind: isActive ? 'activeSearch' : 'search',
|
||||
rects: rects
|
||||
});
|
||||
if (isActive) {
|
||||
activeRect = rects[0];
|
||||
}
|
||||
}
|
||||
|
||||
currentLocalIndex += 1;
|
||||
cursor = foundIndex + keyword.length;
|
||||
}
|
||||
});
|
||||
|
||||
if (activeRect && !isFixedLayoutDocument()) {
|
||||
var previousPageIndex = visiblePageIndex;
|
||||
var activePageIndex = Math.max(0, Math.floor(Math.max(0, activeRect.x + currentPageOffset()) / effectivePageStride()));
|
||||
setVisiblePageIndex(activePageIndex);
|
||||
var deltaX = (activePageIndex - previousPageIndex) * effectivePageStride();
|
||||
if (deltaX !== 0) {
|
||||
for (var index = contextStartIndex; index < decorations.length; index += 1) {
|
||||
decorations[index].rects = decorations[index].rects.map(function(rect) {
|
||||
return {
|
||||
x: rect.x - deltaX,
|
||||
y: rect.y,
|
||||
width: rect.width,
|
||||
height: rect.height
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return decorations;
|
||||
}
|
||||
function resolveDecorations() {
|
||||
return {
|
||||
highlights: collectHighlightDecorations(),
|
||||
search: collectSearchDecorations()
|
||||
};
|
||||
}
|
||||
function selectionPayload() {
|
||||
var selection = window.getSelection();
|
||||
@@ -290,7 +438,9 @@
|
||||
this.scrollToPage(derivedPageIndex);
|
||||
},
|
||||
setHighlights: setHighlights,
|
||||
clearHighlights: unwrapHighlights,
|
||||
clearHighlights: clearHighlights,
|
||||
setSearchPresentation: setSearchPresentation,
|
||||
resolveDecorations: resolveDecorations,
|
||||
reportProgression: reportProgression,
|
||||
selectionPayload: selectionPayload
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user