| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870 |
- /*
- Photo.js
- Author: tobychui
- This is a complete rewrite of the legacy Photo module for ArozOS
- */
- // Number of photos to load per page (infinite scroll batch size)
- const PAGE_SIZE = 40; //large enough to fill the whole page on load, but small enough to keep initial load fast and responsive
- let photoList = [];
- let prePhoto = "";
- let nextPhoto = "";
- let currentModel = "";
- let currentPhotoAllIndex = -1; // index of current photo in allImages (full server list)
- let isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
- // Check if image should use compression (only JPG/PNG)
- function shouldUseCompression(filepath, filesize) {
- const ext = filepath.split('.').pop().toLowerCase();
- const isJpgOrPng = (ext === 'jpg' || ext === 'jpeg' || ext === 'png');
- const COMPRESSION_THRESHOLD = 5 * 1024 * 1024; // 5MB
- return isJpgOrPng && filesize && filesize > COMPRESSION_THRESHOLD;
- }
- // Get viewable image URL (handles RAW files)
- function getViewableImageUrl(filepath, callback) {
- // Both RAW and regular images now use backend rendering
- const imageUrl = "../media?file=" + encodeURIComponent(filepath);
- callback(imageUrl, true, false, isRawImage(filepath) ? 'backend_raw' : 'direct');
- }
- function getImageWidth(){
- // Use the actual viewbox container width so the sidebar and scrollbar are
- // already subtracted — this prevents gaps when the window is resized.
- const container = document.getElementById('viewboxContainer');
- const containerWidth = container ? container.clientWidth : (window.innerWidth - 210);
- let boxCount;
- if (containerWidth < 400) {
- boxCount = 2;
- } else if (containerWidth < 600) {
- boxCount = 3;
- } else if (containerWidth < 900) {
- boxCount = 4;
- } else if (containerWidth < 1100) {
- boxCount = 5;
- } else if (containerWidth < 1400) {
- boxCount = 6;
- } else {
- boxCount = 8;
- }
- return Math.floor(containerWidth / boxCount);
- }
- function updateImageSizes(){
- let newImageWidth = getImageWidth();
- console.log(newImageWidth, $("#viewbox").width());
- //Updates all the size of the images
- $(".imagecard").css({
- width: newImageWidth,
- height: newImageWidth
- });
- }
- function extractFolderName(folderpath){
- return folderpath.split("/").pop();
- }
- function parseExifValue(value) {
- if (typeof value === 'string' && value.includes('/')) {
- let parts = value.split('/');
- if (parts.length === 2) {
- let num = parseFloat(parts[0]);
- let den = parseFloat(parts[1]);
- if (den !== 0) {
- return num / den;
- }
- }
- }
- return parseFloat(value) || value;
- }
- function formatShutterSpeed(value) {
- let num = parseExifValue(value);
- if (num < 1) {
- return "1/" + Math.round(1 / num);
- } else {
- return num ;
- }
- }
- function photoListObject() {
- return {
- // data
- pathWildcard: "user:/Photo/*",
- currentPath: "user:/Photo",
- renderSize: 200,
- vroots: [],
- allImages: [], // full list from server
- images: [], // currently displayed slice
- folders: [],
- sortOrder: 'smart',
- restored: false,
- hasMoreImages: false,
- isLoadingMore: false, // guard: blocks new batch until DOM has updated
- sidebarOpen: !isMobile, // start hidden on mobile, visible on desktop
- // init
- init() {
- this.getFolderInfo();
- this.getRootInfo();
- this.renderSize = getImageWidth();
- updateImageSizes();
- this.restored = false;
- this.$nextTick(() => { this.setupInfiniteScroll(); });
- const MOBILE_BP = 768;
- let _prevMobile = window.innerWidth <= MOBILE_BP;
- let _resizeTimer;
- window.addEventListener('resize', () => {
- clearTimeout(_resizeTimer);
- _resizeTimer = setTimeout(() => {
- // Recalculate tile sizes
- this.renderSize = getImageWidth();
- updateImageSizes();
- // Auto-manage sidebar visibility on breakpoint crossing
- const nowMobile = window.innerWidth <= MOBILE_BP;
- if (nowMobile && !_prevMobile) {
- // Desktop → mobile: hide sidebar so it doesn't overlay content
- this.sidebarOpen = false;
- } else if (!nowMobile && _prevMobile) {
- // Mobile → desktop: sidebar is back in normal flow, keep state clean
- this.sidebarOpen = false;
- }
- _prevMobile = nowMobile;
- }, 80);
- });
- },
-
- updateRenderingPath(newPath, callback = null){
- this.currentPath = JSON.parse(JSON.stringify(newPath));
- this.pathWildcard = newPath + '/*';
- this.restored = false;
- if (isMobile) this.sidebarOpen = false;
- this.getFolderInfo(callback);
- },
- // Returns path segments for the sidebar breadcrumb tree
- getPathSegments() {
- const parts = this.currentPath.split('/');
- let segments = [];
- let accumulated = '';
- for (let i = 0; i < parts.length; i++) {
- accumulated = i === 0 ? parts[0] : accumulated + '/' + parts[i];
- segments.push({ name: parts[i], path: accumulated, depth: i, isDiskRoot: i === 0 });
- }
- return segments;
- },
- getFolderInfo(callback = null) {
- fetch(ao_root + "system/ajgi/interface?script=Photo/backend/listFolder.js", {
- method: 'POST',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- "folder": this.pathWildcard,
- "sort": this.sortOrder
- })
- }).then(resp => {
- resp.json().then(data => {
- console.log(data);
- this.folders = data[0];
- this.allImages = data[1];
- this.images = this.allImages.slice(0, PAGE_SIZE);
- this.hasMoreImages = this.allImages.length > PAGE_SIZE;
- this.isLoadingMore = false;
- if (this.allImages.length == 0){
- $("#noimg").show();
- }else{
- $("#noimg").hide();
- }
- console.log(this.folders);
- if (!this.restored) { restoreFromHash(); this.restored = true; }
-
- if (callback) callback();
- });
- });
- },
- getRootInfo() {
- fetch(ao_root + "system/ajgi/interface?script=Photo/backend/listRoots.js", {
- method: 'POST',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({})
- }).then(resp => {
- resp.json().then(data => {
- this.vroots = data;
- this.$nextTick(() => {
- $('.ui.dropdown').dropdown();
- });
- });
- })
- },
- changeSort(newSort) {
- this.sortOrder = newSort;
- this.getFolderInfo();
- },
- // Load the next PAGE_SIZE images into the displayed list
- loadMoreImages() {
- if (this.isLoadingMore) return;
- const current = this.images.length;
- if (current >= this.allImages.length) return;
- this.isLoadingMore = true;
- const next = this.allImages.slice(current, current + PAGE_SIZE);
- this.images = this.images.concat(next);
- this.hasMoreImages = this.images.length < this.allImages.length;
- // Release the guard only after Alpine has re-rendered and scrollHeight has grown
- this.$nextTick(() => { this.isLoadingMore = false; });
- },
- // Attach a scroll listener to the viewbox container for infinite scroll
- setupInfiniteScroll() {
- const container = document.getElementById('viewboxContainer');
- if (!container) return;
- container.addEventListener('scroll', () => {
- const { scrollTop, scrollHeight, clientHeight } = container;
- // Trigger when within 300px of the bottom
- if (scrollTop + clientHeight >= scrollHeight - 300) {
- this.loadMoreImages();
- }
- });
- }
- }
- }
- function renderImageList(object){
- var fd = $(object).attr("filedata");
- fd = JSON.parse(decodeURIComponent(fd));
- console.log(fd);
-
- }
- function ShowModal(){
- $('#photo-viewer').show();
- }
- function closeViewer(){
- $('#photo-viewer').hide();
- if (!ao_module_virtualDesktop){
- // Only update hash if not under WebDesktop mode
- // to prevent iframe refresh
- window.location.hash = '';
- }
- ao_module_setWindowTitle("Photo");
- setTimeout(function(){
- $("#fullImage").attr("src","img/loading.png");
- $("#compressedImage").attr("src","").hide().removeClass('hidden');
- $("#bg-image").attr("src","");
- $("#info-filename").text("");
- $("#info-filepath").text("");
- $("#info-dimensions").text("Loading...");
- // Reset EXIF data display
- $('#basic-info-section').hide();
- $('#shooting-params-section').hide();
- $('#tone-analysis-section').hide();
- $('#device-info-section').hide();
- $('#shooting-mode-section').hide();
- $('#technical-params-section').hide();
- $('#no-exif-message').hide();
- $('.ui.divider').hide();
- // Clear histogram canvas
- const canvas = document.getElementById('histogram-canvas');
- if (canvas) {
- const ctx = canvas.getContext('2d');
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- }
- }, 300);
- }
- let compressedImageLoaded = false;
- let fullsizeImageLoaded = false;
- function showImage(object){
- // Reset zoom level when switching photos
- if (typeof resetZoom === 'function') {
- resetZoom();
- }
- if (!$(object).hasClass("imagecard")){
- // Not an image card, do nothing
- return;
- }
-
- // Reset loading flags
- compressedImageLoaded = false;
- fullsizeImageLoaded = false;
-
- var fd = JSON.parse(decodeURIComponent($(object).attr("filedata")));
- $("#info-dimensions").text("Calculating...");
- // Check if we should use compression (only for JPG/PNG > 5MB)
- const useCompression = shouldUseCompression(fd.filepath, fd.filesize);
- // Set thumbnail as placeholder for full image
- const thumbnailUrl = $(object).find('img').attr('src');
- $("#fullImage").attr("src", thumbnailUrl);
- $("#fullImage").hide();
- $("#compressedImage").show();
- $("#compressedImage").attr("src", thumbnailUrl);
- $("#bg-image").attr("src", thumbnailUrl);
-
- // Get image URL (backend handles RAW files automatically)
- getViewableImageUrl(fd.filepath, (imageUrl, isSupported, isBlob, method) => {
- $("#loading-progress").show();
- const compressedImg = document.getElementById('compressedImage');
- const fullImg = document.getElementById('fullImage');
- const bgImg = document.getElementById('bg-image');
- $("#loading-progress").html(`<i class="loading spinner icon"></i> Loading`);
- if (useCompression) {
- // Use compressed version for large JPG/PNG files
- console.log('Large JPG/PNG detected (' + (fd.filesize / 1024 / 1024).toFixed(2) + 'MB), loading compressed version first');
- fetch(ao_root + "system/ajgi/interface?script=Photo/backend/getCompressedImg.js", {
- method: 'POST',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- "filepath": fd.filepath
- })
- }).then(resp => {
- resp.text().then(dataURL => {
- $("#loading-progress").html(`<i class="loading spinner icon"></i> Optimizing Resolution`);
- compressedImageLoaded = true;
- // Only show compressed image if full-size hasn't loaded yet
- if (!fullsizeImageLoaded) {
- compressedImg.src = dataURL;
- compressedImg.style.display = 'block';
- bgImg.src = dataURL;
- } else {
- console.log('Full-size image already loaded, skipping compressed image display');
- }
- });
- }).catch(error => {
- console.error('Failed to load compressed image:', error);
- // Fall back to full size image
- fullImg.src = imageUrl;
- bgImg.src = imageUrl;
- });
- // Start loading full-size image in background
- loadFullSizeImageInBackground(imageUrl, fd);
- } else {
- $("#compressedImage").hide();
- $("#fullImage").show();
- $("#loading-progress").hide();
- // Use full image URL directly for RAW, WEBP, or small JPG/PNG files
- if (method === 'backend_raw') {
- console.log('RAW file: Rendered by backend');
- }
- fullImg.src = imageUrl;
- bgImg.src = imageUrl;
- }
- // Update image dimensions and generate histogram when full image loads
- $("#fullImage").off("load").on('load', function() {
- fullsizeImageLoaded = true;
- let width = this.naturalWidth;
- let height = this.naturalHeight;
- $("#info-dimensions").text(width + ' × ' + height + "px");
- // Hide the compressed image once full image is loaded
- $("#compressedImage").hide();
- $("#fullImage").show();
- $("#loading-progress").hide();
- const canvas = document.getElementById('histogram-canvas');
- if (canvas) {
- generateHistogram(this, canvas);
- }
- });
- $("#info-filename").text(fd.filename);
- $("#info-filepath").text(fd.filepath);
- var nextCard = $(object).next();
- var prevCard = $(object).prev();
- if (nextCard.length > 0){
- nextPhoto = nextCard[0];
- }else{
- nextPhoto = null;
- }
- if (prevCard.length > 0){
- prePhoto = prevCard[0];
- }else{
- prePhoto = null;
- }
- // Track position in the full allImages list for index-based navigation
- const _appEl = document.querySelector('[x-data*="photoListObject"]');
- if (_appEl && _appEl._x_dataStack) {
- const _app = _appEl._x_dataStack[0];
- currentPhotoAllIndex = _app.allImages.findIndex(img => img.filepath === fd.filepath);
- // Proactively load next batch when within PAGE_SIZE of the end of rendered images
- if (currentPhotoAllIndex >= 0 && _app.hasMoreImages &&
- currentPhotoAllIndex >= _app.images.length - PAGE_SIZE) {
- _app.loadMoreImages();
- }
- }
- // Update navigation buttons state
- if (typeof updateNavigationButtons === 'function') {
- updateNavigationButtons();
- }
- ao_module_setWindowTitle("Photo - " + fd.filename);
- if (!ao_module_virtualDesktop){
- window.location.hash = encodeURIComponent(JSON.stringify({filename: fd.filename, filepath: fd.filepath}));
- }
-
- // Check for EXIF data
- fetch(ao_root + "system/ajgi/interface?script=Photo/backend/getExif.js", {
- method: 'POST',
- cache: 'no-cache',
- headers: {
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({
- "filepath": fd.filepath
- })
- }).then(resp => {
- resp.json().then(data => {
- formatExifData(data, fd);
- })
- }).catch(error => {
- console.error('Failed to fetch EXIF data:', error);
- formatExifData({}, fd); // Call with empty EXIF to show tone analysis
- });
- });
- }
- // Function to load full-size image in background with progress tracking
- function loadFullSizeImageInBackground(fullSizeUrl, fileData) {
- console.log('Starting background download of full-size image...');
- const fullImage = document.getElementById('fullImage');
- fullImage.src = fullSizeUrl;
- }
- $(document).on("keydown", function(e){
- if (e.keyCode == 27){ // Escape
- if ($('#photo-viewer').is(':visible')) {
- closeViewer();
- }
- } else if (e.keyCode == 37){
- //Left
- if (typeof showPreviousImage === 'function') {
- showPreviousImage();
- } else if (prePhoto != null){
- showImage(prePhoto);
- }
-
- }else if (e.keyCode == 39){
- //Right
- if (typeof showNextImage === 'function') {
- showNextImage();
- } else if (nextPhoto != null){
- showImage(nextPhoto);
- }
-
- }
- })
- function generateToneAnalysis(imageElement) {
- analysis_tone_types(imageElement, function(result) {
- if (result) {
- // Update tone type based on brightness, contrast, shadow and highlight ratios
- let toneType = get_tone_type(result.brightness, result.contrast, result.shadowRatio, result.highlightRatio);
- $('.tone-type-value').text(toneType);
- $('.brightness-value').text(result.brightness);
- $('.contrast-value').text(result.contrast);
- $('.shadow-ratio-value').text(result.shadowRatio);
- $('.highlight-ratio-value').text(result.highlightRatio);
- } else {
- $('.tone-type-value').text("N/A");
- $('.brightness-value').text("N/A");
- $('.contrast-value').text("N/A");
- $('.shadow-ratio-value').text("N/A");
- $('.highlight-ratio-value').text("N/A");
- }
- });
- }
- function formatExifData(exif, fileData) {
- // Hide all sections initially
- $('#basic-info-section').hide();
- $('#shooting-params-section').hide();
- $('#tone-analysis-section').hide();
- $('#device-info-section').hide();
- $('#shooting-mode-section').hide();
- $('#technical-params-section').hide();
- $('#no-exif-message').hide();
- // Hide all dividers
- $('.ui.divider').hide();
- if (!exif || Object.keys(exif).length === 0) {
- $('#no-exif-message').show();
- //Generate histogram and tone analysis only
- generateHistogram(document.getElementById('fullImage'), document.getElementById('histogram-canvas'));
- generateToneAnalysis(document.getElementById('fullImage'));
- $('#tone-analysis-section').show();
- return;
- }
- let sectionsShown = 0;
- // Section 1: Basic Information
- let basicInfoShown = false;
- if (fileData.filename) {
- let ext = fileData.filename.split('.').pop().toUpperCase();
- $('#format-value').text(ext);
- $('#format-row').show();
- basicInfoShown = true;
- } else {
- $('#format-row').hide();
- }
- if (exif.PixelXDimension && exif.PixelYDimension) {
- $('#dimensions-value').text(`${exif.PixelXDimension} × ${exif.PixelYDimension}`);
- $('#dimensions-row').show();
- let pixels = (exif.PixelXDimension * exif.PixelYDimension / 1000000).toFixed(1);
- $('#pixels-value').text(`${pixels} MP`);
- $('#pixels-row').show();
- basicInfoShown = true;
- } else {
- $('#dimensions-row').hide();
- $('#pixels-row').hide();
- }
- if (exif.ColorSpace !== undefined) {
- exif.ColorSpace = JSON.parse(exif.ColorSpace);
- let colorSpace = exif.ColorSpace === 1 ? "sRGB" : exif.ColorSpace === 65535 ? "Uncalibrated" : "Unknown";
- $('#color-space-value').text(colorSpace);
- $('#color-space-row').show();
- basicInfoShown = true;
- } else {
- $('#color-space-row').hide();
- }
- if (exif.DateTimeOriginal) {
- exif.DateTimeOriginal = JSON.parse(exif.DateTimeOriginal);
- $('#shooting-time-value').text(exif.DateTimeOriginal.replace(/:/g, '/').replace(' ', ' '));
- $('#shooting-time-row').show();
- basicInfoShown = true;
- } else {
- $('#shooting-time-row').hide();
- }
- if (exif.Software) {
- exif.Software = JSON.parse(exif.Software);
- $('#software-value').text(exif.Software);
- $('#software-row').show();
- basicInfoShown = true;
- } else {
- $('#software-row').hide();
- }
- if (basicInfoShown) {
- $('#basic-info-section').show();
- sectionsShown++;
- if (sectionsShown > 1) $('#basic-info-divider').show();
- }
- // Section 2: Shooting Parameters
- let shootingParamsShown = false;
- if (exif.FocalLength) {
- $('#focal-length-value').text(JSON.parse(exif.FocalLength));
- $('#focal-length-row').show();
- shootingParamsShown = true;
- } else {
- $('#focal-length-row').hide();
- }
- if (exif.FNumber) {
- exif.FNumber = JSON.parse(exif.FNumber);
- let aperture = parseExifValue(exif.FNumber);
- let formattedAperture = aperture % 1 === 0 ? aperture.toString() : aperture.toFixed(1);
- $('#aperture-value').text('f/' + formattedAperture);
- $('#aperture-row').show();
- shootingParamsShown = true;
- } else {
- $('#aperture-row').hide();
- }
- if (exif.ExposureTime) {
- let exposureTime = JSON.parse(exif.ExposureTime);
- let formattedExposure = formatShutterSpeed(exposureTime);
- $('#shutter-speed-value').text(formattedExposure + 's');
- $('#shutter-speed-row').show();
- shootingParamsShown = true;
- } else {
- $('#shutter-speed-row').hide();
- }
- if (exif.ISOSpeedRatings) {
- $('#iso-value').text(exif.ISOSpeedRatings);
- $('#iso-row').show();
- shootingParamsShown = true;
- } else {
- $('#iso-row').hide();
- }
- if (exif.ExposureBiasValue) {
- $('#ev-value').text(JSON.parse(exif.ExposureBiasValue));
- $('#ev-row').show();
- shootingParamsShown = true;
- } else {
- $('#ev-row').hide();
- }
- if (shootingParamsShown) {
- $('#shooting-params-section').show();
- sectionsShown++;
- if (sectionsShown > 1) $('#shooting-params-divider').show();
- }
- // Section 3: Tone Analysis
- $('#tone-analysis-section').show();
- sectionsShown++;
- if (sectionsShown > 1) $('#tone-analysis-divider').show();
- generateToneAnalysis(document.getElementById('fullImage'));
- // Section 4: Device Information
- let deviceInfoShown = false;
- if (exif.Make && exif.Model) {
- exif.Make = JSON.parse(exif.Make);
- exif.Model = JSON.parse(exif.Model);
- $('#camera-value').text(`${exif.Make} ${exif.Model}`);
- $('#camera-row').show();
- deviceInfoShown = true;
- } else if (exif.Model) {
- exif.Model = JSON.parse(exif.Model);
- $('#camera-value').text(exif.Model);
- $('#camera-row').show();
- deviceInfoShown = true;
- } else {
- $('#camera-row').hide();
- }
- if (exif.LensModel) {
- exif.LensModel = JSON.parse(exif.LensModel);
- $('#lens-value').text(exif.LensModel);
- $('#lens-row').show();
- deviceInfoShown = true;
- } else {
- $('#lens-row').hide();
- }
- if (exif.FocalLength) {
- exif.FocalLength = JSON.parse(exif.FocalLength);
- $('#focal-length-device-value').text(`${exif.FocalLength}mm`);
- $('#focal-length-device-row').show();
- deviceInfoShown = true;
- } else {
- $('#focal-length-device-row').hide();
- }
- if (exif.MaxApertureValue) {
- exif.MaxApertureValue = JSON.parse(exif.MaxApertureValue);
- $('#max-aperture-value').text(exif.MaxApertureValue);
- $('#max-aperture-row').show();
- deviceInfoShown = true;
- } else {
- $('#max-aperture-row').hide();
- }
- if (deviceInfoShown) {
- $('#device-info-section').show();
- sectionsShown++;
- if (sectionsShown > 1) $('#device-info-divider').show();
- }
- // Section 5: Shooting Mode
- let shootingModeShown = false;
- if (exif.ExposureProgram !== undefined) {
- let programs = ["Not defined", "Manual", "Normal program", "Aperture priority", "Shutter priority", "Creative program", "Action program", "Portrait mode", "Landscape mode"];
- let program = programs[exif.ExposureProgram] || "Unknown";
- $('#exposure-program-value').text(program);
- $('#exposure-program-row').show();
- shootingModeShown = true;
- } else {
- $('#exposure-program-row').hide();
- }
- if (exif.ExposureMode !== undefined) {
- let modes = ["Auto exposure", "Manual exposure", "Auto bracket"];
- let mode = modes[exif.ExposureMode] || "Unknown";
- $('#exposure-mode-value').text(mode);
- $('#exposure-mode-row').show();
- shootingModeShown = true;
- } else {
- $('#exposure-mode-row').hide();
- }
- if (exif.MeteringMode !== undefined) {
- let metering = ["Unknown", "Average", "Center-weighted average", "Spot", "Multi-spot", "Pattern", "Partial"];
- let meter = metering[exif.MeteringMode] || "Unknown";
- $('#metering-mode-value').text(meter);
- $('#metering-mode-row').show();
- shootingModeShown = true;
- } else {
- $('#metering-mode-row').hide();
- }
- if (exif.WhiteBalance !== undefined) {
- let wb = exif.WhiteBalance === 0 ? "Auto" : "Manual";
- $('#white-balance-value').text(wb);
- $('#white-balance-row').show();
- shootingModeShown = true;
- } else {
- $('#white-balance-row').hide();
- }
- if (exif.Flash !== undefined) {
- let flash = (exif.Flash & 1) ? "On" : "Off";
- $('#flash-value').text(flash);
- $('#flash-row').show();
- shootingModeShown = true;
- } else {
- $('#flash-row').hide();
- }
- if (exif.SceneCaptureType !== undefined) {
- let scenes = ["Standard", "Landscape", "Portrait", "Night scene"];
- let scene = scenes[exif.SceneCaptureType] || "Unknown";
- $('#scene-capture-value').text(scene);
- $('#scene-capture-row').show();
- shootingModeShown = true;
- } else {
- $('#scene-capture-row').hide();
- }
- if (shootingModeShown) {
- $('#shooting-mode-section').show();
- sectionsShown++;
- if (sectionsShown > 1) $('#shooting-mode-divider').show();
- }
- // Section 6: Technical Parameters
- let technicalParamsShown = false;
- if (exif.ShutterSpeedValue) {
- exif.ShutterSpeedValue = JSON.parse(exif.ShutterSpeedValue);
- let apexValue = parseExifValue(exif.ShutterSpeedValue);
- let shutterSpeedSeconds = Math.pow(2, -apexValue);
- let shutterValue = formatShutterSpeed(shutterSpeedSeconds);
- $('#shutter-speed-tech-value').text(shutterValue);
- $('#shutter-speed-tech-row').show();
- technicalParamsShown = true;
- } else {
- $('#shutter-speed-tech-row').hide();
- }
- if (exif.ApertureValue) {
- exif.ApertureValue = JSON.parse(exif.ApertureValue);
- let apexValue = parseExifValue(exif.ApertureValue);
- let apertureValue = Math.pow(2, apexValue / 2);
- $('#aperture-value-value').text(apertureValue.toFixed(1) + ' EV');
- $('#aperture-value-row').show();
- technicalParamsShown = true;
- } else {
- $('#aperture-value-row').hide();
- }
- if (exif.FocalPlaneXResolution && exif.FocalPlaneYResolution) {
- exif.FocalPlaneXResolution = JSON.parse(exif.FocalPlaneXResolution);
- exif.FocalPlaneYResolution = JSON.parse(exif.FocalPlaneYResolution);
- let xRes = parseExifValue(exif.FocalPlaneXResolution);
- let yRes = parseExifValue(exif.FocalPlaneYResolution);
- $('#focal-plane-res-value').text(Math.round(xRes) + ' × ' + Math.round(yRes));
- $('#focal-plane-res-row').show();
- technicalParamsShown = true;
- } else {
- $('#focal-plane-res-row').hide();
- }
- if (technicalParamsShown) {
- $('#technical-params-section').show();
- sectionsShown++;
- if (sectionsShown > 1) $('#technical-params-divider').show();
- }
- }
- function restoreFromHash() {
- if (window.location.hash) {
- let hashData = decodeURIComponent(window.location.hash.substring(1));
- try {
- let data = JSON.parse(hashData);
- // Find the element with matching filepath
- let elements = document.querySelectorAll('[filedata]');
- for (let el of elements) {
- let fdStr = el.getAttribute('filedata');
- if (fdStr) {
- let fd = JSON.parse(decodeURIComponent(fdStr));
- if (fd.filepath === data.filepath) {
- showImage(el);
- ShowModal();
- break;
- }
- }
- }
- } catch (e) {
- console.error('Invalid hash data', e);
- }
- }
- }
- // Modify the window onload event to ensure folder and thumbnails are loaded first
- window.addEventListener('load', () => {
- setTimeout(function(){
- if (window.location.hash) {
- const hashData = decodeURIComponent(window.location.hash.substring(1));
- try {
- const data = JSON.parse(hashData);
- let filename = data.filename;
- let filepath = data.filepath;
- let dir = filepath.split("/").slice(0, -1).join("/");
- // Access the Alpine data instance
- const appElement = document.querySelector('[x-data*="photoListObject"]');
- if (appElement) {
- const app = appElement._x_dataStack[0];
- if (app.currentPath !== dir) {
- app.updateRenderingPath(dir, () => {
- setTimeout(function(){
- console.log("Test")
- restoreFromHash();
- }, 100);
- });
- } else {
- // Folder is already loaded, try to restore immediately
- restoreFromHash();
- }
- }
- } catch (e) {
- console.error('Invalid hash data', e);
- }
- }
- }, 100);
- });
|