epub-full-text-search
Version:
163 lines (117 loc) • 4.91 kB
JavaScript
;
var _cheerio = require('cheerio');
var _cheerio2 = _interopRequireDefault(_cheerio);
var _string = require('string');
var _string2 = _interopRequireDefault(_string);
var _MathML = require('./MathML.js');
var _MathML2 = _interopRequireDefault(_MathML);
var _fsReadfilePromise = require('fs-readfile-promise');
var _fsReadfilePromise2 = _interopRequireDefault(_fsReadfilePromise);
var _validUrl = require('valid-url');
var _validUrl2 = _interopRequireDefault(_validUrl);
var _Logger = require('./Logger');
var _Logger2 = _interopRequireDefault(_Logger);
var _Helper = require('./Helper');
var _Helper2 = _interopRequireDefault(_Helper);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**************
* public
*************/
exports.generate = function (data) {
return new Promise(function (resolve, reject) {
var fetch = _validUrl2.default.isUri(data.spineItemPath) ? _Helper2.default.getContent(data.spineItemPath) : (0, _fsReadfilePromise2.default)(data.spineItemPath);
fetch.then(function (html) {
var $dom = _cheerio2.default.load(html);
var needMathMlOffset = false;
_MathML2.default.process($dom, function (needOffset) {
needMathMlOffset = needOffset;
});
var elements = getAllTextNodesContainsQuery(data.searchFor, $dom);
var result = generateCFIs(data.baseCfi, elements, needMathMlOffset);
resolve(result);
}).catch(function (err) {
_Logger2.default.log('error', err);
reject(err);
});
});
};
/**************
* private
*************/
function generateCFIs(cfiBase, elements, needOffset) {
var cfiList = [];
for (var key in elements) {
var cfiParts = [];
var textNode = elements[key].textNode;
var child = textNode.parent();
var childContents = child.contents();
var textNodeIndex = childContents.index(textNode) + 1;
// "mixed content" context
// the first chunk is located before the first child element
// <p><span></span>text</p>
if (childContents.first()[0].type === "tag") {
textNodeIndex += 1;
}
var parent = child.parent();
while (parent[0]) {
var index = child.index(),
inOff = needOffset && parent[0].name === 'body',
id = child.attr('id'),
idSelector = id ? '[' + id + ']' : '',
part = (index + 1) * 2 + (inOff ? 2 : 0) + idSelector;
cfiParts.unshift(part);
child = parent;
parent = child.parent();
}
var startOffset = elements[key].range.startOffset,
endOffset = elements[key].range.endOffset;
var inlinePath = ',/' + textNodeIndex + ':';
var cfi = cfiBase + '/' + cfiParts.join('/') + inlinePath + startOffset + inlinePath + endOffset;
cfiList.push({ cfi: cfi, excerpt: elements[key].excerpt });
}
return cfiList;
}
function getAllTextNodesContainsQuery(q, $) {
var matches = [];
$('body').find("*").contents().filter(function () {
return this.nodeType === 3 && $(this).text().toLowerCase().indexOf(q) > -1;
}).each(function () {
var text = $(this).text();
// the query can match several times in the same text element
// so it necessary to get all indices
var indices = allIndexOf(text, q);
for (var i in indices) {
var startOffset = indices[i],
endOffset = startOffset + q.length;
var excerptLength = 80;
var startExcerpt = startOffset - Math.floor((excerptLength - q.length) / 2);
if (startExcerpt < 0) {
// Start from the begining of the text if there are not enough characters before it
startExcerpt = 0;
}
var excerpt = text.slice(startExcerpt);
excerpt = excerpt.slice(excerpt.indexOf(' ') + 1); // trim to start on a full word
excerpt = (0, _string2.default)(excerpt).truncate(excerptLength).s;
if (startExcerpt > 0) {
excerpt = '...' + excerpt; // add begining ellipsis
}
matches.push({
textNode: $(this),
range: {
startOffset: startOffset,
endOffset: endOffset
},
excerpt: excerpt
});
}
});
return matches;
}
function allIndexOf(str, q) {
var matchCase = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
var indices = [];
if (!matchCase) str = str.toLowerCase();
for (var pos = str.indexOf(q); pos !== -1; pos = str.indexOf(q, pos + 1)) {
indices.push(pos);
}return indices;
}