implemented iterative binary search for CFIs, without using carretFromRange
MODIFY
js/views/cfi_navigation_logic.js
js/views/cfi_navigation_logic.js
@@ -75,6 +75,12 @@ var CfiNavigationLogic = function(options) {
return self.getRootDocument().createRange();
}
+ function createRangeFromNode(textnode) {
+ var documentRange = createRange();
+ documentRange.selectNodeContents(textnode);
+ return documentRange;
+ }
+
function getNodeClientRect(node) {
var range = createRange();
range.selectNode(node);
@@ -231,6 +237,12 @@ var CfiNavigationLogic = function(options) {
return options.paginationOffsetsGetter();
}
+ return {
+ top: 0,
+ left: 0
+ };
+ }
+
// CAUSES REGRESSION BUGS !! TODO FIXME
// https://github.com/readium/readium-shared-js/issues/384#issuecomment-305145129
// else {
@@ -241,12 +253,6 @@ var CfiNavigationLogic = function(options) {
// };
// }
- return {
- top: 0,
- left: 0
- };
- }
-
/**
* New (rectangle-based) algorithm, useful in multi-column layouts
*
@@ -324,26 +330,6 @@ var CfiNavigationLogic = function(options) {
function findPageByRectangles($element) {
var visibleContentOffsets = getVisibleContentOffsets();
- //////////////////////
- // ABOVE CAUSES REGRESSION BUGS !! TODO FIXME
- // https://github.com/readium/readium-shared-js/issues/384#issuecomment-305145129
- if (options.visibleContentOffsets) {
- visibleContentOffsets = options.visibleContentOffsets();
- }
- if (isVerticalWritingMode()) {
- visibleContentOffsets = {
- top: (options.paginationInfo ? options.paginationInfo.pageOffset : 0),
- left: 0
- };
- }
- else { // THIS IS ENABLED ONLY FOR findPageByRectangles(), to fix the pageIndex computation. TODO FIXME!
- visibleContentOffsets = {
- top: 0,
- left: (options.paginationInfo ? options.paginationInfo.pageOffset : 0)
- //* (isPageProgressionRightToLeft() ? -1 : 1)
- };
- }
- //////////////////////
var clientRectangles = getNormalizedRectangles($element, visibleContentOffsets);
if (clientRectangles.length === 0) { // elements with display:none, etc.
@@ -587,7 +573,7 @@ var CfiNavigationLogic = function(options) {
offsetRectangle(rect, -columnFullWidth, frameDimensions.height);
}
- // ... then, if necessary (for visibility offset checks),
+ // ... then, if necessary (for visifindTextRangeOffsetRecursivelity offset checks),
// each column is tried again (now in reverse order)
// the loop will be stopped when the column is aligned with a viewport
// (i.e., is the first visible one).
@@ -739,127 +725,93 @@ var CfiNavigationLogic = function(options) {
var DEBUG = false;
- function getVisibleTextRangeOffsetsSelectedByFunc(textNode, pickerFunc, visibleContentOffsets, frameDimensions) {
- visibleContentOffsets = visibleContentOffsets || getVisibleContentOffsets();
-
- var textNodeFragments = getNodeClientRectList(textNode, visibleContentOffsets);
- var visibleFragments = _.filter(textNodeFragments, function (rect) {
- return isRectVisible(rect, false, frameDimensions);
- });
- var fragment = pickerFunc(visibleFragments);
- if (!fragment) {
- //no visible fragment, empty text node?
- return null;
- }
- var fragmentCorner = pickerFunc(getTextNodeRectCornerPairs(fragment));
- // Reverse taking into account of visible content offsets
- fragmentCorner.x -= visibleContentOffsets.left;
- fragmentCorner.y -= visibleContentOffsets.top;
-
- var caretRange = getCaretRangeFromPoint(fragmentCorner.x, fragmentCorner.y);
-
- // Workaround for inconsistencies with the caretRangeFromPoint IE TextRange based shim.
- if (caretRange && caretRange.startContainer !== textNode && caretRange.startContainer === textNode.parentNode) {
- if (DEBUG) console.log('ieTextRangeWorkaround needed');
- var startOrEnd = pickerFunc([0, 1]);
-
- // #1
- if (caretRange.startOffset === caretRange.endOffset) {
- var checkNode = caretRange.startContainer.childNodes[Math.max(caretRange.startOffset - 1, 0)];
- if (checkNode === textNode) {
- caretRange = {
- startContainer: textNode,
- endContainer: textNode,
- startOffset: startOrEnd === 0 ? 0 : textNode.nodeValue.length
- };
- if (DEBUG) console.log('ieTextRangeWorkaround #1:', caretRange);
- }
- }
+ function determineSplit (Range,division) {
+ //either 50/50 or 60 /40
+ var percent = parseFloat(division)/parseFloat(100);
+ var length = Math.floor((Range.endOffset - Range.startOffset) * percent);
+ return length;
- // Failed
- else if (DEBUG) {
- console.log('ieTextRangeWorkaround didn\'t work :(');
}
+ function splitRange(Range, division) {
+ var length = determineSplit(Range,parseFloat(division));
+ if (length <= 0 ){
+ return [Range];
}
+ var textNode = Range.startContainer;
+ var leftNodeRange = Range.cloneRange();
+ leftNodeRange.setStart(textNode, Range.startOffset);
+ leftNodeRange.setEnd(textNode, Range.startOffset + length);
+ var rightNodeRange = Range.cloneRange();
+ rightNodeRange.setStart(textNode, Range.startOffset + length);
+ rightNodeRange.setEnd(textNode, Range.endOffset);
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'a0');
+ return [leftNodeRange, rightNodeRange];
- // Desperately try to find it from all angles! Darn sub pixeling..
- //TODO: remove the need for this brute-force method, since it's making the result non-deterministic
- if (!caretRange || caretRange.startContainer !== textNode) {
- caretRange = getCaretRangeFromPoint(fragmentCorner.x - 1, fragmentCorner.y);
-
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'a1');
}
- if (!caretRange || caretRange.startContainer !== textNode) {
- caretRange = getCaretRangeFromPoint(fragmentCorner.x, fragmentCorner.y - 1);
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'a2');
- }
- if (!caretRange || caretRange.startContainer !== textNode) {
- caretRange = getCaretRangeFromPoint(fragmentCorner.x - 1, fragmentCorner.y - 1);
+ // create Range from target node and search for visibleOutput Range
+ function getVisibleTextRangeOffsets(textNode, pickerFunc, visibleContentOffsets, frameDimensions) {
+ visibleContentOffsets = visibleContentOffsets || getVisibleContentOffsets();
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'a3');
- }
- if (!caretRange || caretRange.startContainer !== textNode) {
- fragmentCorner.x = Math.floor(fragmentCorner.x);
- fragmentCorner.y = Math.floor(fragmentCorner.y);
- caretRange = getCaretRangeFromPoint(fragmentCorner.x, fragmentCorner.y);
+ // Create a Range around targeted textNode
+ var nodeRange = createRangeFromNode(textNode);
+ // split the range into two halves
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'b0');
- }
- // Desperately try to find it from all angles! Darn sub pixeling..
- if (!caretRange || caretRange.startContainer !== textNode) {
- caretRange = getCaretRangeFromPoint(fragmentCorner.x - 1, fragmentCorner.y);
+ //Split Ratio depends whether we are searching for the lastCFI or first CFI, last CFI will most likely be near the end of the given view , so we allocate the spliting to be 60/40
+ // 40/60 for firstCFI
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'b1');
- }
- if (!caretRange || caretRange.startContainer !== textNode) {
- caretRange = getCaretRangeFromPoint(fragmentCorner.x, fragmentCorner.y - 1);
-
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'b2');
- }
- if (!caretRange || caretRange.startContainer !== textNode) {
- caretRange = getCaretRangeFromPoint(fragmentCorner.x - 1, fragmentCorner.y - 1);
+ var outputRange = getTextRangeOffset(splitRange(nodeRange, parseFloat(50)), visibleContentOffsets, pickerFunc([0, 1]),
+ function (rect) {
+ return (isVerticalWritingMode() ? rect.height : rect.width) && isRectVisible(rect, false, frameDimensions);
+ });
+ return outputRange;
+ // Split Range into two halves for binary search implementation
- if (DEBUG)
- console.log('getVisibleTextRangeOffsetsSelectedByFunc: ', 'b3');
}
- // Still nothing? fall through..
- if (!caretRange) {
- if (DEBUG)
- console.warn('getVisibleTextRangeOffsetsSelectedByFunc: no caret range result');
+ // reverses a directionBit
+ function changeDirection (directionBit) {
+ if (directionBit == 1 ) {return 0;}
+ else { return 1;}
- return null;
}
- if (caretRange.startContainer === textNode) {
- return pickerFunc(
- [{start: caretRange.startOffset, end: caretRange.startOffset + 1},
- {start: caretRange.startOffset - 1, end: caretRange.startOffset}]
- );
- } else {
+ function getTextRangeOffset(startingSet, visibleContentOffsets, directionBit, filterfunc) {
- if (DEBUG)
- console.warn('getVisibleTextRangeOffsetsSelectedByFunc: incorrect caret range result');
+ var currRange = startingSet;
+ var count = 1 ;
- return null;
+ //begin iterative binary search, each iteration will check Range length and visibility
+ // if current range has visible fragments we check the range length
+ // if not visible check the other half.
+ while (currRange.length !=1) {
+ count ++;
+ if(currRange.length <= 1) { return currRange;}
+ var currTextNodeFragments = getRangeClientRectList(currRange[directionBit], visibleContentOffsets);
+ if (fragmentVisible(currTextNodeFragments, filterfunc)) {
+ currRange = splitRange(currRange[directionBit], parseFloat(50));
+ }
+ // No visible fragment Look in other half
+ else {
+ currRange = splitRange(currRange[changeDirection(directionBit)], parseFloat(50));
}
}
+ console.log(count);
+ return currRange[0];
+ }
+
+ function fragmentVisible(fragments,filterfunc) {
+ var visibleFragments = _.filter(fragments,filterfunc);
+ var testBool = visibleFragments.length? true: false;
+ return visibleFragments.length ? true : false;
+ }
function findVisibleLeafNodeCfi(leafNodeList, pickerFunc, targetLeafNode, visibleContentOffsets, frameDimensions, startingParent) {
var index = 0;
+
if (!targetLeafNode) {
index = leafNodeList.indexOf(pickerFunc(leafNodeList));
var leafNode = leafNodeList[index];
@@ -891,16 +843,14 @@ var CfiNavigationLogic = function(options) {
//if a valid text node is found, try to generate a CFI with range offsets
if (textNode && isValidTextNode(textNode)) {
- var visibleRange = getVisibleTextRangeOffsetsSelectedByFunc(textNode, pickerFunc, visibleContentOffsets, frameDimensions);
+
+ var visibleRange = getVisibleTextRangeOffsets(textNode, pickerFunc, visibleContentOffsets, frameDimensions);
if (!visibleRange) {
//the text node is valid, but not visible..
//let's try again with the next node in the list
return findVisibleLeafNodeCfi(leafNodeList, pickerFunc, visibleLeafNode, visibleContentOffsets, frameDimensions, startingParent);
}
- var range = createRange();
- range.setStart(textNode, visibleRange.start);
- range.setEnd(textNode, visibleRange.end);
- return generateCfiFromDomRange(range);
+ return generateCfiFromDomRange(visibleRange);
} else {
//if not then generate a CFI for the element
return self.getCfiForElement(element);
@@ -1089,7 +1039,11 @@ var CfiNavigationLogic = function(options) {
this.getIdBlacklist());
var visibleContentOffsets = getVisibleContentOffsets();
- return {startInfo: null, endInfo: null, clientRect: getNormalizedBoundingRect($element, visibleContentOffsets)};
+ return {
+ startInfo: null,
+ endInfo: null,
+ clientRect: getNormalizedBoundingRect($element, visibleContentOffsets)
+ };
}
};
@@ -1188,7 +1142,7 @@ var CfiNavigationLogic = function(options) {
};
- this.isElementVisible = checkVisibilityByRectangles;
+ this.isElementVisible = this.getElementVisibility;
this.getVisibleElementsWithFilter = function (visibleContentOffsets, filterFunction) {
var $elements = this.getElementsWithFilter($(this.getBodyElement()), filterFunction);
@@ -1213,16 +1167,15 @@ var CfiNavigationLogic = function(options) {
var visibleElements = [];
_.each($elements, function ($node) {
- var node = $node[0];
- var isTextNode = (node.nodeType === Node.TEXT_NODE);
- var element = isTextNode ? node.parentElement : node;
+ var isTextNode = ($node[0].nodeType === Node.TEXT_NODE);
+ var $element = isTextNode ? $node.parent() : $node;
var visibilityPercentage = checkVisibilityByRectangles(
$node, true, visibleContentOffsets, frameDimensions);
if (visibilityPercentage) {
visibleElements.push({
- element: element, // DOM Element is pushed
- textNode: isTextNode ? node : null,
+ element: $element[0], // DOM Element is pushed
+ textNode: isTextNode ? $node[0] : null,
percentVisible: visibilityPercentage
});
}
@@ -1339,7 +1292,6 @@ var CfiNavigationLogic = function(options) {
$leafNodeElements.push($(node));
}
}
- }
if (_cacheEnabled) {
_cache.leafNodeElements.set($root, $leafNodeElements);
@@ -1519,6 +1471,6 @@ var CfiNavigationLogic = function(options) {
//
// }
-};
+ }
return CfiNavigationLogic;
});

implemented iterative binary search for CFIs, without using carretFromRange