feat(epub): complete wxread parity scope
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
(function() {
|
||||
if (window.WeReadApi) { return; }
|
||||
|
||||
function bridge() {
|
||||
return window.RDReaderBridge || null;
|
||||
}
|
||||
|
||||
function sharedThemeCSS(theme) {
|
||||
if (!theme) { return ''; }
|
||||
var backgroundColor = theme.backgroundColor || 'transparent';
|
||||
var textColor = theme.textColor || 'inherit';
|
||||
return [
|
||||
':root {',
|
||||
' color-scheme: light;',
|
||||
'}',
|
||||
'html, body {',
|
||||
' background: ' + backgroundColor + ' !important;',
|
||||
' color: ' + textColor + ' !important;',
|
||||
'}',
|
||||
'a {',
|
||||
' -webkit-tap-highlight-color: transparent;',
|
||||
'}'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
window.WeReadApi = {
|
||||
applySharedTheme: function(theme) {
|
||||
var css = sharedThemeCSS(theme);
|
||||
if (window.RDInjectedCSS) {
|
||||
window.RDInjectedCSS.setStyle('ss-reader-theme', css, {
|
||||
includeFrames: !!(theme && theme.includeFrames)
|
||||
});
|
||||
}
|
||||
return true;
|
||||
},
|
||||
applyPagination: function(styleText) {
|
||||
return !!(bridge() && bridge().applyPagination(styleText));
|
||||
},
|
||||
setPageMetrics: function(pageCount, pageStride) {
|
||||
return !!(bridge() && bridge().setPageMetrics(pageCount, pageStride));
|
||||
},
|
||||
scrollToPage: function(pageIndex) {
|
||||
return !!(bridge() && bridge().scrollToPage(pageIndex));
|
||||
},
|
||||
scrollToLocation: function(location, fallbackPageIndex) {
|
||||
return !!(bridge() && bridge().scrollToLocation(location, fallbackPageIndex));
|
||||
},
|
||||
setHighlights: function(items) {
|
||||
return !!(bridge() && bridge().setHighlights(items));
|
||||
},
|
||||
clearHighlights: function() {
|
||||
return !!(bridge() && bridge().clearHighlights());
|
||||
},
|
||||
setSearchPresentation: function(payload) {
|
||||
return !!(bridge() && bridge().setSearchPresentation(payload));
|
||||
},
|
||||
resolveDecorations: function() {
|
||||
return bridge() ? bridge().resolveDecorations() : { highlights: [], search: [] };
|
||||
},
|
||||
reportProgression: function(fragment) {
|
||||
return !!(bridge() && bridge().reportProgression(fragment));
|
||||
},
|
||||
selectionPayload: function() {
|
||||
return bridge() ? bridge().selectionPayload() : null;
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,45 @@
|
||||
(function() {
|
||||
if (window.RDInjectedCSS) { return; }
|
||||
|
||||
function applyStyle(doc, identifier, cssText) {
|
||||
if (!doc || !doc.head) { return; }
|
||||
var style = doc.getElementById(identifier);
|
||||
if (!style) {
|
||||
style = doc.createElement('style');
|
||||
style.id = identifier;
|
||||
doc.head.appendChild(style);
|
||||
}
|
||||
style.textContent = cssText || '';
|
||||
}
|
||||
|
||||
function walkDocuments(rootDoc, includeFrames, visitor) {
|
||||
if (!rootDoc) { return; }
|
||||
visitor(rootDoc);
|
||||
if (!includeFrames || !rootDoc.querySelectorAll) { return; }
|
||||
Array.prototype.forEach.call(rootDoc.querySelectorAll('iframe'), function(frame) {
|
||||
try {
|
||||
if (!frame.contentDocument) { return; }
|
||||
walkDocuments(frame.contentDocument, includeFrames, visitor);
|
||||
} catch (error) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.RDInjectedCSS = {
|
||||
setStyle: function(identifier, cssText, options) {
|
||||
var includeFrames = !!(options && options.includeFrames);
|
||||
walkDocuments(document, includeFrames, function(doc) {
|
||||
applyStyle(doc, identifier, cssText);
|
||||
});
|
||||
},
|
||||
removeStyle: function(identifier, options) {
|
||||
var includeFrames = !!(options && options.includeFrames);
|
||||
walkDocuments(document, includeFrames, function(doc) {
|
||||
var style = doc.getElementById(identifier);
|
||||
if (style && style.parentNode) {
|
||||
style.parentNode.removeChild(style);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -7,48 +7,17 @@
|
||||
} catch (error) {
|
||||
}
|
||||
});
|
||||
function nodePath(node) {
|
||||
var path = [];
|
||||
var current = node;
|
||||
while (current && current !== document) {
|
||||
var parent = current.parentNode;
|
||||
if (!parent) { break; }
|
||||
var index = Array.prototype.indexOf.call(parent.childNodes, current);
|
||||
path.unshift(index);
|
||||
current = parent;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
function nodeFromPath(path) {
|
||||
var current = document;
|
||||
for (var i = 0; i < path.length; i += 1) {
|
||||
if (!current || !current.childNodes || current.childNodes.length <= path[i]) { return null; }
|
||||
current = current.childNodes[path[i]];
|
||||
}
|
||||
return current;
|
||||
}
|
||||
function serializeRange(range) {
|
||||
return JSON.stringify({
|
||||
kind: 'dom-range',
|
||||
startPath: nodePath(range.startContainer),
|
||||
startOffset: range.startOffset,
|
||||
endPath: nodePath(range.endContainer),
|
||||
endOffset: range.endOffset
|
||||
});
|
||||
if (window.rangy && typeof window.rangy.serializeRange === 'function') {
|
||||
return window.rangy.serializeRange(range, range && range.startContainer && range.startContainer.ownerDocument);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function rangeFromInfo(rangeInfo) {
|
||||
try {
|
||||
var payload = typeof rangeInfo === 'string' ? JSON.parse(rangeInfo) : rangeInfo;
|
||||
var startNode = nodeFromPath(payload.startPath || []);
|
||||
var endNode = nodeFromPath(payload.endPath || []);
|
||||
if (!startNode || !endNode) { return null; }
|
||||
var range = document.createRange();
|
||||
range.setStart(startNode, payload.startOffset || 0);
|
||||
range.setEnd(endNode, payload.endOffset || 0);
|
||||
return range;
|
||||
} catch (error) {
|
||||
return null;
|
||||
if (window.rangy && typeof window.rangy.deserializeRange === 'function') {
|
||||
return window.rangy.deserializeRange(rangeInfo, document);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var visiblePageIndex = 0;
|
||||
var configuredPageCount = 1;
|
||||
@@ -153,6 +122,22 @@
|
||||
var absoluteLeft = Math.max(0, rect.left + currentPageOffset());
|
||||
return Math.max(0, Math.floor(absoluteLeft / effectivePageStride()));
|
||||
}
|
||||
function fragmentTarget(fragment) {
|
||||
if (!fragment) { return null; }
|
||||
var contexts = documentContexts(document, 0, 0);
|
||||
for (var index = 0; index < contexts.length; index += 1) {
|
||||
var context = contexts[index];
|
||||
var target = context.doc.getElementById(fragment)
|
||||
|| context.doc.querySelector('[name="' + fragment.replace(/"/g, '\\"') + '"]');
|
||||
if (target) {
|
||||
return {
|
||||
context: context,
|
||||
target: target
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
var storedHighlights = [];
|
||||
var storedSearchPresentation = null;
|
||||
function normalizeHref(rawHref) {
|
||||
@@ -228,6 +213,36 @@
|
||||
});
|
||||
return items;
|
||||
}
|
||||
var hookedDocuments = new WeakSet();
|
||||
var hookedFrames = new WeakSet();
|
||||
function selectionForDocument(doc) {
|
||||
if (!doc) { return null; }
|
||||
try {
|
||||
var view = doc.defaultView || window;
|
||||
return view.getSelection ? view.getSelection() : null;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function currentSelectionContext() {
|
||||
var contexts = documentContexts(document, 0, 0);
|
||||
for (var index = 0; index < contexts.length; index += 1) {
|
||||
var context = contexts[index];
|
||||
var selection = selectionForDocument(context.doc);
|
||||
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
|
||||
continue;
|
||||
}
|
||||
var text = selection.toString ? selection.toString() : '';
|
||||
if (!text || !text.trim()) {
|
||||
continue;
|
||||
}
|
||||
return {
|
||||
context: context,
|
||||
selection: selection
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
function rectPayloadForRange(range, offsetX, offsetY) {
|
||||
if (!range || range.collapsed) { return []; }
|
||||
var rects = [];
|
||||
@@ -344,17 +359,18 @@
|
||||
};
|
||||
}
|
||||
function selectionPayload() {
|
||||
var selection = window.getSelection();
|
||||
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
|
||||
var payloadContext = currentSelectionContext();
|
||||
if (!payloadContext) {
|
||||
return null;
|
||||
}
|
||||
var range = selection.getRangeAt(0);
|
||||
var text = selection.toString();
|
||||
var range = payloadContext.selection.getRangeAt(0);
|
||||
var text = payloadContext.selection.toString();
|
||||
if (!text || !text.trim()) { return null; }
|
||||
var pageCount = totalPageCount();
|
||||
var progression = pageCount <= 1 ? 0 : visiblePageIndex / Math.max(pageCount - 1, 1);
|
||||
var lastProgression = progression;
|
||||
return {
|
||||
href: payloadContext.context.href || null,
|
||||
text: text,
|
||||
rangeInfo: serializeRange(range),
|
||||
progression: progression,
|
||||
@@ -362,13 +378,54 @@
|
||||
};
|
||||
}
|
||||
var selectionTimer = null;
|
||||
document.addEventListener('selectionchange', function() {
|
||||
function notifySelectionChange() {
|
||||
clearTimeout(selectionTimer);
|
||||
selectionTimer = setTimeout(function() {
|
||||
var payload = selectionPayload();
|
||||
window.webkit.messageHandlers.{{SELECTION_CHANGED_MESSAGE}}.postMessage(payload);
|
||||
}, 120);
|
||||
});
|
||||
}
|
||||
function handleDocumentClick(event) {
|
||||
var anchor = event.target.closest ? event.target.closest('a[href]') : null;
|
||||
if (!anchor) { return; }
|
||||
var href = anchor.getAttribute('href');
|
||||
if (!href) { return; }
|
||||
if (/^(https?:|mailto:|tel:)/i.test(href)) {
|
||||
event.preventDefault();
|
||||
window.webkit.messageHandlers.{{EXTERNAL_LINK_MESSAGE}}.postMessage({ url: href });
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
window.webkit.messageHandlers.{{INTERNAL_LINK_MESSAGE}}.postMessage({ href: href });
|
||||
}
|
||||
function installDocumentHooks(doc) {
|
||||
if (!doc || hookedDocuments.has(doc)) { return; }
|
||||
hookedDocuments.add(doc);
|
||||
doc.addEventListener('selectionchange', notifySelectionChange);
|
||||
doc.addEventListener('click', handleDocumentClick, true);
|
||||
}
|
||||
function installFrameHooks(rootDoc) {
|
||||
if (!rootDoc || !rootDoc.querySelectorAll) { return; }
|
||||
Array.prototype.forEach.call(rootDoc.querySelectorAll('iframe'), function(frame) {
|
||||
if (hookedFrames.has(frame)) { return; }
|
||||
hookedFrames.add(frame);
|
||||
var hookFrameDocument = function() {
|
||||
try {
|
||||
if (!frame.contentDocument) { return; }
|
||||
installDocumentHooks(frame.contentDocument);
|
||||
installFrameHooks(frame.contentDocument);
|
||||
} catch (error) {
|
||||
}
|
||||
};
|
||||
frame.addEventListener('load', function() {
|
||||
hookFrameDocument();
|
||||
scheduleRelayout();
|
||||
}, { passive: true });
|
||||
hookFrameDocument();
|
||||
});
|
||||
}
|
||||
installDocumentHooks(document);
|
||||
installFrameHooks(document);
|
||||
window.addEventListener('resize', function() {
|
||||
if (isFixedLayoutDocument()) { return; }
|
||||
setVisiblePageIndex(visiblePageIndex);
|
||||
@@ -384,30 +441,22 @@
|
||||
node.addEventListener('error', scheduleRelayout, { passive: true });
|
||||
});
|
||||
}
|
||||
document.addEventListener('click', function(event) {
|
||||
var anchor = event.target.closest ? event.target.closest('a[href]') : null;
|
||||
if (!anchor) { return; }
|
||||
var href = anchor.getAttribute('href');
|
||||
if (!href) { return; }
|
||||
if (/^(https?:|mailto:|tel:)/i.test(href)) {
|
||||
event.preventDefault();
|
||||
window.webkit.messageHandlers.{{EXTERNAL_LINK_MESSAGE}}.postMessage({ url: href });
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
window.webkit.messageHandlers.{{INTERNAL_LINK_MESSAGE}}.postMessage({ href: href });
|
||||
}, true);
|
||||
window.RDReaderBridge = {
|
||||
applyPagination: function(styleText) {
|
||||
if (isFixedLayoutDocument()) { return; }
|
||||
var style = document.getElementById('ss-reader-pagination');
|
||||
if (!style) {
|
||||
style = document.createElement('style');
|
||||
style.id = 'ss-reader-pagination';
|
||||
document.head.appendChild(style);
|
||||
if (window.RDInjectedCSS) {
|
||||
window.RDInjectedCSS.setStyle('ss-reader-pagination', styleText, { includeFrames: false });
|
||||
} else {
|
||||
var style = document.getElementById('ss-reader-pagination');
|
||||
if (!style) {
|
||||
style = document.createElement('style');
|
||||
style.id = 'ss-reader-pagination';
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
style.textContent = styleText;
|
||||
}
|
||||
style.textContent = styleText;
|
||||
ensurePaginationStructure();
|
||||
installFrameHooks(document);
|
||||
},
|
||||
setPageMetrics: function(pageCount, pageStride) {
|
||||
if (isFixedLayoutDocument()) { return; }
|
||||
@@ -423,9 +472,15 @@
|
||||
},
|
||||
scrollToLocation: function(location, fallbackPageIndex) {
|
||||
if (location && location.fragment) {
|
||||
var target = document.getElementById(location.fragment) || document.querySelector('[name="' + location.fragment.replace(/"/g, '\\"') + '"]');
|
||||
if (target) {
|
||||
setVisiblePageIndex(pageIndexForElement(target));
|
||||
var resolvedTarget = fragmentTarget(location.fragment);
|
||||
if (resolvedTarget) {
|
||||
if (isFixedLayoutDocument()) {
|
||||
reportProgression(location.fragment);
|
||||
return;
|
||||
}
|
||||
var rect = resolvedTarget.target.getBoundingClientRect();
|
||||
var absoluteLeft = Math.max(0, rect.left + resolvedTarget.context.offsetX + currentPageOffset());
|
||||
setVisiblePageIndex(Math.max(0, Math.floor(absoluteLeft / effectivePageStride())));
|
||||
reportProgression(location.fragment);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
(function() {
|
||||
if (window.rangy) { return; }
|
||||
|
||||
window.rangy = {
|
||||
initialized: true,
|
||||
init: function() {
|
||||
return window.rangy;
|
||||
},
|
||||
createRange: function(doc) {
|
||||
return (doc || document).createRange();
|
||||
}
|
||||
};
|
||||
})();
|
||||
@@ -0,0 +1,93 @@
|
||||
(function() {
|
||||
if (!window.rangy) {
|
||||
window.rangy = {};
|
||||
}
|
||||
if (window.rangy.serializeRange && window.rangy.deserializeRange) {
|
||||
return;
|
||||
}
|
||||
|
||||
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 nodePath(node) {
|
||||
var path = [];
|
||||
var current = node;
|
||||
while (current && current !== current.ownerDocument) {
|
||||
var parent = current.parentNode;
|
||||
if (!parent) { break; }
|
||||
var index = Array.prototype.indexOf.call(parent.childNodes, current);
|
||||
path.unshift(index);
|
||||
current = parent;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function nodeFromPath(doc, path) {
|
||||
var current = doc;
|
||||
for (var index = 0; index < path.length; index += 1) {
|
||||
if (!current || !current.childNodes || current.childNodes.length <= path[index]) {
|
||||
return null;
|
||||
}
|
||||
current = current.childNodes[path[index]];
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function documentContexts(rootDoc) {
|
||||
var items = [];
|
||||
if (!rootDoc) { return items; }
|
||||
items.push(rootDoc);
|
||||
var frames = rootDoc.querySelectorAll ? rootDoc.querySelectorAll('iframe') : [];
|
||||
Array.prototype.forEach.call(frames, function(frame) {
|
||||
try {
|
||||
if (!frame.contentDocument) { return; }
|
||||
documentContexts(frame.contentDocument).forEach(function(item) {
|
||||
items.push(item);
|
||||
});
|
||||
} catch (error) {
|
||||
}
|
||||
});
|
||||
return items;
|
||||
}
|
||||
|
||||
window.rangy.serializeRange = function(range, doc) {
|
||||
var ownerDocument = doc || (range && range.startContainer && range.startContainer.ownerDocument) || document;
|
||||
return JSON.stringify({
|
||||
kind: 'dom-range',
|
||||
documentHref: normalizeHref(ownerDocument.location && ownerDocument.location.href),
|
||||
startPath: nodePath(range.startContainer),
|
||||
startOffset: range.startOffset,
|
||||
endPath: nodePath(range.endContainer),
|
||||
endOffset: range.endOffset
|
||||
});
|
||||
};
|
||||
|
||||
window.rangy.deserializeRange = function(rangeInfo, rootDoc) {
|
||||
try {
|
||||
var payload = typeof rangeInfo === 'string' ? JSON.parse(rangeInfo) : rangeInfo;
|
||||
var documentHref = normalizeHref(payload.documentHref);
|
||||
var resolvedDocument = (documentContexts(rootDoc || document).find(function(candidate) {
|
||||
return normalizeHref(candidate.location && candidate.location.href) === documentHref;
|
||||
})) || (rootDoc || document);
|
||||
var startNode = nodeFromPath(resolvedDocument, payload.startPath || []);
|
||||
var endNode = nodeFromPath(resolvedDocument, payload.endPath || []);
|
||||
if (!startNode || !endNode) { return null; }
|
||||
var range = resolvedDocument.createRange();
|
||||
range.setStart(startNode, payload.startOffset || 0);
|
||||
range.setEnd(endNode, payload.endOffset || 0);
|
||||
return range;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user