307 lines
20 KiB
HTML
307 lines
20 KiB
HTML
{% 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>
|
|
|
|
<div id="files_div">
|
|
<div class="container-fluid">
|
|
<div class="d-flex row mb-2">
|
|
{% if OPT.folders %}
|
|
<div class="my-auto col col-auto">
|
|
<span class="alert alert-primary py-2">
|
|
{% if "files_ip" in request.url %}
|
|
<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#import"/></svg>
|
|
{% set tmp_path=OPT.cwd | replace( "static/Import", "" ) + "/" %}
|
|
{% elif "files_sp" in request.url %}
|
|
<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#db"/></svg>
|
|
{% set tmp_path=OPT.cwd | replace( "static/Storage", "" ) + "/" %}
|
|
{% elif "files_rbp" in request.url %}
|
|
<svg width="20" height="20" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#trash"/></svg>
|
|
{% set tmp_path=OPT.cwd | replace( "static/Bin", "" ) + "/" %}
|
|
{% endif %}
|
|
{{tmp_path}}</span>
|
|
</div class="col my-auto">
|
|
{% endif %}
|
|
<div class="col col-auto">
|
|
<div class="input-group">
|
|
{{CreateSelect( "noo", OPT.noo, ["Oldest", "Newest","A to Z", "Z to A"], "changeOPT(getPageFigures); return false", "rounded-start py-2")|safe }}
|
|
{{CreateSelect( "how_many", OPT.how_many|string, ["10", "25", "50", "75", "100", "150", "200", "500"], "changeOPT(getPageFigures); return false" )|safe }}
|
|
{% if OPT.folders %}
|
|
{{CreateFoldersSelect( OPT.folders, "changeOPT(getPageFigures); return false", "rounded-end" )|safe }}
|
|
{% else %}
|
|
{{CreateFoldersSelect( OPT.folders, "changeOPT(getPageFigures); return false" )|safe }}
|
|
<span class="sm-txt my-auto btn btn-outline-info disabled border-top border-bottom">grouped by:</span>
|
|
{{CreateSelect( "grouping", OPT.grouping, ["None", "Day", "Week", "Month"], "OPT.grouping=$('#grouping').val();drawPageOfFigures();return false", "rounded-end")|safe }}
|
|
{% endif %}
|
|
</div class="input-group">
|
|
</div class="col">
|
|
{% if search_term is defined %}
|
|
<div class="col col-auto my-auto">
|
|
<span class="alert alert-primary p-2">Searched for: '{{search_term}}'</span>
|
|
</div class="col my-auto">
|
|
{% 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>
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
|
|
</button>
|
|
<span class="how_many_text sm-txt my-auto"> {{OPT.how_many}} files </span>
|
|
<button aria-label="next" id="next" name="next" class="next sm-txt btn btn-outline-secondary" onClick="nextPage(getPageFigures)">
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
|
</button>
|
|
<button aria-label="move" id="move" disabled name="move" class="sm-txt btn btn-outline-primary ms-4" onClick="MoveDBox(move_paths); return false;">
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#folder_plus"/></svg>
|
|
</button>
|
|
{% if "files_rbp" in request.url %}
|
|
<button aria-label="delete" id="del" disabled name="del" class="sm-txt btn btn-outline-success mx-1" onClick="DelDBox('Restore'); return false;">
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#trash-fill"/></svg>
|
|
{% else %}
|
|
<button aria-label="delete" id="del" disabled name="del" class="sm-txt btn btn-outline-danger mx-1" onClick="DelDBox('Delete'); return false;">
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#trash-fill"/></svg>
|
|
{% endif %}
|
|
</button>
|
|
<button style="visibility:hidden" class="btn btn-outline-secondary" aria-label="shift-key" id="shift-key" onclick="document.fake_shift=1-document.fake_shift; event.stopPropagation(); return false">shift</button>
|
|
<button style="visibility:hidden" class="btn btn-outline-secondary" aria-label="ctrl-key" id="ctrl-key" onclick="document.fake_ctrl=1-document.fake_ctrl; event.stopPropagation(); return false">ctrl</button>
|
|
</div class="col flex-grow-1">
|
|
<div class="d-flex col col-auto justify-content-end">
|
|
<div class="btn-group" role="group" aria-label="Size radio button group">
|
|
<input type="radio" class="btn-check" name="size" id="size-xs" onCLick="changeSize()" autocomplete="off" value="64">
|
|
<label class="btn btn-outline-info btn-radio" for="size-xs">XS</label>
|
|
|
|
<input type="radio" class="btn-check" name="size" id="size-s" onCLick="changeSize()" autocomplete="off" value="96">
|
|
<label class="btn btn-outline-info btn-radio" for="size-s">S</label>
|
|
|
|
<input type="radio" class="btn-check" name="size" id="size-m" onCLick="changeSize()" autocomplete="off" value="128">
|
|
<label class="btn btn-outline-info btn-radio" for="size-m">M</label>
|
|
|
|
<input type="radio" class="btn-check" name="size" id="size-l" onCLick="changeSize()" autocomplete="off" value="192">
|
|
<label class="btn btn-outline-info btn-radio" for="size-l">L</label>
|
|
|
|
<input type="radio" class="btn-check" name="size" id="size-xl" onCLick="changeSize()" autocomplete="off" value="256">
|
|
<label class="btn btn-outline-info btn-radio" for="size-xl">XL</label>
|
|
</div>
|
|
</div class="d-flex col">
|
|
</div class="d-flex row mb-2">
|
|
</div container="fluid">
|
|
<div id="figures" class="row ms-2">
|
|
</div>
|
|
<div class="container-fluid">
|
|
<div class="row">
|
|
<div class="col my-auto d-flex justify-content-center">
|
|
<button aria-label="prev" id="prev" name="prev" class="prev sm-txt btn btn-outline-secondary disabled" onClick="prevPage(getPageFigures)" disabled>
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
|
|
</button>
|
|
<span class="how_many_text sm-txt my-auto"> {{OPT.how_many}} files </span>
|
|
<button aria-label="next" id="next" name="next" class="next sm-txt btn btn-outline-secondary" onClick="nextPage(getPageFigures)">
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
|
</button>
|
|
</div class="col my-auto">
|
|
</div class="row">
|
|
</div class="container-fluid">
|
|
</div id="files_div">
|
|
<div id="viewer_div" class="d-none">
|
|
<div id="viewer" class="container-fluid">
|
|
<div class="row">
|
|
<button title="Show previous image" class="col-auto btn btn-outline-info px-2"
|
|
style="padding: 10%" id="la"
|
|
onClick="
|
|
getPreviousEntry()
|
|
setDisabledForViewingNextPrevBttons()
|
|
ViewImageOrVideo()
|
|
">
|
|
<svg width="16" height="16" fill="currentColor">
|
|
<use xlink:href="{{url_for('internal', filename='icons.svg')}}#prev"/></svg>
|
|
</button>
|
|
<figure class="col col-auto border border-info rounded m-0 p-1" id="figure">
|
|
<canvas id="canvas"></canvas>
|
|
<img id="throbber" src="{{url_for('internal', filename='throbber.gif')}}" style="display:none;">
|
|
<script>
|
|
var im=new Image();
|
|
im.onload=DrawImg
|
|
var context = canvas.getContext('2d')
|
|
</script>
|
|
<figcaption id="img-cap" class="figure-caption text-center text-wrap text-break">
|
|
<span id="fname_i"></span></figcaption>
|
|
</figure>
|
|
<div id="video_div" class="col col-auto">
|
|
<video id="video" class="col col-auto" controls>
|
|
<source id="videoSource" src="" type="video/mp4">
|
|
Your browser does not support the video tag.
|
|
</video>
|
|
<figcaption id="vid-cap" class="figure-caption text-center text-wrap text-break">
|
|
<span id="fname_v"></span></figcaption>
|
|
</div>
|
|
<button title="Show next image" class="col-auto btn btn-outline-info px-2" style="padding: 10%" id="ra"
|
|
onClick="
|
|
getNextEntry()
|
|
setDisabledForViewingNextPrevBttons()
|
|
ViewImageOrVideo()
|
|
">
|
|
<svg width="16" height="16" fill="currentColor">
|
|
<use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
|
</button>
|
|
</div class="row">
|
|
<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">
|
|
<button class="btn btn-outline-info px-2 invisible" disabled>
|
|
<svg width="16" height="16" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#next"/></svg>
|
|
</button>
|
|
</div>
|
|
<span class="col-auto my-auto">Show:</span>
|
|
<div title="Toggle showing filename (hotkey: n)" class="d-flex form-check form-switch border border-info rounded col col-auto my-auto py-1 justify-content-center ps-5">
|
|
<input class="form-check-input" type="checkbox" id="fname_toggle" onChange="$('.figure-caption').toggle()" checked>
|
|
<label class="form-check-label ps-1" for="fname_toggle">Filename</label>
|
|
</div>
|
|
<div title="Toggle showing matched faces (hotkey: f)" class="d-flex form-check form-switch border border-info rounded col col-auto my-auto py-1 justify-content-center ps-5">
|
|
<input class="form-check-input" type="checkbox" onChange="FaceToggle()" id="faces">
|
|
<label class="form-check-label ps-1" for="faces">Faces</label>
|
|
</div>
|
|
<div title="Toggle showing 'distance' on matched faces (hotkey: d)" class="d-flex form-check form-switch border border-info rounded col col-auto my-auto py-1 justify-content-center ps-5">
|
|
<input class="form-check-input" type="checkbox" onChange="DrawImg()" id="distance">
|
|
<label class="form-check-label ps-1" for="distance">Distance</label>
|
|
</div>
|
|
<div title="Change the model used to detect faces" class="col col-auto my-auto">
|
|
AI Model:
|
|
{# can use 0 as default, it will be (re)set correctly in DrawImg() anyway #}
|
|
{{CreateSelect( "model", 0, ["N/A", "normal", "slow/accurate"], "", "rounded norm-txt", [0,1,2])|safe }}
|
|
</div>
|
|
<div class="col col-auto pt-1">
|
|
<button class="btn btn-outline-info p-1" title="Rotate by 90 degrees" onClick="Transform(90)">
|
|
<img src="{{url_for('internal', filename='rot90.png')}}" width="32" height="32" onMouseOver="this.src='{{url_for('internal', filename='rot90-invert.png')}}'"
|
|
onMouseOut="this.src='{{url_for('internal', filename='rot90.png')}}'" />
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="Rotate by 180 degrees" onClick="Transform(180)">
|
|
<img src="{{url_for('internal', filename='rot180.png')}}" width="32" height="32" onMouseOver="this.src='{{url_for('internal', filename='rot180-invert.png')}}'"
|
|
onMouseOut="this.src='{{url_for('internal', filename='rot180.png')}}'" />
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="Rotate by 270 degrees" onClick="Transform(270)">
|
|
<img src="{{url_for('internal', filename='rot270.png')}}" width="32" height="32" onMouseOver="this.src='{{url_for('internal', filename='rot270-invert.png')}}'"
|
|
onMouseOut="this.src='{{url_for('internal', filename='rot270.png')}}'" />
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="Flip horizontally" onClick="Transform('fliph')">
|
|
<svg width="32" height="32" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#flip_h"/></svg>
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="Flip vertically" onClick="Transform('flipv')">
|
|
<svg width="32" height="32" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#flip_v"/></svg>
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="View in Fullscreen mode (hotkey: F)" onClick="fullscreen=true; ViewImageOrVideo()">
|
|
<svg width="32" height="32" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#fullscreen"/></svg>
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="Show logs relating to this filename (hotkey: l)" onClick="JoblogSearch()">
|
|
<svg width="32" height="32" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#log"/></svg>
|
|
</button>
|
|
<button class="btn btn-outline-info p-1" title="View Original" onClick="window.location='/'+document.viewing.FullPathOnFS">
|
|
<svg width="32" height="32" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#download"/></svg>
|
|
</button>
|
|
<button id="viewer_del" class="btn btn-outline-danger p-1" title="Delete (hotkey: Del)" onClick="DelDBox('Delete')">
|
|
<svg id="viewer_bin" width="32" height="32" fill="currentColor"><use xlink:href="{{url_for('internal', filename='icons.svg')}}#trash"/></svg>
|
|
</button>
|
|
</div>
|
|
</div class="row">
|
|
</div id="viewer">
|
|
</div id="viewer_div">
|
|
{#
|
|
$.ajax({ type: 'POST', data: '&eid-0='+document.viewing.id, url: '/delete_files', success: function(data){ window.location='/'; return false; } })">
|
|
#}
|
|
{% endblock main_content %}
|
|
|
|
{% block script_content %}
|
|
<script>
|
|
// GLOBALS
|
|
document.fake_shift=0
|
|
document.fake_ctrl=0
|
|
|
|
// FIXME: used by viewer code - should probably get rid of this?
|
|
var fullscreen=false;
|
|
|
|
// 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 }};
|
|
|
|
// get items out of query_data into convenience javascript vars...
|
|
var move_paths = {{ query_data.move_paths|tojson }};
|
|
var NMO={{query_data.NMO|tojson}}
|
|
var people={{query_data.people|tojson}}
|
|
|
|
// 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
|
|
|
|
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("statuc/Import","" )
|
|
}
|
|
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
|
|
}
|
|
|
|
// check the size radiobutton
|
|
$(`input[name="size"][value="${OPT.size}"]`).prop('checked', true)
|
|
|
|
window.addEventListener('resize', DrawImg, false);
|
|
window.addEventListener('resize', ResizeVideo, false);
|
|
|
|
// when we are in recycle bin, change colours to green & func to restore
|
|
if( window.location.href.includes('files_rbp') )
|
|
{
|
|
$('#viewer_bin').attr('fill', 'var(--bs-success)')
|
|
// fill with bg-success colour
|
|
$('#viewer_bin use').attr('fill', 'var(--bs-success)')
|
|
$('#viewer_del').removeClass('btn-outline-danger').addClass('btn-outline-success')
|
|
$('#viewer_del').on('mouseenter', function() {
|
|
// Set the SVG fill to white
|
|
$('#viewer_bin use').attr('fill', 'white');
|
|
});
|
|
|
|
// When mouse leaves the button
|
|
$('#viewer_del').on('mouseleave', function() {
|
|
// Revert the SVG fill to the bg-success colour
|
|
$('#viewer_bin use').attr('fill', 'var(--bs-success)');
|
|
});
|
|
$('#viewer_del').on('click', function() { DelDBox('Restore') } )
|
|
}
|
|
|
|
</script>
|
|
{% endblock script_content %}
|