Introduce new function getNearestCfiFromElement
It is useful for getting a CFI that's as close as possible to an invisible (not rendered, zero client rects) element
MODIFY
js/views/cfi_navigation_logic.js
js/views/cfi_navigation_logic.js
@@ -1117,6 +1117,53 @@ var CfiNavigationLogic = function (options) {
}
};
+ this.getNearestCfiFromElement = function (element) {
+ var collapseToStart;
+ var chosenNode;
+ var isTextNode;
+
+ var siblingTextNodesAndSelf = _.filter(element.parentElement.childNodes, function (n) {
+ return n === element || isValidTextNode(n);
+ });
+
+ var indexOfSelf = siblingTextNodesAndSelf.indexOf(element);
+ var nearestNode = siblingTextNodesAndSelf[indexOfSelf - 1];
+ if (!nearestNode) {
+ nearestNode = siblingTextNodesAndSelf[indexOfSelf + 1];
+ collapseToStart = true;
+ }
+ if (!nearestNode) {
+ nearestNode = _.last(this.getLeafNodeElements($(element.previousElementSibling)));
+ if (!nearestNode) {
+ collapseToStart = true;
+ nearestNode = _.first(this.getLeafNodeElements($(element.nextElementSibling)));
+ }
+ }
+
+ // Prioritize text node use
+ if (isValidTextNode(nearestNode)) {
+ chosenNode = nearestNode;
+ isTextNode = true;
+ } else if (isElementNode(nearestNode)) {
+ chosenNode = nearestNode;
+ } else if (isElementNode(element.previousElementSibling)) {
+ chosenNode = element.previousElementSibling;
+ } else if (isElementNode(element.nextElementSibling)) {
+ chosenNode = element.nextElementSibling;
+ } else {
+ chosenNode = element.parentElement;
+ }
+
+ if (isTextNode) {
+ var range = chosenNode.ownerDocument.createRange();
+ range.selectNodeContents(chosenNode);
+ range.collapse(collapseToStart);
+ return this.getRangeCfiFromDomRange(range);
+ } else {
+ return this.getCfiForElement(chosenNode);
+ }
+ };
+
this.getElementByCfi = function (partialCfi, classBlacklist, elementBlacklist, idBlacklist) {
return getElementByPartialCfi(partialCfi, classBlacklist, elementBlacklist, idBlacklist);
};
@@ -1125,8 +1172,8 @@ var CfiNavigationLogic = function (options) {
var pageIndex = findPageByRectangles($element);
if (pageIndex === null) {
- console.warn('Impossible to locate a hidden element: ', $element);
- return 0;
+ //Attempted to locate a hidden element, try a fallback with best guess
+ return findPageByRectangles(this.getNearestCfiFromElement($element[0]));
}
return pageIndex;
};
@@ -1361,8 +1408,19 @@ var CfiNavigationLogic = function (options) {
return $leafNodeElements;
};
- function isValidTextNode(node) {
+ function isElementNode(node) {
+ if (!node) {
+ return false;
+ }
+ else {
+ return node.nodeType === Node.ELEMENT_NODE;
+ }
+ }
+ function isValidTextNode(node) {
+ if (!node) {
+ return false;
+ }
if (node.nodeType === Node.TEXT_NODE) {
return isValidTextNodeContent(node.nodeValue);

MODIFY
js/views/fixed_view.js
js/views/fixed_view.js
@@ -862,6 +862,19 @@ var FixedView = function(options, reader){
});
};
+ this.getNearestCfiFromElement = function(element) {
+ var views = getDisplayingViews();
+
+ for (var i = 0, count = views.length; i < count; i++) {
+
+ var view = views[i];
+ if (view.getLoadedContentFrames()[0].$iframe[0].contentDocument === element.ownerDocument) {
+ return view.getNearestCfiFromElement(element);
+ }
+ }
+
+ };
+
};
return FixedView;
});

MODIFY
js/views/one_page_view.js
js/views/one_page_view.js
@@ -1124,6 +1124,10 @@ var OnePageView = function (options, classes, enableBookStyleOverrides, reader)
return self.getNavigator().getElementFromPoint(x, y);
};
+
+ this.getNearestCfiFromElement = function(element) {
+ return createBookmarkFromCfi(self.getNavigator().getNearestCfiFromElement(element));
+ };
};
OnePageView.Events = {

MODIFY
js/views/reader_view.js
js/views/reader_view.js
@@ -1733,6 +1733,19 @@ var ReaderView = function (options) {
}
return undefined;
};
+
+ /**
+ * Useful for getting a CFI that's as close as possible to an invisible (not rendered, zero client rects) element
+ * @param {HTMLElement} element
+ * @returns {*}
+ */
+ this.getNearestCfiFromElement = function(element) {
+ if (_currentView) {
+ return _currentView.getNearestCfiFromElement(element);
+ }
+ return undefined;
+ };
+
};
/**

MODIFY
js/views/reflowable_view.js
js/views/reflowable_view.js
@@ -1199,6 +1199,10 @@ var ReflowableView = function(options, reader){
this.getElementFromPoint = function(x, y) {
return _navigationLogic.getElementFromPoint(x,y);
};
+
+ this.getNearestCfiFromElement = function(element) {
+ return createBookmarkFromCfi(_navigationLogic.getNearestCfiFromElement(element));
+ };
};
return ReflowableView;
});

MODIFY
js/views/scroll_view.js
js/views/scroll_view.js
@@ -1407,6 +1407,10 @@ var ScrollView = function (options, isContinuousScroll, reader) {
});
};
+ function createBookmarkFromCfi(currentSpineItem, cfi){
+ return new BookmarkData(currentSpineItem.idref, cfi);
+ }
+
this.getRangeCfiFromDomRange = function (domRange) {
return callOnVisiblePageView(function (pageView) {
return pageView.getRangeCfiFromDomRange(domRange);
@@ -1437,9 +1441,11 @@ var ScrollView = function (options, isContinuousScroll, reader) {
});
};
- function createBookmarkFromCfi(currentSpineItem, cfi){
- return new BookmarkData(currentSpineItem.idref, cfi);
- }
+ this.getNearestCfiFromElement = function (element) {
+ return callOnVisiblePageView(function (pageView) {
+ return pageView.getNearestCfiFromElement(element);
+ });
+ };
};
return ScrollView;

Introduce new function getNearestCfiFromElement

It is useful for getting a CFI that's as close as possible to an invisible (not rendered, zero client rects) element