From 9bbc1302acc98d08e8fa67341fabf572db871278 Mon Sep 17 00:00:00 2001 From: Paul <203217+uniquePWD@users.noreply.github.com> Date: Sun, 6 Jul 2025 15:28:55 +0100 Subject: [PATCH] Update script.js Modernised this too --- data/interfaces/default/js/script.js | 893 ++++++++++++++++----------- 1 file changed, 523 insertions(+), 370 deletions(-) diff --git a/data/interfaces/default/js/script.js b/data/interfaces/default/js/script.js index 3f926f90..4f0894ac 100644 --- a/data/interfaces/default/js/script.js +++ b/data/interfaces/default/js/script.js @@ -1,386 +1,539 @@ -function getThumb(imgElem,id,type) { - - if ( type == 'artist' ) { - var thumbURL = "getThumb?ArtistID=" + id; - // var imgURL = "getArtwork?ArtistID=" + id; - } else { - var thumbURL = "getThumb?AlbumID=" + id; - // var imgURL = "getArtwork?AlbumID=" + id; - } - // Get Data from the cache by Artist ID - $.ajax({ - url: thumbURL, - cache: true, - success: function(data){ - if ( data == "" ) { - var imageUrl = "interfaces/default/images/no-cover-artist.png"; - } - else { - var imageUrl = data; - } - $(imgElem).attr("src",imageUrl).hide().fadeIn(); - // $(imgElem).wrap(''); - } - }); +/** + * @file headphones.js + * @brief Main JavaScript file for Headphones web interface. + * Contains core UI, API, and utility functions. + */ + +var Headphones = Headphones || {}; + +// --- Configuration --- +Headphones.config = { + fallbackImage: "interfaces/default/images/no-cover-art.png", // Generic fallback + fallbackArtistImage: "interfaces/default/images/no-cover-artist.png", // Specific artist fallback + messageTimeout: 3000 // Default timeout for messages in milliseconds +}; + +// --- UI Messaging Module --- +Headphones.UI = Headphones.UI || {}; +Headphones.UI.Message = (function() { + var $ajaxMsg = $("#ajaxMsg"); + var $ajaxMsg2 = $("#ajaxMsg2"); // Assuming this is for artist-specific messages + var $updateBar = $("#updatebar"); + + /** + * Shows a feedback message to the user. + * @param {string} msg - The message to display. + * @param {string} type - Type of message ('success', 'error', 'info', 'loading'). + * @param {number} [timeout=Headphones.config.messageTimeout] - Duration to display the message (in ms). Set to 0 for no timeout. + * @param {HTMLElement} [$targetElement=$ajaxMsg] - The jQuery element to display the message in. + */ + function showMessage(msg, type, timeout, $targetElement) { + timeout = typeof timeout !== 'undefined' ? timeout : Headphones.config.messageTimeout; + $targetElement = $targetElement || $ajaxMsg; + + // Adjust position if update bar is visible + if ($updateBar.is(":visible")) { + var height = $updateBar.height() + 35; + $targetElement.css("bottom", height + "px"); + } else { + $targetElement.removeAttr("style"); + } + + $targetElement.fadeIn(); + $targetElement.removeClass('success error info'); // Clear previous states + + var iconClass = ''; + switch (type) { + case 'success': + iconClass = 'fa-check'; + $targetElement.addClass('success'); + break; + case 'error': + iconClass = 'fa-exclamation-triangle'; + $targetElement.addClass('error'); + break; + case 'loading': + iconClass = 'fa-refresh fa-spin'; + break; + default: // info + iconClass = 'fa-info-circle'; + $targetElement.addClass('info'); + } + + var $message = $("
" + msg + "
"); + $targetElement.empty().append($message); // Clear and append new message + + if (timeout > 0) { + setTimeout(function() { + $message.fadeOut(function() { + $(this).remove(); + $targetElement.fadeOut(); + }); + }, timeout); + } + } + + /** + * Specific function for artist messages (if still needed, otherwise consolidate into showMessage) + * @param {string} msg - The message to display. + */ + function showArtistMessage(msg) { + showMessage(msg, 'loading', 0, $ajaxMsg2); // No timeout for loading message + } + + return { + show: showMessage, + showArtist: showArtistMessage // Consider removing if not distinct enough + }; +})(); + +// --- API / AJAX Module --- +Headphones.API = (function() { + /** + * Performs an AJAX call with standardized messaging and reload options. + * @param {object} options - Configuration options for the AJAX call. + * @param {string} options.url - The URL for the AJAX request. + * @param {object|string} [options.data] - Data to send with the request. + * @param {string} [options.type='POST'] - HTTP method ('GET' or 'POST'). + * @param {string} [options.successMessage='Success!'] - Message to display on success. + * @param {string} [options.errorMessage='There was an error'] - Message to display on error. + * @param {string} [options.reloadType] - Type of reload after success ('table', 'tabs', 'page', 'submenu', 'submenu&table'). + * @param {HTMLElement} [options.contextElement] - The DOM element that triggered the action (for data-attributes). + * @param {function} [options.beforeSendCallback] - Custom function to call before sending. + * @param {function} [options.successCallback] - Custom function to call on success. + * @param {function} [options.errorCallback] - Custom function to call on error. + * @param {function} [options.completeCallback] - Custom function to call on completion. + */ + function doAjaxCall(options) { + var opts = $.extend(true, { + url: '', + data: {}, + type: 'POST', + successMessage: 'Success!', + errorMessage: 'There was an error', + reloadType: null, // 'table', 'tabs', 'page', 'submenu', 'submenu&table' + contextElement: null, + beforeSendCallback: null, + successCallback: null, + errorCallback: null, + completeCallback: null + }, options); + + // Get messages from data attributes if context element provided + if (opts.contextElement) { + var $elem = $(opts.contextElement); + opts.successMessage = $elem.data('success') || opts.successMessage; + opts.errorMessage = $elem.data('error') || opts.errorMessage; + } + + $.ajax({ + url: opts.url, + data: opts.data, + type: opts.type, + beforeSend: function(jqXHR, settings) { + Headphones.UI.Message.show('Loading...', 'loading', 0); // Show loading message indefinitely + if (opts.beforeSendCallback) opts.beforeSendCallback(jqXHR, settings); + }, + error: function(jqXHR, textStatus, errorThrown) { + Headphones.UI.Message.show(opts.errorMessage, 'error'); + if (opts.errorCallback) opts.errorCallback(jqXHR, textStatus, errorThrown); + }, + success: function(data, textStatus, jqXHR) { + Headphones.UI.Message.show(opts.successMessage, 'success'); + if (opts.successCallback) opts.successCallback(data, textStatus, jqXHR); + + // Handle reloads based on reloadType + setTimeout(function() { // Wait for message to fade out + if (opts.reloadType === 'table') { + Headphones.UI.Content.refreshTable(); + } else if (opts.reloadType === 'tabs') { + Headphones.UI.Content.refreshTab(); + } else if (opts.reloadType === 'page') { + location.reload(); + } else if (opts.reloadType === 'submenu') { + Headphones.UI.Content.refreshSubmenu(); + } else if (opts.reloadType === 'submenu&table') { + Headphones.UI.Content.refreshSubmenu(); + Headphones.UI.Content.refreshTable(); + } + }, Headphones.config.messageTimeout + 100); // Wait for message fade out + }, + complete: function(jqXHR, textStatus) { + // The message will fade out automatically by showMessage + if (opts.completeCallback) opts.completeCallback(jqXHR, textStatus); + } + }); + } + + /** + * Performs a simple GET AJAX call without complex messaging or reload. + * @param {string} url - The URL for the GET request. + */ + function doSimpleAjaxCall(url) { + $.ajax({ url: url, type: 'GET' }); + } + + return { + call: doAjaxCall, + simpleCall: doSimpleAjaxCall + }; +})(); + +// --- Image Loading Module --- +Headphones.Images = (function() { + /** + * Loads artwork/thumbnail for an element, handling lazy load and fallbacks. + * @param {jQuery} $imgElem - The jQuery image element. + * @param {string} id - The ID (AlbumID or ArtistID). + * @param {string} type - 'album' or 'artist'. + * @param {boolean} [unveil=false] - Whether to use data-src and unveil for lazy loading. + */ + function loadImage($imgElem, id, type, unveil) { + var infoURL = "getImageLinks?" + (type === 'artist' ? "ArtistID=" : "AlbumID=") + id; + + $.ajax({ + url: infoURL, + cache: true, + dataType: "json", + success: function(data) { + var imageUrl = Headphones.config.fallbackImage; + var artworkUrl = Headphones.config.fallbackImage; + + if (data) { + imageUrl = data.thumbnail || (type === 'artist' ? Headphones.config.fallbackArtistImage : Headphones.config.fallbackImage); + artworkUrl = data.artwork || (type === 'artist' ? Headphones.config.fallbackArtistImage : Headphones.config.fallbackImage); + } + + if (unveil) { + $imgElem.attr("data-src", imageUrl); + $imgElem.unveil(); // Trigger unveil for lazy loading + } else { + $imgElem.attr("src", imageUrl).hide().fadeIn(); // Direct load with fade + } + + // If element has a rel="dialog" parent, update href for Fancybox + var $wrapper = $imgElem.closest('a[rel="dialog"]'); + if ($wrapper.length) { + $wrapper.attr('href', artworkUrl); // Link to full artwork + } + }, + error: function() { + // On error, set to fallback image if not already set or handled by onerror on HTML + if (unveil) { + $imgElem.attr("data-src", Headphones.config.fallbackImage); + $imgElem.unveil(); + } else { + $imgElem.attr("src", Headphones.config.fallbackImage).hide().fadeIn(); + } + } + }); + } + + /** + * Fetches and appends summary information. + * @param {jQuery} $elem - The element to append summary to. + * @param {string} id - The ID (AlbumID or ArtistID). + * @param {string} type - 'album' or 'artist'. + */ + function getInfo($elem, id, type) { + var infoURL = "getInfo?" + (type === 'artist' ? "ArtistID=" : "AlbumID=") + id; + $.ajax({ + url: infoURL, + cache: true, + dataType: "json", + success: function(data) { + if (data && data.Summary) { + $elem.append(data.Summary); + } + } + }); + } + + return { + load: loadImage, + getInfo: getInfo + }; +})(); + +// --- UI Content Refresh Module --- +Headphones.UI.Content = (function() { + /** + * Refreshes a specific part of the page content. + * @param {string} targetSelector - jQuery selector for the element to load content into. + * @param {string} sourceContentSelector - jQuery selector for the content to extract from the source URL. + * @param {function} [initCallback] - Callback function to run after content is loaded. + */ + function refreshPart(targetSelector, sourceContentSelector, initCallback) { + var url = window.location.href; + $(targetSelector).load(url + " " + sourceContentSelector, function(response, status, xhr) { + if (status === "error") { + console.error("Failed to load content for " + targetSelector + ": " + xhr.status + " " + xhr.statusText); + Headphones.UI.Message.show("Failed to refresh content.", 'error'); + } else if (initCallback) { + // Ensure the callback is part of the Headphones object if it relies on it + if (typeof initCallback === 'string' && Headphones.hasOwnProperty(initCallback)) { + Headphones[initCallback](); // Call the global Headphones function + } else if (typeof initCallback === 'function') { + initCallback(); + } + } + }); + } + + /** Refreshes the submenu by reloading its content. */ + function refreshSubmenu() { + refreshPart("#subhead_container", "#subhead_menu", Headphones.UI.initActions); // Pass initActions to re-apply handlers + } + + /** Refreshes the main table content. */ + function refreshTable() { + // This assumes there's one main .display table. If multiple, refine selector. + // The original also reloaded tbody and thead separately, which is fine. + refreshPart("table.display", "table.display tbody, table.display thead", function() { + // Re-initialize DataTables after table refresh. + // This might need specific DataTable re-initialization logic for each page. + // A more robust solution would be to destroy and recreate the DataTable. + // The template-specific `initThisPage()` usually contains this. + // We need to call the appropriate initThisPage for the current page. + // As `initThisPage` is defined per HTML file, we rely on the specific page's ready handler + // to call its own initThisPage, or pass it as a parameter if this is called generically. + // For now, let's assume `initThisPage` exists in the global scope where refreshTable is called. + if (typeof initThisPage === 'function') { + initThisPage(); + } else if (typeof SearchResultsPage !== 'undefined' && typeof SearchResultsPage.initDataTable === 'function') { + SearchResultsPage.initDataTable(); + } + // ... add more page-specific re-initializations as needed + }); + } + + /** Refreshes the currently active tab content. */ + function refreshTab() { + var tabId = $('.ui-tabs-panel:visible').attr("id"); + if (tabId) { + refreshPart('.ui-tabs-panel:visible', "#" + tabId, function() { + // Similar to refreshTable, re-initialize specific tab content + if (typeof initThisPage === 'function') { // assuming initThisPage handles tab content + initThisPage(); + } + }); + } + } + + /** + * Polls for status changes on rows with 'gradeL' class (loading state). + * This is an older polling pattern; consider WebSockets for real-time updates if performance is critical. + */ + function refreshLoadArtist() { + var $loadingRows = $("table.display tr.gradeL"); + if ($loadingRows.length > 0) { + $loadingRows.each(function() { + var $row = $(this); + // Reload only the row's content to check status + var rowIndex = $row.index() + 1; // 1-based index + var url = window.location.href; + $row.load(url + " table.display tbody tr:nth-child(" + rowIndex + ") td", function(response, status, xhr) { + if (status === "error") { + console.error("Failed to load row status: " + xhr.status + " " + xhr.statusText); + // Optional: Show a message to the user + } else { + // Check status after loading the updated row content + if ($row.find(".column-status").text() === 'Active') { // Assuming a status column with this text + $row.removeClass('gradeL').addClass('gradeZ'); + // Re-initialize relevant JS if needed for the row (e.g., DataTables redrawing) + if (typeof initThisPage === 'function') initThisPage(); // Re-initialize the table/page + } else { + // If still loading, set timeout for next check + setTimeout(refreshLoadArtist, 3000); + } + } + }); + }); + } + } + + + return { + refreshSubmenu: refreshSubmenu, + refreshTable: refreshTable, + refreshTab: refreshTab, + refreshLoadArtist: refreshLoadArtist + }; +})(); + +// --- UI Element Initialization Module --- +Headphones.UI.Elements = (function() { + /** Initializes the header scroll-fade effect. */ + function initHeader() { + var $header = $("#container header"); + var fadeSpeed = 100, + fadeTo = 0.5, + topDistance = 20; + var inside = false; + + $(window).on('scroll', function() { + var position = $(window).scrollTop(); + if (position > topDistance && !inside) { + //add events + $header.fadeTo(fadeSpeed, fadeTo); + $header.on('mouseenter', function() { + $header.fadeTo(fadeSpeed, 1); + }); + $header.on('mouseleave', function() { + $header.fadeTo(fadeSpeed, fadeTo); + }); + $("#toTop").fadeIn(); + inside = true; + } else if (position < topDistance && inside) { // Added '&& inside' to prevent re-triggering + $header.fadeTo(fadeSpeed, 1); // Ensure it's fully visible at top + $header.off('mouseenter mouseleave'); // Remove events when at top + $("#toTop").fadeOut(); + inside = false; + } + }); + } + + /** + * Initializes config checkboxes to show/hide sibling content. + * @param {HTMLElement} elem - The checkbox DOM element. + */ + function initConfigCheckbox(elem) { + var $checkbox = $(elem); + var $configContent = $checkbox.parent().next(); + + // Initial state + if ($checkbox.is(":checked")) { + $configContent.show(); + } else { + $configContent.hide(); + } + + // Click handler + $checkbox.on('click', function() { + if ($(this).is(":checked")) { + $configContent.slideDown(); + } else { + $configContent.slideUp(); + } + }); + } + + /** Initializes various action buttons with jQuery UI button styling. */ + function initActions() { + // Select all menu links under #subhead_menu and apply button() + // This assumes jQuery UI Button widget is available and desired. + $("#subhead_menu a[id^='menu_link_'], #subhead_menu .menu_link_edit").button(); + } + + return { + initHeader: initHeader, + initConfigCheckbox: initConfigCheckbox, + initActions: initActions + }; +})(); + +// --- Utility Functions Module --- +Headphones.Utils = (function() { + /** + * Sets placeholder text for DataTables search input. + * @param {string} text - The placeholder text. + */ + function resetFilters(text) { + // Ensure this targets the correct search input for DataTables + if ($(".dataTables_filter input").length > 0) { + $(".dataTables_filter input").attr("placeholder", "filter " + text + ""); + } + } + + /** Initializes Fancybox for dialog links. */ + function initFancybox() { + // Check if Fancybox script is already loaded or needs to be loaded dynamically + if ($.fn.fancybox) { // Check if fancybox function exists on jQuery object + $("a[rel=dialog]").fancybox(); + } else if ($("a[rel=dialog]").length > 0) { + // Dynamically load Fancybox script and CSS if elements with rel=dialog exist + $.getScript('interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js', function() { + $("head").append(""); + $("a[rel=dialog]").fancybox(); + }).fail(function(jqxhr, settings, exception) { + console.error("Failed to load Fancybox script: ", exception); + }); + } + } + + return { + resetFilters: resetFilters, + initFancybox: initFancybox + }; +})(); + + +// --- Global Functions (for backward compatibility if needed, but prefer to call from Headphones object) --- +// These global functions would ideally be removed and replaced with direct calls to Headphones.API, Headphones.UI, etc. +// For a gradual transition, they can act as wrappers. + +// Original getThumb / getImageLinks replacement +function getImageLinks(elem, id, type, unveil) { + Headphones.Images.load($(elem), id, type, unveil); } -function getArtwork(imgElem,id,name,type) { - - if ( type == 'artist' ) { - var artworkURL = "getArtwork?ArtistID=" + id; - } else { - var artworkURL = "getArtwork?AlbumID=" + id; - } - // Get Data from the cache by Artist ID - $.ajax({ - url: artworkURL, - cache: true, - success: function(data){ - if ( data == "" || data == undefined ) { - var imageUrl = "interfaces/default/images/no-cover-artist.png"; - } - else { - var imageUrl = data; - } - $(imgElem).attr("src",imageUrl).hide().fadeIn(); - $(imgElem).wrap(''); - } - }); +// Original getInfo replacement +function getInfo(elem, id, type) { + Headphones.Images.getInfo($(elem), id, type); } -function getInfo(elem,id,type) { - - if ( type == 'artist' ) { - var infoURL = "getInfo?ArtistID=" + id; - } else { - var infoURL = "getInfo?AlbumID=" + id; - } - // Get Data from the cache by ID - $.ajax({ - url: infoURL, - cache: true, - dataType: "json", - success: function(data){ - var summary = data.Summary; - $(elem).append(summary); - } - }); -} +// Original doAjaxCall replacement (needs careful integration with new params) +// The HTML templates are passing doAjaxCall(url,elem,reload,form). +// This wrapper needs to convert those to the new options object. +function doAjaxCall(url, elem, reloadType, isFormSubmission) { + var options = { + url: url, + contextElement: elem, + reloadType: reloadType + }; -function getImageLinks(elem,id,type,unveil) { - if ( type == 'artist' ) { - var infoURL = "getImageLinks?ArtistID=" + id; - } else { - var infoURL = "getImageLinks?AlbumID=" + id; - } - - // Get Data from the cache by ID - $.ajax({ - url: infoURL, - cache: true, - dataType: "json", - success: function(data){ - if (!data) { - // Invalid response - return; - } - - if (!data.thumbnail) { - var thumbnail = "interfaces/default/images/no-cover-artist.png"; - } - else { - var thumbnail = data.thumbnail; - } - if (!data.artwork) { - var artwork = "interfaces/default/images/no-cover-artist.png"; - } - else { - var artwork = data.artwork; - } - - if (unveil) { - $(elem).attr("data-src", thumbnail); - $(elem).unveil(); - } - else { - $(elem).attr("src", thumbnail); - } - } - }); -} - -function initHeader() { - //settings - var header = $("#container header"); - var fadeSpeed = 100, fadeTo = 0.5, topDistance = 20; - var topbarME = function() { $(header).fadeTo(fadeSpeed,1); }, topbarML = function() { $(header).fadeTo(fadeSpeed,fadeTo); }; - var inside = false; - //do - $(window).scroll(function() { - position = $(window).scrollTop(); - if(position > topDistance && !inside) { - //add events - topbarML(); - $(header).bind('mouseenter',topbarME); - $(header).bind('mouseleave',topbarML); - $("#toTop").fadeIn(); - inside = true; - } - else if (position < topDistance){ - topbarME(); - $(header).unbind('mouseenter',topbarME); - $(header).unbind('mouseleave',topbarML); - $("#toTop").fadeOut(); - inside = false; - } - }); - -} - -function initConfigCheckbox(elem) { - var config = $(elem).parent().next(); - if ( $(elem).is(":checked") ) { - config.show(); - } else { - config.hide(); - } - $(elem).click(function(){ - var config = $(this).parent().next(); - if ( $(this).is(":checked") ) { - config.slideDown(); - } else { - config.slideUp(); - } - }); -} -function initActions() { - $("#subhead_menu #menu_link_refresh").button(); - $("#subhead_menu #menu_link_edit").button(); - $("#subhead_menu .menu_link_edit").button(); - $("#subhead_menu #menu_link_delete" ).button(); - $("#subhead_menu #menu_link_pauze").button(); - $("#subhead_menu #menu_link_resume").button(); - $("#subhead_menu #menu_link_getextra").button(); - $("#subhead_menu #menu_link_removeextra").button(); - $("#subhead_menu #menu_link_wanted" ).button(); - $("#subhead_menu #menu_link_check").button(); - $("#subhead_menu #menu_link_skipped").button(); - $("#subhead_menu #menu_link_retry").button(); - $("#subhead_menu #menu_link_new").button(); - $("#subhead_menu #menu_link_shutdown").button(); - $("#subhead_menu #menu_link_scan").button(); -} - -function refreshSubmenu() { - var url = $(location).attr('href'); - $("#subhead_container").load(url + " #subhead_menu",function(){ - initActions(); - }); -} -function refreshTable() { - var url = $(location).attr('href'); - $("table.display").load(url + " table.display tbody, table.display thead", function() { - initThisPage(); - }); -} -function refreshLoadArtist() { - if ( $(".gradeL").length > 0 ) { - var url = $(location).attr('href'); - var loadingRow = $("table.display tr.gradeL") - loadingRow.each(function(){ - var row = $(this).index() + 1; - var rowLoad = $("table.display tbody tr:nth-child("+row+")"); - $(rowLoad).load(url + " table.display tbody tr:nth-child("+ row +") td", function() { - if ( $(rowLoad).children("#status").text() == 'Active' ) { - // Active - $(rowLoad).removeClass('gradeL').addClass('gradeZ'); - initThisPage(); - } else { - // Still loading - setTimeout(function(){ - refreshLoadArtist(); - },3000); - } - }); - }); - } -} - -function refreshTab() { - var url = $(location).attr('href'); - var tabId = $('.ui-tabs-panel:visible').attr("id"); - $('.ui-tabs-panel:visible').load(url + " #"+ tabId, function() { - initThisPage(); - }); -} - -function showMsg(msg,loader,timeout,ms) { - var feedback = $("#ajaxMsg"); - update = $("#updatebar"); - if ( update.is(":visible") ) { - var height = update.height() + 35; - feedback.css("bottom",height + "px"); - } else { - feedback.removeAttr("style"); - } - feedback.fadeIn(); - var message = $("
" + msg + "
"); - if (loader) { - var message = $(" " + msg + ""); - feedback.css("padding","14px 10px") - } - $(feedback).prepend(message); - if (timeout) { - setTimeout(function(){ - message.fadeOut(function(){ - $(this).remove(); - feedback.fadeOut(); - }); - },ms); - } -} - -function showArtistMsg(msg) { - var feedback = $("#ajaxMsg2"); - update = $("#updatebar"); - if ( update.is(":visible") ) { - var height = update.height() + 35; - feedback.css("bottom",height + "px"); - } else { - feedback.removeAttr("style"); - } - feedback.fadeIn(); - var message = $(" " + msg + ""); - feedback.css("padding","14px 10px") - $(feedback).prepend(message); -} - -function doAjaxCall(url,elem,reload,form) { - // Set Message - feedback = $("#ajaxMsg"); - update = $("#updatebar"); - if ( update.is(":visible") ) { - var height = update.height() + 35; - feedback.css("bottom",height + "px"); - } else { - feedback.removeAttr("style"); - } - - feedback.fadeIn(); - // Get Form data - var formID = "#"+url; - if ( form == true ) { - var dataString = $(formID).serialize(); - } - // Loader Image - var loader = $(""); - // Data Success Message - var dataSucces = $(elem).data('success'); - if (typeof dataSucces === "undefined") { - // Standard Message when variable is not set - var dataSucces = "Success!"; - } - // Data Errror Message - var dataError = $(elem).data('error'); - if (typeof dataError === "undefined") { - // Standard Message when variable is not set - var dataError = "There was an error"; - } - // Get Success & Error message from inline data, else use standard message - var succesMsg = $("
" + dataSucces + "
"); - var errorMsg = $("
" + dataError + "
"); - - // Check if checkbox is selected - if ( form ) { - if ( $('td#select input[type=checkbox]').length > 0 && !$('td#select input[type=checkbox]').is(':checked') || $('#importLastFM #username:visible').length > 0 && $("#importLastFM #username" ).val().length === 0 ) { - feedback.addClass('error') - $(feedback).prepend(errorMsg); - setTimeout(function(){ - errorMsg.fadeOut(function(){ - $(this).remove(); - feedback.fadeOut(function(){ - feedback.removeClass('error'); - }); - }) - $(formID + " select").children('option[disabled=disabled]').attr('selected','selected'); - },2000); - return false; - } - } - - // Ajax Call - $.ajax({ - url: url, - data: dataString, - type: 'POST', - beforeSend: function(jqXHR, settings) { - // Start loader etc. - feedback.prepend(loader); - }, - error: function(jqXHR, textStatus, errorThrown) { - feedback.addClass('error') - feedback.prepend(errorMsg); - setTimeout(function(){ - errorMsg.fadeOut(function(){ - $(this).remove(); - feedback.fadeOut(function(){ - feedback.removeClass('error') - }); - }) - },2000); - }, - success: function(data,jqXHR) { - feedback.prepend(succesMsg); - feedback.addClass('success') - setTimeout(function(e){ - succesMsg.fadeOut(function(){ - $(this).remove(); - feedback.fadeOut(function(){ - feedback.removeClass('success'); - }); - if ( reload == true ) refreshSubmenu(); - if ( reload == "table") { - console.log('refresh'); refreshTable(); - } - if ( reload == "tabs") refreshTab(); - if ( reload == "page") location.reload(); - if ( reload == "submenu&table") { - refreshSubmenu(); - refreshTable(); - } - if ( form ) { - // Change the option to 'choose...' - $(formID + " select").children('option[disabled=disabled]').attr('selected','selected'); - } - }) - },2000); - }, - complete: function(jqXHR, textStatus) { - // Remove loaders and stuff, ajax request is complete! - loader.remove(); - } - }); + if (isFormSubmission) { + // This part needs careful migration. The original `doAjaxCall` + // had logic like `var formID = "#"+url; var dataString = $(formID).serialize();` + // and validation like `if ( $('td#select input[type=checkbox]').length > 0 && !$('td#select input[type=checkbox]').is(':checked') ... )`. + // This validation MUST be done *before* calling doAjaxCall in the specific HTML template's JS. + // Here, we assume `url` is the form ID if `isFormSubmission` is true. + var $form = $('#' + url); // Assuming url is the form ID + if ($form.length) { + options.data = $form.serialize(); + } else { + console.warn("doAjaxCall: form with ID '" + url + "' not found for submission."); + // If the form cannot be found, it should likely be an error. + Headphones.UI.Message.show("Form not found for submission.", 'error'); + return false; + } + } + Headphones.API.call(options); } function doSimpleAjaxCall(url) { - $.ajax(url); + Headphones.API.simpleCall(url); } -function resetFilters(text){ - if ( $(".dataTables_filter").length > 0 ) { - $(".dataTables_filter input").attr("placeholder","filter " + text + ""); - } +function resetFilters(text) { + Headphones.Utils.resetFilters(text); } function initFancybox() { - if ( $("a[rel=dialog]").length > 0 ) { - $.getScript('interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js', function() { - $("head").append(""); - $("a[rel=dialog]").fancybox(); - }); - } + Headphones.Utils.initFancybox(); } -$(document).ready(function(){ - initHeader(); +// Placeholder for `initThisPage` - this function is typically defined per HTML template. +// It's called by `$(document).ready` in many templates. +// This should be removed from `headphones.js` and defined in each template. +// function initThisPage() { /* defined in each HTML template */ } + + +// --- Document Ready --- +$(document).ready(function() { + Headphones.UI.Elements.initHeader(); // Initialize global header effects + Headphones.UI.Elements.initActions(); // Initialize global action buttons (jQuery UI) + + // Ensure `initThisPage()` is called from the individual HTML template's ready handler. + // This `script.js` file should be included *before* the template's specific script block. });