mirror of
https://github.com/simon987/Simple-Incremental-Search-Tool.git
synced 2025-12-14 07:39:05 +00:00
Code cleanup
This commit is contained in:
604
static/js/search.js
Normal file
604
static/js/search.js
Normal file
@@ -0,0 +1,604 @@
|
||||
let searchBar = document.getElementById("searchBar");
|
||||
let pathBar = document.getElementById("pathBar");
|
||||
let must_match = true;
|
||||
let scroll_id = null;
|
||||
let docCount = 0;
|
||||
let searchQueued = false;
|
||||
let coolingDown = false;
|
||||
let selectedDirs = [];
|
||||
|
||||
function toggleSearchBar() {
|
||||
must_match = !must_match;
|
||||
searchQueued = true;
|
||||
}
|
||||
|
||||
let tree = new InspireTree({
|
||||
selection: {
|
||||
mode: 'checkbox'
|
||||
},
|
||||
data: mimeMap
|
||||
});
|
||||
new InspireTreeDOM(tree, {
|
||||
target: '.tree'
|
||||
});
|
||||
|
||||
//Select all
|
||||
tree.select();
|
||||
tree.node("any").deselect();
|
||||
|
||||
tree.on("node.click", function(event, node, handler) {
|
||||
event.preventTreeDefault();
|
||||
|
||||
if (node.id === "any") {
|
||||
|
||||
if (!node.itree.state.checked) {
|
||||
tree.deselect();
|
||||
}
|
||||
} else {
|
||||
tree.node("any").deselect();
|
||||
}
|
||||
|
||||
handler();
|
||||
searchQueued = true;
|
||||
});
|
||||
|
||||
new autoComplete({
|
||||
selector: '#pathBar',
|
||||
minChars: 1,
|
||||
delay: 75,
|
||||
renderItem: function (item){
|
||||
return '<div class="autocomplete-suggestion" data-val="' + item + '">' + item + '</div>';
|
||||
},
|
||||
source: async function(term, suggest) {
|
||||
term = term.toLowerCase();
|
||||
|
||||
const choices = await getPathChoices();
|
||||
|
||||
let matches = [];
|
||||
for (let i=0; i<choices.length; i++) {
|
||||
if (~choices[i].toLowerCase().indexOf(term)) {
|
||||
matches.push(choices[i]);
|
||||
}
|
||||
}
|
||||
suggest(matches);
|
||||
},
|
||||
onSelect: function() {
|
||||
searchQueued = true;
|
||||
}
|
||||
});
|
||||
|
||||
function makeStatsCard(searchResult) {
|
||||
|
||||
let statsCard = document.createElement("div");
|
||||
statsCard.setAttribute("class", "card");
|
||||
let statsCardBody = document.createElement("div");
|
||||
statsCardBody.setAttribute("class", "card-body");
|
||||
|
||||
let stat = document.createElement("p");
|
||||
stat.appendChild(document.createTextNode(searchResult["hits"]["total"] + " results in " + searchResult["took"] + "ms"));
|
||||
|
||||
let sizeStat = document.createElement("span");
|
||||
sizeStat.appendChild(document.createTextNode(humanFileSize(searchResult["aggregations"]["total_size"]["value"])));
|
||||
|
||||
statsCardBody.appendChild(stat);
|
||||
statsCardBody.appendChild(sizeStat);
|
||||
statsCard.appendChild(statsCardBody);
|
||||
|
||||
return statsCard;
|
||||
}
|
||||
|
||||
function makeResultContainer() {
|
||||
let resultContainer = document.createElement("div");
|
||||
resultContainer.setAttribute("class", "card-columns");
|
||||
|
||||
return resultContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://stackoverflow.com/questions/10420352
|
||||
*/
|
||||
function humanFileSize(bytes) {
|
||||
|
||||
if(bytes === 0) {
|
||||
return "? B"
|
||||
}
|
||||
|
||||
let thresh = 1000;
|
||||
if(Math.abs(bytes) < thresh) {
|
||||
return bytes + ' B';
|
||||
}
|
||||
let units = ['kB','MB','GB','TB','PB','EB','ZB','YB'];
|
||||
let u = -1;
|
||||
do {
|
||||
bytes /= thresh;
|
||||
++u;
|
||||
} while(Math.abs(bytes) >= thresh && u < units.length - 1);
|
||||
|
||||
return bytes.toFixed(1) + ' ' + units[u];
|
||||
}
|
||||
|
||||
|
||||
function initPopover() {
|
||||
$('[data-toggle="popover"]').popover({
|
||||
trigger: "focus",
|
||||
delay: { "show": 0, "hide": 100 },
|
||||
placement: "bottom",
|
||||
html: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable gif loading on hover
|
||||
* @param thumbnail
|
||||
* @param documentId
|
||||
*/
|
||||
function gifOver(thumbnail, documentId) {
|
||||
let callee = arguments.callee;
|
||||
|
||||
thumbnail.addEventListener("mouseover", function () {
|
||||
|
||||
thumbnail.mouseStayedOver = true;
|
||||
|
||||
window.setTimeout(function() {
|
||||
if (thumbnail.mouseStayedOver) {
|
||||
thumbnail.removeEventListener('mouseover', callee, false);
|
||||
|
||||
//Load gif
|
||||
thumbnail.setAttribute("src", "/file/" + documentId);
|
||||
}
|
||||
}, 750); //todo grab hover time from config
|
||||
|
||||
});
|
||||
|
||||
thumbnail.addEventListener("mouseout", function() {
|
||||
//Reset timer
|
||||
thumbnail.mouseStayedOver = false;
|
||||
thumbnail.setAttribute("src", "/thumb/" + documentId);
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
function videoOver(thumbnail, imgWrapper, thumbnailOverlay, documentId, docCard) {
|
||||
|
||||
docCard.addEventListener("focus", function () {
|
||||
let callee = arguments.callee;
|
||||
docCard.mouseStayedOver = true;
|
||||
|
||||
window.setTimeout(function() {
|
||||
|
||||
if(docCard.mouseStayedOver) {
|
||||
docCard.removeEventListener('focus', callee, false);
|
||||
|
||||
imgWrapper.removeChild(thumbnail);
|
||||
imgWrapper.removeChild(thumbnailOverlay);
|
||||
|
||||
let video = document.createElement("video");
|
||||
let vidSource = document.createElement("source");
|
||||
vidSource.setAttribute("src", "/file/" + documentId);
|
||||
vidSource.setAttribute("type", "video/webm");
|
||||
video.appendChild(vidSource);
|
||||
video.setAttribute("class", "fit");
|
||||
video.setAttribute("loop", "");
|
||||
video.setAttribute("controls", "");
|
||||
video.setAttribute("preload", "");
|
||||
video.setAttribute("poster", "/thumb/" + documentId);
|
||||
imgWrapper.appendChild(video);
|
||||
|
||||
video.addEventListener("dblclick", function() {
|
||||
video.webkitRequestFullScreen();
|
||||
})
|
||||
}
|
||||
}, 750);
|
||||
});
|
||||
docCard.addEventListener("blur", function() {
|
||||
docCard.mouseStayedOver = false;
|
||||
});
|
||||
}
|
||||
|
||||
function downloadPopover(element, documentId) {
|
||||
element.setAttribute("data-content",
|
||||
'<a class="btn btn-sm btn-primary" href="/dl/'+ documentId +'"><i class="fas fa-download"></i> Download</a>' +
|
||||
'<a class="btn btn-sm btn-success" style="margin-left:3px;" href="/file/'+ documentId + '" target="_blank"><i class="fas fa-eye"></i> View</a>');
|
||||
element.setAttribute("data-toggle", "popover");
|
||||
element.addEventListener("mouseover", function() {
|
||||
element.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param hit
|
||||
* @returns {Element}
|
||||
*/
|
||||
function createDocCard(hit) {
|
||||
|
||||
let docCard = document.createElement("div");
|
||||
docCard.setAttribute("class", "card");
|
||||
docCard.setAttribute("tabindex", "-1");
|
||||
|
||||
let docCardBody = document.createElement("div");
|
||||
docCardBody.setAttribute("class", "card-body document");
|
||||
|
||||
let link = document.createElement("a");
|
||||
link.setAttribute("href", "/document/" + hit["_id"]);
|
||||
link.setAttribute("target", "_blank");
|
||||
|
||||
//Title
|
||||
let title = document.createElement("p");
|
||||
title.setAttribute("class", "file-title");
|
||||
let extension = hit["_source"].hasOwnProperty("extension") && hit["_source"]["extension"] !== "" ? "." + hit["_source"]["extension"] : "";
|
||||
|
||||
if (hit.hasOwnProperty("highlight") && hit["highlight"].hasOwnProperty("name")) {
|
||||
title.insertAdjacentHTML('afterbegin', hit["highlight"]["name"] + extension);
|
||||
} else {
|
||||
title.appendChild(document.createTextNode(hit["_source"]["name"] + extension));
|
||||
}
|
||||
|
||||
title.setAttribute("title", hit["_source"]["path"] + hit["_source"]["name"] + extension);
|
||||
docCard.appendChild(title);
|
||||
|
||||
let tagContainer = document.createElement("div");
|
||||
tagContainer.setAttribute("class", "card-text");
|
||||
|
||||
if (hit["_source"].hasOwnProperty("mime") && hit["_source"]["mime"] !== null) {
|
||||
|
||||
let tags = [];
|
||||
let thumbnail = null;
|
||||
let thumbnailOverlay = null;
|
||||
let imgWrapper = document.createElement("div");
|
||||
imgWrapper.setAttribute("style", "position: relative");
|
||||
|
||||
let mimeCategory = hit["_source"]["mime"].split("/")[0];
|
||||
|
||||
//Thumbnail
|
||||
switch (mimeCategory) {
|
||||
|
||||
case "video":
|
||||
case "image":
|
||||
thumbnail = document.createElement("img");
|
||||
thumbnail.setAttribute("class", "card-img-top");
|
||||
thumbnail.setAttribute("src", "/thumb/" + hit["_id"]);
|
||||
break;
|
||||
}
|
||||
|
||||
//Thumbnail overlay
|
||||
switch (mimeCategory) {
|
||||
|
||||
case "image":
|
||||
thumbnailOverlay = document.createElement("div");
|
||||
thumbnailOverlay.setAttribute("class", "card-img-overlay");
|
||||
|
||||
//Resolution
|
||||
let resolutionBadge = document.createElement("span");
|
||||
resolutionBadge.setAttribute("class", "badge badge-resolution");
|
||||
if (hit["_source"].hasOwnProperty("width")) {
|
||||
resolutionBadge.appendChild(document.createTextNode(hit["_source"]["width"] + "x" + hit["_source"]["height"]));
|
||||
}
|
||||
thumbnailOverlay.appendChild(resolutionBadge);
|
||||
|
||||
var format = hit["_source"]["format"];
|
||||
|
||||
//Hover
|
||||
if(format === "GIF") {
|
||||
gifOver(thumbnail, hit["_id"]);
|
||||
}
|
||||
break;
|
||||
|
||||
case "video":
|
||||
thumbnailOverlay = document.createElement("div");
|
||||
thumbnailOverlay.setAttribute("class", "card-img-overlay");
|
||||
|
||||
//Duration
|
||||
let durationBadge = document.createElement("span");
|
||||
durationBadge.setAttribute("class", "badge badge-resolution");
|
||||
durationBadge.appendChild(document.createTextNode(parseFloat(hit["_source"]["duration"]).toFixed(2) + "s"));
|
||||
thumbnailOverlay.appendChild(durationBadge);
|
||||
|
||||
//Hover
|
||||
videoOver(thumbnail, imgWrapper, thumbnailOverlay, hit["_id"], docCard)
|
||||
|
||||
}
|
||||
|
||||
//Tags
|
||||
switch (mimeCategory) {
|
||||
|
||||
case "video":
|
||||
if (hit["_source"].hasOwnProperty("format_long_name")) {
|
||||
let formatTag = document.createElement("span");
|
||||
formatTag.setAttribute("class", "badge badge-pill badge-video");
|
||||
formatTag.appendChild(document.createTextNode(hit["_source"]["format_long_name"].replace(" ", "")));
|
||||
tags.push(formatTag);
|
||||
}
|
||||
|
||||
break;
|
||||
case "image": {
|
||||
let formatTag = document.createElement("span");
|
||||
formatTag.setAttribute("class", "badge badge-pill badge-image");
|
||||
formatTag.appendChild(document.createTextNode(format));
|
||||
tags.push(formatTag);
|
||||
}
|
||||
break;
|
||||
case "audio": {
|
||||
if (hit["_source"].hasOwnProperty("format_long_name")) {
|
||||
let formatTag = document.createElement("span");
|
||||
formatTag.setAttribute("class", "badge badge-pill badge-audio");
|
||||
formatTag.appendChild(document.createTextNode(hit["_source"]["format_long_name"]));
|
||||
tags.push(formatTag);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
case "text": {
|
||||
let formatTag = document.createElement("span");
|
||||
formatTag.setAttribute("class", "badge badge-pill badge-text");
|
||||
formatTag.appendChild(document.createTextNode(hit["_source"]["encoding"]));
|
||||
tags.push(formatTag);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//Content
|
||||
if (hit.hasOwnProperty("highlight") && hit["highlight"].hasOwnProperty("content")) {
|
||||
|
||||
let contentDiv = document.createElement("div");
|
||||
contentDiv.setAttribute("class", "content-div bg-light");
|
||||
contentDiv.insertAdjacentHTML('afterbegin', hit["highlight"]["content"][0]);
|
||||
docCard.appendChild(contentDiv);
|
||||
}
|
||||
|
||||
//Audio
|
||||
if (mimeCategory === "audio" && hit["_source"].hasOwnProperty("format_long_name")) {
|
||||
|
||||
let audio = document.createElement("audio");
|
||||
audio.setAttribute("preload", "none");
|
||||
audio.setAttribute("class", "audio-fit fit");
|
||||
audio.setAttribute("controls", "");
|
||||
audio.setAttribute("type", hit["_source"]["mime"]);
|
||||
audio.setAttribute("src", "file/" + hit["_id"]);
|
||||
|
||||
docCard.appendChild(audio)
|
||||
}
|
||||
|
||||
if (thumbnail !== null) {
|
||||
imgWrapper.appendChild(thumbnail);
|
||||
docCard.appendChild(imgWrapper);
|
||||
}
|
||||
if (thumbnailOverlay !== null) {
|
||||
imgWrapper.appendChild(thumbnailOverlay);
|
||||
}
|
||||
|
||||
for (let i = 0; i < tags.length; i++) {
|
||||
tagContainer.appendChild(tags[i]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
//Size tag
|
||||
let sizeTag = document.createElement("small");
|
||||
sizeTag.appendChild(document.createTextNode(humanFileSize(hit["_source"]["size"])));
|
||||
sizeTag.setAttribute("class", "text-muted");
|
||||
tagContainer.appendChild(sizeTag);
|
||||
|
||||
|
||||
//Download button
|
||||
downloadPopover(docCard, hit["_id"]);
|
||||
|
||||
docCardBody.appendChild(link);
|
||||
docCard.appendChild(docCardBody);
|
||||
|
||||
link.appendChild(title);
|
||||
docCardBody.appendChild(tagContainer);
|
||||
|
||||
return docCard;
|
||||
}
|
||||
|
||||
function makePageIndicator(searchResult) {
|
||||
let pageIndicator = document.createElement("div");
|
||||
pageIndicator.appendChild(document.createTextNode(docCount + " / " +searchResult["hits"]["total"]));
|
||||
return pageIndicator;
|
||||
}
|
||||
|
||||
|
||||
function insertHits(resultContainer, hits) {
|
||||
for (let i = 0 ; i < hits.length; i++) {
|
||||
resultContainer.appendChild(createDocCard(hits[i]));
|
||||
docCount++;
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("scroll", function () {
|
||||
|
||||
if (!coolingDown) {
|
||||
|
||||
let threshold = 350;
|
||||
|
||||
if ((window.innerHeight + window.scrollY) >= document.body.offsetHeight - threshold) {
|
||||
//load next page
|
||||
|
||||
let xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
|
||||
let searchResult = JSON.parse(this.responseText);
|
||||
let searchResults = document.getElementById("searchResults");
|
||||
let hits = searchResult["hits"]["hits"];
|
||||
|
||||
//Page indicator
|
||||
let pageIndicator = makePageIndicator(searchResult);
|
||||
searchResults.appendChild(pageIndicator);
|
||||
|
||||
//Result container
|
||||
let resultContainer = makeResultContainer();
|
||||
searchResults.appendChild(resultContainer);
|
||||
|
||||
insertHits(resultContainer, hits);
|
||||
|
||||
initPopover();
|
||||
|
||||
if (hits.length !== 0) {
|
||||
coolingDown = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/scroll?scroll_id=" + scroll_id, true);
|
||||
xhttp.send();
|
||||
coolingDown = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function getSelectedMimeTypes() {
|
||||
let mimeTypes = [];
|
||||
|
||||
let selected = tree.selected();
|
||||
|
||||
for (let i = 0; i < selected.length; i++) {
|
||||
|
||||
if(selected[i].id === "any") {
|
||||
return "any"
|
||||
}
|
||||
|
||||
//Only get children
|
||||
if (selected[i].text.indexOf("(") !== -1) {
|
||||
mimeTypes.push(selected[i].id);
|
||||
}
|
||||
}
|
||||
|
||||
return mimeTypes
|
||||
}
|
||||
|
||||
function search() {
|
||||
|
||||
if (searchQueued === true) {
|
||||
searchQueued = false;
|
||||
|
||||
//Clear old search results
|
||||
let searchResults = document.getElementById("searchResults");
|
||||
while (searchResults.firstChild) {
|
||||
searchResults.removeChild(searchResults.firstChild);
|
||||
}
|
||||
|
||||
let query = searchBar.value;
|
||||
|
||||
let xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
|
||||
let searchResult = JSON.parse(this.responseText);
|
||||
scroll_id = searchResult["_scroll_id"];
|
||||
|
||||
//Search stats
|
||||
searchResults.appendChild(makeStatsCard(searchResult));
|
||||
|
||||
//Autocomplete
|
||||
if (searchResult.hasOwnProperty("suggest") && searchResult["suggest"].hasOwnProperty("path")) {
|
||||
pathAutoComplete = [];
|
||||
for (let i = 0; i < searchResult["suggest"]["path"][0]["options"].length; i++) {
|
||||
pathAutoComplete.push(searchResult["suggest"]["path"][0]["options"][i].text)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Setup page
|
||||
let resultContainer = makeResultContainer();
|
||||
searchResults.appendChild(resultContainer);
|
||||
|
||||
//Insert search results (hits)
|
||||
docCount = 0;
|
||||
insertHits(resultContainer, searchResult["hits"]["hits"]);
|
||||
|
||||
//Initialise download/view button popover
|
||||
initPopover();
|
||||
}
|
||||
};
|
||||
|
||||
xhttp.open("POST", "/search", true);
|
||||
|
||||
let postBody = {};
|
||||
postBody.q = query;
|
||||
postBody.size_min = size_min;
|
||||
postBody.size_max = size_max;
|
||||
postBody.mime_types = getSelectedMimeTypes();
|
||||
postBody.must_match = must_match;
|
||||
postBody.directories = selectedDirs;
|
||||
postBody.path = pathBar.value.replace(/\/$/, "").toLowerCase(); //remove trailing slashes
|
||||
xhttp.setRequestHeader('content-type', 'application/json');
|
||||
xhttp.send(JSON.stringify(postBody));
|
||||
}
|
||||
}
|
||||
|
||||
let pathAutoComplete = [];
|
||||
let size_min = 0;
|
||||
let size_max = 10000000000000;
|
||||
|
||||
searchBar.addEventListener("keyup", function () {
|
||||
searchQueued = true;
|
||||
});
|
||||
|
||||
//Size slider
|
||||
let sizeSlider = $("#sizeSlider").ionRangeSlider({
|
||||
type: "double",
|
||||
grid: false,
|
||||
force_edges: true,
|
||||
min: 0,
|
||||
max: 3684.03149864,
|
||||
from: 0,
|
||||
to: 3684.03149864,
|
||||
min_interval: 5,
|
||||
drag_interval: true,
|
||||
prettify: function (num) {
|
||||
|
||||
if(num === 0) {
|
||||
return "0 B"
|
||||
} else if (num >= 3684) {
|
||||
return humanFileSize(num * num * num) + "+";
|
||||
}
|
||||
|
||||
return humanFileSize(num * num * num)
|
||||
},
|
||||
onChange: function(e) {
|
||||
size_min = (e.from * e.from * e.from);
|
||||
size_max = (e.to * e.to * e.to);
|
||||
|
||||
if (e.to >= 3684) {
|
||||
size_max = 10000000000000;
|
||||
}
|
||||
|
||||
searchQueued = true;
|
||||
}
|
||||
})[0];
|
||||
|
||||
//Directories select
|
||||
function updateDirectories() {
|
||||
let selected = $('#directories').find('option:selected');
|
||||
selectedDirs = [];
|
||||
$(selected).each(function(){
|
||||
selectedDirs.push(parseInt($(this).val()));
|
||||
});
|
||||
|
||||
searchQueued = true;
|
||||
}
|
||||
document.getElementById("directories").addEventListener("change", updateDirectories);
|
||||
updateDirectories();
|
||||
searchQueued = false;
|
||||
|
||||
//Suggest
|
||||
function getPathChoices() {
|
||||
return new Promise(getPaths => {
|
||||
|
||||
let xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState === 4 && this.status === 200) {
|
||||
getPaths(JSON.parse(xhttp.responseText))
|
||||
}
|
||||
};
|
||||
xhttp.open("GET", "/suggest?prefix=" + pathBar.value, true);
|
||||
xhttp.send();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
window.setInterval(search, 75);
|
||||
Reference in New Issue
Block a user