JSFiddle - React, Tailwind, and code Playground
HTML
<a href="https://github.com/anpur/client-line-navigator">Demo of LineNavigator</a><br><br>
<input id="file-select" type="file" name="files[]" />
<hr>
<button id="read">Read</button>
<hr>
<button id="search-beginning">Find first</button>
<input type="text" id="find-first-pattern" placeholder="Regexp pattern" />
<button id="search-next">Find next</button>
<hr>
<button id="searchAll">Find all</button>
<input type="text" id="find-all-pattern" placeholder="Regexp pattern" />
(results are limited to 100 lines max)
<hr>
<div id="meta"></div><br>
<div id="output">RESULTS</div>
CSS
input, button {
margin: 5px;
}
#output {
border: 1px solid black;
padding: 5px;
}
JavaScript
// Allows to navigate given sources lines, saving milestones to optimize random reading
// options = {
// milestones: [], // optional: array of milestones, which can be obtained by getMilestones() method and stored to speed up random reading in future
// chunkSize: 1024 * 4, // optional: size of chunk to read at once
// }
function LineNavigator(readChunk, decode, options) {
var self = this;
// verification
if (typeof (readChunk) != 'function') throw 'readChunk argument must be function(offset, length, callback)'
if (typeof (decode) != 'function') throw 'decode argument must be function(buffer, callback)'
// private
options = options ? options : {};
var milestones = options.milestones ? options.milestones : []; // { firstLine, lastLine, offset, length }
var chunkSize = options.chunkSize ? options.chunkSize : 1024 * 4;
var newLineCode = '\n'.charCodeAt(0);
var splitPattern = /\r?\n/;
// Searches for milestone
var getPlaceToStart = function (index) {
for (var i = milestones.length - 1; i >= 0; i--) {
if (milestones[i].lastLine < index)
return { firstLine: milestones[i].lastLine + 1, offset: milestones[i].offset + milestones[i].length };
}
return { firstLine: 0, offset: 0 };
}
// Count lines in chunk and offset of last line, saves milestones
var examineChunk = function(buffer, offset, bytesRead, firstLine) {
var saveMilestone = milestones.length == 0 || milestones[milestones.length - 1].offset < offset;
var lastLine = firstLine - 1;
var length = 0;
// Search for delimiters
for (var i = 0; i < bytesRead; i++) {
if (buffer[i] == newLineCode) {
lastLine++;
length = i + 1;
}
}
// Describe milestone
var milestone = {
firstLine: firstLine,
...