Enabled scrolling to first full match

Former-commit-id: 27d217c412dbd3400674908a89213d6a68d5e500 [formerly 2c4f447f7f7d45bab10c2fac9476df798e615c9a]
Former-commit-id: 1a0e4249cab5fda33c7e66d44366fe1a44293866
This commit is contained in:
Jaifroid 2017-08-20 19:45:01 +01:00
parent 0564aad55c
commit 6343ba7d0e
9 changed files with 249 additions and 57 deletions

View File

@ -32,7 +32,7 @@
<AppXManifest Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\bin\Release\AppxManifest.xml">
<PackagePath>AppxManifest.xml</PackagePath>
<ReRegisterAppIfChanged>true</ReRegisterAppIfChanged>
<Modified>2017-08-19T13:17:37.811</Modified>
<Modified>2017-08-20T18:41:06.455</Modified>
</AppXManifest>
</ItemGroup>
<ItemGroup>
@ -399,7 +399,7 @@
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\css\app.css">
<PackagePath>www\css\app.css</PackagePath>
<Modified>2017-08-17T13:55:29.992</Modified>
<Modified>2017-08-20T18:09:44.037</Modified>
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\css\bootstrap-theme.css">
<PackagePath>www\css\bootstrap-theme.css</PackagePath>
@ -415,7 +415,7 @@
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\index.html">
<PackagePath>www\index.html</PackagePath>
<Modified>2017-08-19T13:17:20.030</Modified>
<Modified>2017-08-20T14:54:56.576</Modified>
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\favicon.ico">
<PackagePath>www\favicon.ico</PackagePath>
@ -543,7 +543,7 @@
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\js\app.js">
<PackagePath>www\js\app.js</PackagePath>
<Modified>2017-08-19T13:17:32.154</Modified>
<Modified>2017-08-20T18:03:09.796</Modified>
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\js\init.js">
<PackagePath>www\js\init.js</PackagePath>
@ -587,7 +587,7 @@
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\js\lib\util.js">
<PackagePath>www\js\lib\util.js</PackagePath>
<Modified>2017-08-19T13:11:37.419</Modified>
<Modified>2017-08-20T18:40:59.430</Modified>
</AppxPackagedFile>
<AppxPackagedFile Include="C:\Users\geoff\Source\Repos\kiwix-js-windows\www\js\lib\xzdec.js">
<PackagePath>www\js\lib\xzdec.js</PackagePath>

View File

@ -31,7 +31,7 @@
[role=region] > header {
/*margin: .5rem 0 1rem 0;*/
margin: 0 0.75em 0 0;
margin: 0 0.75em 0 1px;
text-align: center;
}
@ -116,7 +116,7 @@
background: inherit;
}
.btn-sm {
.btn-sm, .btn-lg {
background: rgba(51,122,183,0.7) !important;
border: transparent !important;
float: none !important;
@ -160,30 +160,34 @@ footer .glyphicon {
border-color: darkgray;
}
.dark a {
color: lightblue !important;
}
.darkfooter .dropdown-menu, .darkfooter .dropdown-menu a {
color: lightblue !important;
background-color: rgba(34,34,34,0.6) !important;
border-color: darkgray;
}
.darkfooter .dropdown-menu a:focus, .darkfooter .dropdown-menu a:hover {
.darkfooter .dropdown-menu a:focus, .darkfooter .dropdown-menu a:hover {
color: lightblue !important;
background: darkslategray !important;
}
}
.dark .list-group-item {
color: lightblue !important;
background-color: #222 !important;
}
.dark a.list-group-item:hover, .dark a.list-group-item:focus, .dark nav a:hover, .dark nav a:focus {
.dark a.list-group-item:hover, .dark a.list-group-item:focus, .dark nav a:hover, .dark nav a:focus, .dark nav .active {
color: lightblue !important;
background-color: darkslategray !important;
}
.dark .btn-default {
.dark .btn-default, .dark .input-group-addon {
background: #222 !important;
color: dimgray !important;
color: lightgray !important;
text-shadow: none;
}

View File

@ -98,14 +98,13 @@
</span>
</span>
</div>
<div class="" id="row2" style="float:right; display:none;">
<div class="" id="row2" style="display:none;">
<span class="input-group">
<input type="search" id="findInArticle" placeholder="Highlight in article..."
class="form-control" />
&nbsp;Matches: 0
<input class="form-control" id="findInArticle" style="display:inline;" type="search" placeholder="Highlight in article...">
<span class="input-group-addon" id="matches">Full: 0</span>
<span class="input-group-addon" id="partial">Partial: 0</span>
</span>
</div>
</form>
</div>
</nav>

View File

@ -95,17 +95,24 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies', 'abstractFile
$('#findText').on('click', function (e) {
var innerDocument = window.frames[0].frameElement.contentDocument || window.frames[0].frameElement.contentWindow.document;
if (innerDocument.body.innerHTML.length < 10) return;
innerDocument = innerDocument.body;
if (!innerDocument) return;
var searchDiv = document.getElementById('row2');
var findInArticle = document.getElementById('findInArticle');
if (searchDiv.style.display == 'none') {
searchDiv.style.display = "inline";
document.getElementById('findText').classList.add("active");
findInArticle.focus();
} else {
findInArticle.value = "";
document.getElementById('matches').innerHTML = "Full: 0";
document.getElementById('partial').innerHTML = "Partial: 0";
searchDiv.style.display = "none";
document.getElementById('findText').classList.remove("active");
}
if (localSearch.remove) {
localSearch.remove();
} else {
} else if (searchDiv.style.display == "inline") {
localSearch = new util.Hilitor(innerDocument);
//TODO: Check right-to-left language support...
localSearch.setMatchType('left');
@ -113,14 +120,32 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies', 'abstractFile
findInArticle.addEventListener('keyup', function (e) {
//Ensure timeout doesn't occur if another key has been pressed within time window
clearTimeout(timer);
//If user pressed Alt-F, exit
if (e.altKey && e.which == 70) return;
var val = this.value;
if (val && e.which == 13) {
localSearch.scrollToFullMatch(val);
return;
}
//Ensure nothing happens if only one value has been entered (not specific enough), but ensure timeout is set
//if no value has been entered (clears highlighting if user deletes all values in search field)
if (~(val.length - 2)) {
timer = setTimeout(function () {
localSearch.apply(val);
var x = localSearch.countMatches();
if (val.length) {
var fullTotal = localSearch.countFullMatches(val);
var partialTotal = localSearch.countPartialMatches();
fullTotal = fullTotal > partialTotal ? partialTotal : fullTotal;
document.getElementById('matches').innerHTML = '<a id="scrollLink" href="#">Full: ' + fullTotal + '</a>';
document.getElementById('partial').innerHTML = "Partial: " + partialTotal;
document.getElementById('scrollLink').addEventListener('click', function () {
localSearch.scrollToFullMatch(val);
});
//localSearch.scrollToFullMatch(val);
} else {
document.getElementById('matches').innerHTML = "Full: 0";
document.getElementById('partial').innerHTML = "Partial: 0";
}
}, 500);
}
}, false);

View File

@ -378,7 +378,6 @@ define(['q'], function(q) {
// Please acknowledge use of this code by including this header.
// For documentation see: http://www.the-art-of-web.com/javascript/search-highlight/
function Hilitor(node, tag) {
var targetNode = node || document.body;
var hiliteTag = tag || "EM";
var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$");
@ -388,7 +387,7 @@ define(['q'], function(q) {
var colorIdx = 0;
var matchRegex = "";
//Add any symbols that may prefix a start string and don't match \b (word boundary)
var leadingSymbols = "’‘¿¡";
var leadingSymbols = "’‘¿¡-";
var openLeft = false;
var openRight = false;
@ -417,11 +416,12 @@ define(['q'], function(q) {
function addAccents(input) {
var retval = input;
retval = retval.replace(/([ao])e/ig, "$1");
retval = retval.replace(/\\u00E[024]/ig, "a");
retval = retval.replace(/\\u00[CE][0124]/ig, "a");
retval = retval.replace(/\\u00E7/ig, "c");
retval = retval.replace(/\\u00E[89AB]|\\u00C[9A]/ig, "e");
retval = retval.replace(/\\u00E[DEF]/ig, "i");
retval = retval.replace(/\\u00F[46]/ig, "o");
retval = retval.replace(/\\u00[CE][DEF]/ig, "i");
retval = retval.replace(/\\u00[DF]1/ig, "n");
retval = retval.replace(/\\u00[FD][346]/ig, "o");
retval = retval.replace(/\\u00F[9BC]/ig, "u");
retval = retval.replace(/\\u00FF/ig, "y");
retval = retval.replace(/\\u00DF/ig, "s");
@ -459,10 +459,78 @@ define(['q'], function(q) {
return retval;
};
this.countMatches = function () {
this.countFullMatches = function (input) {
if (node === undefined || !node) return;
var matches = matchInner(node.body.innerHTML, '<' + hiliteTag + '\\b[^>]*class="hilitor"[^>]*>', '</' + hiliteTag + '>', 'gi');
return matches.length;
var strippedText = node.innerHTML.replace(/<title[^>]*>[\s\S]*<\/title>/i, "");
strippedText = node.innerHTML.replace(/<[^>]*>\s*/g, " ");
if (!strippedText) return 0;
strippedText = strippedText.replace(/(?:&nbsp;|\r?\n|[.,;:?!¿¡-])+/g, " ");
strippedText = strippedText.replace(/\s+/g, " ");
if (!strippedText.length) return 0;
input = input.replace(/[\s.,;:?!¿¡-]+/g, " ");
var inputMatcher = new RegExp(input, "ig");
var matches = strippedText.match(inputMatcher);
if (matches) return matches.length
else return 0;
}
this.countPartialMatches = function () {
if (node === undefined || !node) return;
var matches = matchInner(node.innerHTML, '<' + hiliteTag + '\\b[^>]*class="hilitor"[^>]*>', '</' + hiliteTag + '>', 'gi');
if (matches) return matches.length
else return 0;
//if (matches) {
// var input = document.getElementById('findInArticle').value.replace(/\s*/g, "").toLowerCase();
// var matchedWords = "";
// var buffer = "";
// var countFullMatches = 0;
// for (var i = 0; i < matches.length; i++ ) {
// var instance = matches[i].match(/<[^>]*>([^<]*)</i)[1].toLowerCase();
// buffer += instance;
// var inputMatcher = new RegExp('^' + buffer);
// if (input.match(inputMatcher)) {
// matchedWords += instance;
// } else {
// buffer = "";
// matchedWords = "";
// continue;
// }
// if (~matchedWords.indexOf(input)) {
// countFullMatches++;
// buffer = "";
// matchedWords = "";
// }
// }
// return countFullMatches;
//} else return 0;
}
this.scrollToFullMatch = function (input) {
if (node === undefined || !node) return;
if (!input) return;
//Normalize spaces
input = input.replace(/\s+/g, " ");
var inputWords = input.split(" ");
var testInput = addAccents(input);
var testInput = new RegExp(testInput, "i");
var hilitedNodes = node.getElementsByClassName(className);
var subNodes = [];
var start;
var end = inputWords.length;
for (start = 0; start < hilitedNodes.length; start++) {
for (var f = start; f < end; f++) {
if (f > hilitedNodes.length - 1) break;
subNodes.push(hilitedNodes[f].innerHTML);
}
var nodeText = subNodes.join(" ");
if (testInput.test(nodeText)) {
//hilitedNodes[start].scrollIntoView(true);
$("#articleContent").contents().scrollTop($(hilitedNodes[start]).offset().top);
break;
}
subNodes = [];
end++;
}
}
// recursively apply word highlighting
@ -484,7 +552,7 @@ define(['q'], function(q) {
var match = document.createElement(hiliteTag);
match.appendChild(document.createTextNode(regs[1]));
match.style.backgroundColor = wordColor[regs[1].toLowerCase()];
match.style.setProperty("background-color", wordColor[regs[1].toLowerCase()], "important");
match.style.fontStyle = "inherit";
match.style.color = "#000";
match.className = className;

View File

@ -31,7 +31,7 @@
[role=region] > header {
/*margin: .5rem 0 1rem 0;*/
margin: 0 0.75em 0 0;
margin: 0 0.75em 0 1px;
text-align: center;
}
@ -116,7 +116,7 @@
background: inherit;
}
.btn-sm {
.btn-sm, .btn-lg {
background: rgba(51,122,183,0.7) !important;
border: transparent !important;
float: none !important;
@ -160,30 +160,34 @@ footer .glyphicon {
border-color: darkgray;
}
.dark a {
color: lightblue !important;
}
.darkfooter .dropdown-menu, .darkfooter .dropdown-menu a {
color: lightblue !important;
background-color: rgba(34,34,34,0.6) !important;
border-color: darkgray;
}
.darkfooter .dropdown-menu a:focus, .darkfooter .dropdown-menu a:hover {
.darkfooter .dropdown-menu a:focus, .darkfooter .dropdown-menu a:hover {
color: lightblue !important;
background: darkslategray !important;
}
}
.dark .list-group-item {
color: lightblue !important;
background-color: #222 !important;
}
.dark a.list-group-item:hover, .dark a.list-group-item:focus, .dark nav a:hover, .dark nav a:focus {
.dark a.list-group-item:hover, .dark a.list-group-item:focus, .dark nav a:hover, .dark nav a:focus, .dark nav .active {
color: lightblue !important;
background-color: darkslategray !important;
}
.dark .btn-default {
.dark .btn-default, .dark .input-group-addon {
background: #222 !important;
color: dimgray !important;
color: lightgray !important;
text-shadow: none;
}

View File

@ -98,14 +98,13 @@
</span>
</span>
</div>
<div class="" id="row2" style="float:right; display:none;">
<div class="" id="row2" style="display:none;">
<span class="input-group">
<input type="search" id="findInArticle" placeholder="Highlight in article..."
class="form-control" />
&nbsp;Matches: 0
<input class="form-control" id="findInArticle" style="display:inline;" type="search" placeholder="Highlight in article...">
<span class="input-group-addon" id="matches">Full: 0</span>
<span class="input-group-addon" id="partial">Partial: 0</span>
</span>
</div>
</form>
</div>
</nav>

View File

@ -95,17 +95,24 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies', 'abstractFile
$('#findText').on('click', function (e) {
var innerDocument = window.frames[0].frameElement.contentDocument || window.frames[0].frameElement.contentWindow.document;
if (innerDocument.body.innerHTML.length < 10) return;
innerDocument = innerDocument.body;
if (!innerDocument) return;
var searchDiv = document.getElementById('row2');
var findInArticle = document.getElementById('findInArticle');
if (searchDiv.style.display == 'none') {
searchDiv.style.display = "inline";
document.getElementById('findText').classList.add("active");
findInArticle.focus();
} else {
findInArticle.value = "";
document.getElementById('matches').innerHTML = "Full: 0";
document.getElementById('partial').innerHTML = "Partial: 0";
searchDiv.style.display = "none";
document.getElementById('findText').classList.remove("active");
}
if (localSearch.remove) {
localSearch.remove();
} else {
} else if (searchDiv.style.display == "inline") {
localSearch = new util.Hilitor(innerDocument);
//TODO: Check right-to-left language support...
localSearch.setMatchType('left');
@ -113,14 +120,32 @@ define(['jquery', 'zimArchiveLoader', 'util', 'uiUtil', 'cookies', 'abstractFile
findInArticle.addEventListener('keyup', function (e) {
//Ensure timeout doesn't occur if another key has been pressed within time window
clearTimeout(timer);
//If user pressed Alt-F, exit
if (e.altKey && e.which == 70) return;
var val = this.value;
if (val && e.which == 13) {
localSearch.scrollToFullMatch(val);
return;
}
//Ensure nothing happens if only one value has been entered (not specific enough), but ensure timeout is set
//if no value has been entered (clears highlighting if user deletes all values in search field)
if (~(val.length - 2)) {
timer = setTimeout(function () {
localSearch.apply(val);
var x = localSearch.countMatches();
if (val.length) {
var fullTotal = localSearch.countFullMatches(val);
var partialTotal = localSearch.countPartialMatches();
fullTotal = fullTotal > partialTotal ? partialTotal : fullTotal;
document.getElementById('matches').innerHTML = '<a id="scrollLink" href="#">Full: ' + fullTotal + '</a>';
document.getElementById('partial').innerHTML = "Partial: " + partialTotal;
document.getElementById('scrollLink').addEventListener('click', function () {
localSearch.scrollToFullMatch(val);
});
//localSearch.scrollToFullMatch(val);
} else {
document.getElementById('matches').innerHTML = "Full: 0";
document.getElementById('partial').innerHTML = "Partial: 0";
}
}, 500);
}
}, false);

View File

@ -378,7 +378,6 @@ define(['q'], function(q) {
// Please acknowledge use of this code by including this header.
// For documentation see: http://www.the-art-of-web.com/javascript/search-highlight/
function Hilitor(node, tag) {
var targetNode = node || document.body;
var hiliteTag = tag || "EM";
var skipTags = new RegExp("^(?:" + hiliteTag + "|SCRIPT|FORM)$");
@ -388,7 +387,7 @@ define(['q'], function(q) {
var colorIdx = 0;
var matchRegex = "";
//Add any symbols that may prefix a start string and don't match \b (word boundary)
var leadingSymbols = "’‘¿¡";
var leadingSymbols = "’‘¿¡-";
var openLeft = false;
var openRight = false;
@ -417,11 +416,12 @@ define(['q'], function(q) {
function addAccents(input) {
var retval = input;
retval = retval.replace(/([ao])e/ig, "$1");
retval = retval.replace(/\\u00E[024]/ig, "a");
retval = retval.replace(/\\u00[CE][0124]/ig, "a");
retval = retval.replace(/\\u00E7/ig, "c");
retval = retval.replace(/\\u00E[89AB]|\\u00C[9A]/ig, "e");
retval = retval.replace(/\\u00E[DEF]/ig, "i");
retval = retval.replace(/\\u00F[46]/ig, "o");
retval = retval.replace(/\\u00[CE][DEF]/ig, "i");
retval = retval.replace(/\\u00[DF]1/ig, "n");
retval = retval.replace(/\\u00[FD][346]/ig, "o");
retval = retval.replace(/\\u00F[9BC]/ig, "u");
retval = retval.replace(/\\u00FF/ig, "y");
retval = retval.replace(/\\u00DF/ig, "s");
@ -459,10 +459,78 @@ define(['q'], function(q) {
return retval;
};
this.countMatches = function () {
this.countFullMatches = function (input) {
if (node === undefined || !node) return;
var matches = matchInner(node.body.innerHTML, '<' + hiliteTag + '\\b[^>]*class="hilitor"[^>]*>', '</' + hiliteTag + '>', 'gi');
return matches.length;
var strippedText = node.innerHTML.replace(/<title[^>]*>[\s\S]*<\/title>/i, "");
strippedText = node.innerHTML.replace(/<[^>]*>\s*/g, " ");
if (!strippedText) return 0;
strippedText = strippedText.replace(/(?:&nbsp;|\r?\n|[.,;:?!¿¡-])+/g, " ");
strippedText = strippedText.replace(/\s+/g, " ");
if (!strippedText.length) return 0;
input = input.replace(/[\s.,;:?!¿¡-]+/g, " ");
var inputMatcher = new RegExp(input, "ig");
var matches = strippedText.match(inputMatcher);
if (matches) return matches.length
else return 0;
}
this.countPartialMatches = function () {
if (node === undefined || !node) return;
var matches = matchInner(node.innerHTML, '<' + hiliteTag + '\\b[^>]*class="hilitor"[^>]*>', '</' + hiliteTag + '>', 'gi');
if (matches) return matches.length
else return 0;
//if (matches) {
// var input = document.getElementById('findInArticle').value.replace(/\s*/g, "").toLowerCase();
// var matchedWords = "";
// var buffer = "";
// var countFullMatches = 0;
// for (var i = 0; i < matches.length; i++ ) {
// var instance = matches[i].match(/<[^>]*>([^<]*)</i)[1].toLowerCase();
// buffer += instance;
// var inputMatcher = new RegExp('^' + buffer);
// if (input.match(inputMatcher)) {
// matchedWords += instance;
// } else {
// buffer = "";
// matchedWords = "";
// continue;
// }
// if (~matchedWords.indexOf(input)) {
// countFullMatches++;
// buffer = "";
// matchedWords = "";
// }
// }
// return countFullMatches;
//} else return 0;
}
this.scrollToFullMatch = function (input) {
if (node === undefined || !node) return;
if (!input) return;
//Normalize spaces
input = input.replace(/\s+/g, " ");
var inputWords = input.split(" ");
var testInput = addAccents(input);
var testInput = new RegExp(testInput, "i");
var hilitedNodes = node.getElementsByClassName(className);
var subNodes = [];
var start;
var end = inputWords.length;
for (start = 0; start < hilitedNodes.length; start++) {
for (var f = start; f < end; f++) {
if (f > hilitedNodes.length - 1) break;
subNodes.push(hilitedNodes[f].innerHTML);
}
var nodeText = subNodes.join(" ");
if (testInput.test(nodeText)) {
//hilitedNodes[start].scrollIntoView(true);
$("#articleContent").contents().scrollTop($(hilitedNodes[start]).offset().top);
break;
}
subNodes = [];
end++;
}
}
// recursively apply word highlighting
@ -484,7 +552,7 @@ define(['q'], function(q) {
var match = document.createElement(hiliteTag);
match.appendChild(document.createTextNode(regs[1]));
match.style.backgroundColor = wordColor[regs[1].toLowerCase()];
match.style.setProperty("background-color", wordColor[regs[1].toLowerCase()], "important");
match.style.fontStyle = "inherit";
match.style.color = "#000";
match.className = className;