EPUBJS.Map = function(layout){ this.layout = layout; }; EPUBJS.Map.prototype.section = function(view) { var ranges = this.findRanges(view); var map = this.rangeListToCfiList(view, ranges); return map; }; EPUBJS.Map.prototype.page = function(view, start, end) { var root = view.document.body; return this.rangePairToCfiPair(view.section, { start: this.findStart(root, start, end), end: this.findEnd(root, start, end) }); }; EPUBJS.Map.prototype.walk = function(root, func) { //var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, null, false); var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode: function (node) { if ( node.data.trim().length > 0 ) { return NodeFilter.FILTER_ACCEPT; } else { return NodeFilter.FILTER_REJECT; } } }, false); var node; var result; while ((node = treeWalker.nextNode())) { result = func(node); if(result) break; } return result; }; EPUBJS.Map.prototype.findRanges = function(view){ var columns = []; var count = this.layout.count(view); var column = this.layout.column; var gap = this.layout.gap; var start, end; for (var i = 0; i < count.pages; i++) { start = (column + gap) * i; end = (column * (i+1)) + (gap * i); columns.push({ start: this.findStart(view.document.body, start, end), end: this.findEnd(view.document.body, start, end) }); }; return columns; }; EPUBJS.Map.prototype.findStart = function(root, start, end){ var stack = [root]; var $el; var found; var $prev; while (stack.length) { $el = stack.shift(); found = this.walk($el, function(node){ var left, right; var elPos var elRange; if(node.nodeType == Node.TEXT_NODE){ elRange = document.createRange(); elRange.selectNodeContents(node); elPos = elRange.getBoundingClientRect(); } else { elPos = node.getBoundingClientRect(); } left = elPos.left; right = elPos.right; if( left >= start && left <= end ) { return node; } else if (right > start) { return node; } else { $prev = node; stack.push(node); } }); if(found) { return this.findTextStartRange(found, start, end); } } // Return last element return this.findTextStartRange($prev, start, end); }; EPUBJS.Map.prototype.findEnd = function(root, start, end){ var stack = [root]; var $el; var $prev; var found; while (stack.length) { $el = stack.shift(); found = this.walk($el, function(node){ var left, right; var elPos var elRange; if(node.nodeType == Node.TEXT_NODE){ elRange = document.createRange(); elRange.selectNodeContents(node); elPos = elRange.getBoundingClientRect(); } else { elPos = node.getBoundingClientRect(); } left = elPos.left; right = elPos.right; if(left > end && $prev) { return $prev } else if(right > end) { return node; } else { $prev = node; stack.push(node); } }); if(found){ return this.findTextEndRange(found, start, end); } } // end of chapter return this.findTextEndRange($prev, start, end); }; EPUBJS.Map.prototype.findTextStartRange = function(node, start, end){ var ranges = this.splitTextNodeIntoRanges(node); var prev; var range; var pos; for (var i = 0; i < ranges.length; i++) { range = ranges[i]; pos = range.getBoundingClientRect(); if( pos.left >= start ) { return range; } prev = range; }; return ranges[0]; }; EPUBJS.Map.prototype.findTextEndRange = function(node, start, end){ var ranges = this.splitTextNodeIntoRanges(node); var prev; var range; var pos; for (var i = 0; i < ranges.length; i++) { range = ranges[i]; pos = range.getBoundingClientRect(); if(pos.left > end) { return prev; } else if(pos.right > end) { return range; } prev = range; }; // Ends before limit return ranges[ranges.length-1]; }; EPUBJS.Map.prototype.splitTextNodeIntoRanges = function(node, _splitter){ var ranges = []; var text = node.textContent.trim(); var range; var rect; var list; var doc = node.ownerDocument; var splitter = _splitter || " "; pos = text.indexOf(splitter); if(pos === -1) { range = doc.createRange(); range.selectNodeContents(node); return [range]; } range = doc.createRange(); range.setStart(node, 0); range.setEnd(node, pos); ranges.push(range); range = false; while ( pos != -1 ) { pos = text.indexOf(splitter, pos + 1); if(pos > 0) { if(range) { range.setEnd(node, pos); ranges.push(range); } range = doc.createRange(); range.setStart(node, pos+1); } } if(range) { range.setEnd(node, text.length); ranges.push(range); } return ranges; }; EPUBJS.Map.prototype.rangePairToCfiPair = function(section, rangePair){ var startRange = rangePair.start; var endRange = rangePair.end; startRange.collapse(true); endRange.collapse(true); startCfi = section.cfiFromRange(startRange); endCfi = section.cfiFromRange(endRange); return { start: startCfi, end: endCfi } }; EPUBJS.Map.prototype.rangeListToCfiList = function(view, columns){ var map = []; var rangePair, cifPair; for (var i = 0; i < columns.length; i++) { cifPair = this.rangePairToCfiPair(view.section, columns[i]); map.push(cifPair); }; return map; };