refactor most javascript code, moved what can be moved into files_support.js, made keydown() items only apply when I go to the viewer code

This commit is contained in:
2025-10-04 23:25:46 +10:00
parent 2f5c6ec949
commit 40f0b5d369
3 changed files with 380 additions and 396 deletions

15
TODO
View File

@@ -1,20 +1,15 @@
###
# get override data into view
# should start with an empty DB and test - definitely no dirs in storage_sp gives:
# dir_id=dir_arr[0]
# IndexError: list index out of range
# TEST this, code in files will work, but now passes root_eid=0 for
# this - so need GUI to work - may even be good to put an alert up - its so odd to hvae not root dir ONLY happens when no data
# empty directories are sometimes showing "No matches for: 'undefined'" <- should only comes up for search in URL???
# should start with an empty DB and test
# definitely no dirs in storage_sp I now pass root_eid=0 for this
# BUT - need GUI to work - may even be good to put an alert up - its so odd to have not root dir ONLY happens when no data
# empty directories (2017/20171015-test/...) showing "No matches for: 'undefined'" <- should only comes up for search in URL???
# think I killed pa_job_manager without passing an eid to a transform job, shouldn't crash
# SHOULD JUST get AI to help clean-up and write defensive code here...
# also all the add ref img/add override, etc are non-functional - FIX the override* stuff first to get table/naming consistency as that is half the problem
# delete button also uses current? (eid is empty anyway)
# move as much jscript into *_support, as possible (if there are no more {{ - # then move it)
# convert move_paths to a json setup
# ALSO revisit this move_paths to be as safe as possible ultimately, triple-check there are no leading / or .. 's
# TEST everything (don't forget keybindings)
# transforms -> should consider removing the pull logic to see if a job is finished (have job-mgr push data back? while more rest, not sure b/e EVER sends data to F/E at moment)
# TEST everything (don't forget keybindings,e.g. delete)
###
### major fix - go to everywhere I call GetEntries(), and redo the logic totally...

View File

@@ -4,6 +4,21 @@ ICON["Import"]="import"
ICON["Storage"]="db"
ICON["Bin"]="trash"
// function called when we get another page from inside the files view
function getPageFigures(res, viewingIdx)
{
// add all the figures to files_div
drawPageOfFigures()
}
// function called when we get another page from inside the viewer
function getPageViewer(res, viewingIdx)
{
document.viewing=document.entries[viewingIdx]
// update viewing, arrows and image/video too
ViewImageOrVideo()
}
// grab all selected thumbnails and return a <div> containing the thumbnails
// with extra yr and date attached as attributes so we can set the default
// dir name for a move directory - not used in del, but no harm to include them
@@ -520,10 +535,10 @@ function drawPageOfFigures()
addFigure( obj, last, ecnt )
ecnt++
}
if( document.entries.length == 0 && OPT.search_term != '' )
if( document.entries.length == 0 && OPT.search_term )
$('#figures').append( `<span class="alert alert-danger p-2 col-auto"> No matches for: '${OPT.search_term}'</span>` )
$('.figure').click( function(e) { DoSel(e, this ); SetButtonState(); return false; });
$('.figure').dblclick( function(e) { dblClickToViewEntry( $(this).attr('id') ) } )
$('.figure').dblclick( function(e) { dblClickToViewEntry( $(this).attr('id') ); setDisabledForViewingNextPrevBttons(); addViewerKeyHandler() } )
// for dir, getDirEntries 2nd param is back (or "up" a dir)
$(".dir").click( function(e) { document.back_id=this.id; getDirEntries(this.id,false) } )
$(".back").click( function(e) { getDirEntries(this.id,true) } )
@@ -553,21 +568,6 @@ function getPageFileList(res, viewingIdx)
$('#file_list_div').append(html)
}
// function called when we get another page from inside the files view
function getPageFigures(res, viewingIdx)
{
// add all the figures to files_div
drawPageOfFigures()
}
// function called when we get another page from inside the viewer
function getPageViewer(res, viewingIdx)
{
document.viewing=document.entries[viewingIdx]
// update viewing, arrows and image/video too
ViewImageOrVideo()
}
// Function to get the 'page' of entry ids out of entryList
function getPage(pageNumber, successCallback, viewingIdx=0)
{
@@ -587,9 +587,15 @@ function getPage(pageNumber, successCallback, viewingIdx=0)
type: 'POST', url: '/get_entries_by_ids',
data: JSON.stringify(data), contentType: 'application/json',
dataType: 'json',
success: function(res) { document.entries=res; successCallback(res,viewingIdx); },
error: function(xhr, status, error) { console.error("Error:", error); } });
success: function(res) {
document.entries=res;
successCallback(res,viewingIdx);
resetNextPrevButtons()
// if search, disable folders
if( OPT.search_term )
$('#folders').prop('disabled', 'disabled').removeClass('border-info').addClass('border-secondary').removeClass('text-info').addClass('text-secondary');
},
error: function(xhr, status, error) { console.error("Error:", error); } });
return
}
@@ -620,6 +626,13 @@ function getPageNumberForId(id) {
// if we are on last page, disable next, it not ensure prev is enabled
function resetNextPrevButtons()
{
// no data, so disabled both
if( getPageNumberForId(pageList[0]) == -1 )
{
$('.prev').prop('disabled', true).addClass('disabled');
$('.next').prop('disabled', true).addClass('disabled');
return
}
if ( isFirstPage( getPageNumberForId(pageList[0]) ) )
$('.prev').prop('disabled', true).addClass('disabled');
else
@@ -661,12 +674,14 @@ function prevPage(successCallback)
return
}
// function to see if we are on a phone or tablet (where we dont have ctrl or shift keys - helps to display fake buttons to allow multiselect on mobiles)
function isMobile() {
try{ document.createEvent("TouchEvent"); return true; }
catch(e){ return false; }
}
// when we change one of the options (noo, how_many, folders) - then update '{how_many} files' str,
// tweak noo menu for folders/flat view then reset the page contents based on current OPT values
function changeOPT(successCallback) {
OPT.how_many=$('#how_many').val()
new_f=$('#folders').val()
@@ -707,3 +722,119 @@ function changeOPT(successCallback) {
}
})
}
// function to change the size of thumbnails when user clicks xs/s/m/l/xl buttons
function changeSize()
{
sz=$('input[name="size"]:checked').val();
$('.thumb').prop('height',sz);
}
function getPreviousEntry() {
var currentIndex = entryList.indexOf(document.viewing.id);
oldPageOffset=Math.floor(currentIndex / OPT.how_many)
if (currentIndex > 0) {
currentIndex--;
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex=currentIndex-(pageOffset*OPT.how_many)
// pref page, load it
if( oldPageOffset != pageOffset )
// pref page is pageOffset+1 now
getPage(pageOffset+1,getPageViewer,currentIndex)
else
document.viewing=document.entries[currentIndex]
}
}
function getNextEntry() {
var currentIndex = entryList.indexOf(document.viewing.id);
oldPageOffset=Math.floor(currentIndex / OPT.how_many)
if (currentIndex < entryList.length - 1) {
currentIndex++
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex=currentIndex-(pageOffset*OPT.how_many)
// next page, load it
if( oldPageOffset != pageOffset )
// next page is pageOffset+1 now
getPage(pageOffset+1,getPageViewer,currentIndex)
else
document.viewing=document.entries[currentIndex]
}
}
function entryIsAtStart() {
return document.viewing.id === entryList[0];
}
function entryIsAtEnd() {
return document.viewing.id === entryList[entryList.length - 1];
}
function setEntryById(id) {
var currentIndex = entryList.indexOf(parseInt(id));
// if we are on a different page, adjust as document.entries only has <= how_many
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex = currentIndex-(pageOffset*OPT.how_many)
document.viewing=document.entries[currentIndex]
}
function setDisabledForViewingNextPrevBttons()
{
$('#la').attr('disabled', entryIsAtStart());
$('#ra').attr('disabled', entryIsAtEnd());
}
function addViewerKeyHandler() {
// allow a keypress on the viewer_div
$(document).keydown(function(event) {
// if dbox is visible, dont process this hot-key, we are inputting text
// into inputs instead
if( $("#dbox").is(':visible') )
return
switch (event.key)
{
case "Left": // IE/Edge specific value
case "ArrowLeft":
if( $('#la').prop('disabled') == false )
$('#la').click()
break;
case "Right": // IE/Edge specific value
case "ArrowRight":
if( $('#ra').prop('disabled') == false )
$('#ra').click()
break;
case "d":
$('#distance').click()
break;
case "f":
$('#faces').click()
break;
case "n":
$('#fname_toggle').click()
break;
case "F":
fullscreen=!document.fullscreen
ViewImageOrVideo()
break;
case "l":
JoblogSearch()
break;
case "Delete":
$('#del').click()
default:
return; // Quit when this doesn't handle the key event.
}
});
}
$(document).on('click', function(e) { $('.highlight').removeClass('highlight') ; SetButtonState() });
function dblClickToViewEntry(id) {
$('#files_div').addClass('d-none')
$('#viewer_div').removeClass('d-none')
setEntryById( id )
ViewImageOrVideo()
}

View File

@@ -1,49 +1,24 @@
{% extends "base.html" %}
{% block main_content %}
<style>
.norm-txt { font-size: 1.0rem }
.form-check-input:checked {
background-color: #39C0ED;
border-color: #CFF4FC;
}
.form-switch .form-check-input {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2339C0ED'/%3e%3c/svg%3e");
}
.form-switch .form-check-input:focus {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23CFF4FC'/%3e%3c/svg%3e");
}
#tst90:hover,#tst90:focus { filter: invert(73%) sepia(27%) saturate(3970%) hue-rotate(146deg) brightness(94%) contrast(100%); }
</style>
<script src="{{ url_for( 'internal', filename='js/files_transform.js')}}"></script>
<script src="{{ url_for( 'internal', filename='js/files_support.js')}}"></script>
<script src="{{ url_for( 'internal', filename='js/view_support.js')}}"></script>
<script>
// FIXME: why is this script block at the top? and not at the bottom
document.fake_shift=0
document.fake_ctrl=0
// FIXME: used by viewer code - should probably get rid of this?
var fullscreen=false;
// FIXME: doco / and convert to json
var move_paths=[]
{% for p in move_paths %}
p = new Object()
p.type = '{{p.type}}'
p.path = '{{p.path}}'
p.icon_url = '{{p.icon_url}}'
move_paths.push(p)
{% endfor %}
// GLOBALS
// this is the current entry (object) we are viewing - an image/video (used when we dbl-click to view & then in next/prev in view)
document.viewing=null;
var OPT = {{ OPT.to_dict()|tojson }};
OPT.root_eid = {{ query_data.root_eid }};
// this is the list of entry ids for the images for ALL matches for this query
var entryList={{query_data.entry_list}}
// pageList is just those entries shown on this page from the full entryList
var pageList=[]
// force pageList to set pageList for & render the first page
getPage(1,getPageFigures)
// FIXME: MOVE to file_support.js
function changeSize()
{
sz=$('input[name="size"]:checked').val();
$('.thumb').prop('height',sz);
}
</script>
<div id="files_div">
<div class="container-fluid">
<div class="d-flex row mb-2">
@@ -80,10 +55,6 @@
<div class="col col-auto my-auto">
<span class="alert alert-primary p-2">Searched for: '{{search_term}}'</span>
</div class="col my-auto">
<script>
// FIXME: need to move this into getPage (or similar), so its picked up always not just on first load
$('#folders').prop('disabled', 'disabled').removeClass('border-info').addClass('border-secondary').removeClass('text-info').addClass('text-secondary');
</script>
{% endif %}
<div class="col flex-grow-1 my-auto d-flex justify-content-center w-100">
<button aria-label="prev" id="prev" name="prev" class="prev sm-txt btn btn-outline-secondary disabled" onClick="prevPage(getPageFigures)" disabled>
@@ -144,117 +115,6 @@
</div class="container-fluid">
</div id="files_div">
<div id="viewer_div" class="d-none">
<style>
.norm-txt { font-size: 1.0rem }
.form-check-input:checked {
background-color: #39C0ED;
border-color: #CFF4FC;
}
.form-switch .form-check-input {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2339C0ED'/%3e%3c/svg%3e");
}
.form-switch .form-check-input:focus {
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23CFF4FC'/%3e%3c/svg%3e");
}
#tst90:hover,#tst90:focus { filter: invert(73%) sepia(27%) saturate(3970%) hue-rotate(146deg) brightness(94%) contrast(100%); }
</style>
<script>
// FIXME: doco, but also gather all globals together, many make them all document. to be obviously global (and add fullscreen)
var gap=0.8
var grayscale=0
var throbber=0
var objs=[]
var NMO=[]
var imp_path="static/Import/{{imp_path}}"
var st_path="static/Storage/{{st_path}}"
var bin_path="static/Bin/{{bin_path}}"
// FIXME: MOVE these functions to file_support.js
function PrettyFname(fname)
{
s='<span class="alert alert-secondary py-2">'
if( fname.indexOf( "static/Import" ) == 0 )
{
s+='<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#import"/></svg>'
tmp_path=fname.replace(imp_path,"" )
}
if( fname.indexOf( "static/Storage" ) == 0 )
{
s+='<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#db"/></svg>'
tmp_path=fname.replace("static/Storage","" )
}
if( fname.indexOf( "static/Bin" ) == 0 )
{
s+='<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#trash-fill"/></svg>'
tmp_path=fname.replace("static/Bin","" )
}
s+=tmp_path+'</span>'
return s
}
function getPreviousEntry() {
var currentIndex = entryList.indexOf(document.viewing.id);
oldPageOffset=Math.floor(currentIndex / OPT.how_many)
if (currentIndex > 0) {
currentIndex--;
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex=currentIndex-(pageOffset*OPT.how_many)
// pref page, load it
if( oldPageOffset != pageOffset )
// pref page is pageOffset+1 now
getPage(pageOffset+1,getPageViewer,currentIndex)
else
document.viewing=document.entries[currentIndex]
}
}
function getNextEntry() {
var currentIndex = entryList.indexOf(document.viewing.id);
oldPageOffset=Math.floor(currentIndex / OPT.how_many)
if (currentIndex < entryList.length - 1) {
currentIndex++
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex=currentIndex-(pageOffset*OPT.how_many)
// next page, load it
if( oldPageOffset != pageOffset )
// next page is pageOffset+1 now
getPage(pageOffset+1,getPageViewer,currentIndex)
else
document.viewing=document.entries[currentIndex]
}
}
function entryIsAtStart() {
return document.viewing.id === entryList[0];
}
function entryIsAtEnd() {
return document.viewing.id === entryList[entryList.length - 1];
}
function setEntryById(id) {
var currentIndex = entryList.indexOf(parseInt(id));
// if we are on a different page, adjust as document.entries only has <= how_many
pageOffset=Math.floor(currentIndex / OPT.how_many)
currentIndex = currentIndex-(pageOffset*OPT.how_many)
document.viewing=document.entries[currentIndex]
}
function setDisabledForViewingNextPrevBttons()
{
$('#la').attr('disabled', entryIsAtStart());
$('#ra').attr('disabled', entryIsAtEnd());
}
</script>
<div id="viewer" class="container-fluid">
<div class="row">
<button title="Show previous image" class="col-auto btn btn-outline-info px-2"
@@ -286,11 +146,6 @@
<figcaption id="vid-cap" class="figure-caption text-center text-wrap text-break">
<span id="fname_v"></span></figcaption>
</div>
<script>
window.addEventListener('resize', DrawImg, false);
window.addEventListener('resize', ResizeVideo, false);
</script>
<button title="Show next image" class="col-auto btn btn-outline-info px-2" style="padding: 10%" id="ra"
onClick="
getNextEntry()
@@ -301,7 +156,6 @@
<use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
</button>
</div class="row">
{# use this for color of toggles: https://www.codeply.com/p/4sL9uhevwJ #}
<div class="row">
{# this whole div, just takes up the same space as the left button and is hidden for alignment only #}
<div class="col-auto px-0">
@@ -367,54 +221,57 @@
{% block script_content %}
<script>
// FIXME: MOVE the majorty of below to files_support/view_support. MIGHT help to have urls go into a global too, and then can ignore many of the above items needing url_for
$( document ).keydown(function(event) {
// if dbox is visible, dont process this hot-key, we are inputting text
// into inputs instead
if( $("#dbox").is(':visible') )
return
switch (event.key)
document.fake_shift=0
document.fake_ctrl=0
// FIXME: used by viewer code - should probably get rid of this?
var fullscreen=false;
var move_paths = {{ move_paths|tojson }};
// GLOBALS
// this is the current entry (object) we are viewing - an image/video (used when we dbl-click to view & then in next/prev in view)
document.viewing=null;
var OPT = {{ OPT.to_dict()|tojson }};
OPT.root_eid = {{ query_data.root_eid }};
// this is the list of entry ids for the images for ALL matches for this query
var entryList={{query_data.entry_list}}
// pageList is just those entries shown on this page from the full entryList
var pageList=[]
// force pageList to set pageList for & render the first page
getPage(1,getPageFigures)
// FIXME: doco, but also gather all globals together, many make them all document. to be obviously global (and add fullscreen)
var gap=0.8
var grayscale=0
var throbber=0
var NMO=[]
var imp_path="static/Import/{{imp_path}}"
var st_path="static/Storage/{{st_path}}"
var bin_path="static/Bin/{{bin_path}}"
function PrettyFname(fname)
{
case "Left": // IE/Edge specific value
case "ArrowLeft":
if( $('#la').prop('disabled') == false )
$('#la').click()
break;
case "Right": // IE/Edge specific value
case "ArrowRight":
if( $('#ra').prop('disabled') == false )
$('#ra').click()
break;
case "d":
$('#distance').click()
break;
case "f":
$('#faces').click()
break;
case "n":
$('#fname_toggle').click()
break;
case "F":
fullscreen=!document.fullscreen
ViewImageOrVideo()
break;
case "l":
JoblogSearch()
break;
case "Delete":
$('#del').click()
default:
return; // Quit when this doesn't handle the key event.
s='<span class="alert alert-secondary py-2">'
if( fname.indexOf( "static/Import" ) == 0 )
{
s+='<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#import"/></svg>'
tmp_path=fname.replace(imp_path,"" )
}
});
$(document).on('click', function(e) { $('.highlight').removeClass('highlight') ; SetButtonState() });
function dblClickToViewEntry(id) {
$('#files_div').addClass('d-none')
$('#viewer_div').removeClass('d-none')
setEntryById( id )
ViewImageOrVideo()
if( fname.indexOf( "static/Storage" ) == 0 )
{
s+='<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#db"/></svg>'
tmp_path=fname.replace("static/Storage","" )
}
if( fname.indexOf( "static/Bin" ) == 0 )
{
s+='<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#trash-fill"/></svg>'
tmp_path=fname.replace("static/Bin","" )
}
s+=tmp_path+'</span>'
return s
}
// different context menu on files
@@ -498,9 +355,7 @@ $.contextMenu({
}
});
$( document ).keydown(function(event) {
switch (event.key)
$( document ).keydown(function(event) { switch (event.key)
{
case "Delete":
{% if "files_rbp" in request.url %}
@@ -519,5 +374,8 @@ if( isMobile() )
// check the size radiobutton
$(`input[name="size"][value="${OPT.size}"]`).prop('checked', true)
window.addEventListener('resize', DrawImg, false);
window.addEventListener('resize', ResizeVideo, false);
</script>
{% endblock script_content %}