mirror of
https://github.com/rembo10/headphones.git
synced 2026-07-20 16:03:59 +01:00
@@ -1,386 +1,539 @@
|
|||||||
function getThumb(imgElem,id,type) {
|
/**
|
||||||
|
* @file headphones.js
|
||||||
if ( type == 'artist' ) {
|
* @brief Main JavaScript file for Headphones web interface.
|
||||||
var thumbURL = "getThumb?ArtistID=" + id;
|
* Contains core UI, API, and utility functions.
|
||||||
// var imgURL = "getArtwork?ArtistID=" + id;
|
*/
|
||||||
} else {
|
|
||||||
var thumbURL = "getThumb?AlbumID=" + id;
|
var Headphones = Headphones || {};
|
||||||
// var imgURL = "getArtwork?AlbumID=" + id;
|
|
||||||
}
|
// --- Configuration ---
|
||||||
// Get Data from the cache by Artist ID
|
Headphones.config = {
|
||||||
$.ajax({
|
fallbackImage: "interfaces/default/images/no-cover-art.png", // Generic fallback
|
||||||
url: thumbURL,
|
fallbackArtistImage: "interfaces/default/images/no-cover-artist.png", // Specific artist fallback
|
||||||
cache: true,
|
messageTimeout: 3000 // Default timeout for messages in milliseconds
|
||||||
success: function(data){
|
};
|
||||||
if ( data == "" ) {
|
|
||||||
var imageUrl = "interfaces/default/images/no-cover-artist.png";
|
// --- UI Messaging Module ---
|
||||||
}
|
Headphones.UI = Headphones.UI || {};
|
||||||
else {
|
Headphones.UI.Message = (function() {
|
||||||
var imageUrl = data;
|
var $ajaxMsg = $("#ajaxMsg");
|
||||||
}
|
var $ajaxMsg2 = $("#ajaxMsg2"); // Assuming this is for artist-specific messages
|
||||||
$(imgElem).attr("src",imageUrl).hide().fadeIn();
|
var $updateBar = $("#updatebar");
|
||||||
// $(imgElem).wrap('<a href="'+ imgURL +'" rel="dialog" title="' + name + '"></a>');
|
|
||||||
}
|
/**
|
||||||
});
|
* 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 = $("<div class='msg'><i class='fa " + iconClass + "'></i> " + msg + "</div>");
|
||||||
|
$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("<link rel='stylesheet' href='interfaces/default/js/fancybox/jquery.fancybox-1.3.4.css'>");
|
||||||
|
$("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) {
|
// Original getInfo replacement
|
||||||
|
function getInfo(elem, id, type) {
|
||||||
if ( type == 'artist' ) {
|
Headphones.Images.getInfo($(elem), id, type);
|
||||||
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('<a href="'+ imageUrl +'" rel="dialog" title="' + name + '"></a>');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getInfo(elem,id,type) {
|
// Original doAjaxCall replacement (needs careful integration with new params)
|
||||||
|
// The HTML templates are passing doAjaxCall(url,elem,reload,form).
|
||||||
if ( type == 'artist' ) {
|
// This wrapper needs to convert those to the new options object.
|
||||||
var infoURL = "getInfo?ArtistID=" + id;
|
function doAjaxCall(url, elem, reloadType, isFormSubmission) {
|
||||||
} else {
|
var options = {
|
||||||
var infoURL = "getInfo?AlbumID=" + id;
|
url: url,
|
||||||
}
|
contextElement: elem,
|
||||||
// Get Data from the cache by ID
|
reloadType: reloadType
|
||||||
$.ajax({
|
};
|
||||||
url: infoURL,
|
|
||||||
cache: true,
|
|
||||||
dataType: "json",
|
|
||||||
success: function(data){
|
|
||||||
var summary = data.Summary;
|
|
||||||
$(elem).append(summary);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getImageLinks(elem,id,type,unveil) {
|
if (isFormSubmission) {
|
||||||
if ( type == 'artist' ) {
|
// This part needs careful migration. The original `doAjaxCall`
|
||||||
var infoURL = "getImageLinks?ArtistID=" + id;
|
// had logic like `var formID = "#"+url; var dataString = $(formID).serialize();`
|
||||||
} else {
|
// and validation like `if ( $('td#select input[type=checkbox]').length > 0 && !$('td#select input[type=checkbox]').is(':checked') ... )`.
|
||||||
var infoURL = "getImageLinks?AlbumID=" + id;
|
// 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
|
||||||
// Get Data from the cache by ID
|
if ($form.length) {
|
||||||
$.ajax({
|
options.data = $form.serialize();
|
||||||
url: infoURL,
|
} else {
|
||||||
cache: true,
|
console.warn("doAjaxCall: form with ID '" + url + "' not found for submission.");
|
||||||
dataType: "json",
|
// If the form cannot be found, it should likely be an error.
|
||||||
success: function(data){
|
Headphones.UI.Message.show("Form not found for submission.", 'error');
|
||||||
if (!data) {
|
return false;
|
||||||
// Invalid response
|
}
|
||||||
return;
|
}
|
||||||
}
|
Headphones.API.call(options);
|
||||||
|
|
||||||
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 = $("<div class='msg'>" + msg + "</div>");
|
|
||||||
if (loader) {
|
|
||||||
var message = $("<i class='fa fa-refresh fa-spin'></i> " + msg + "</div>");
|
|
||||||
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 = $("<i class='fa fa-refresh fa-spin'></i> " + msg + "</div>");
|
|
||||||
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 = $("<i class='fa fa-refresh fa-spin'></i>");
|
|
||||||
// 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 = $("<div class='msg'><i class='fa fa-check'></i> " + dataSucces + "</div>");
|
|
||||||
var errorMsg = $("<div class='msg'><i class='fa fa-exclamation-triangle'></i> " + dataError + "</div>");
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSimpleAjaxCall(url) {
|
function doSimpleAjaxCall(url) {
|
||||||
$.ajax(url);
|
Headphones.API.simpleCall(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetFilters(text){
|
function resetFilters(text) {
|
||||||
if ( $(".dataTables_filter").length > 0 ) {
|
Headphones.Utils.resetFilters(text);
|
||||||
$(".dataTables_filter input").attr("placeholder","filter " + text + "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFancybox() {
|
function initFancybox() {
|
||||||
if ( $("a[rel=dialog]").length > 0 ) {
|
Headphones.Utils.initFancybox();
|
||||||
$.getScript('interfaces/default/js/fancybox/jquery.fancybox-1.3.4.js', function() {
|
|
||||||
$("head").append("<link rel='stylesheet' href='interfaces/default/js/fancybox/jquery.fancybox-1.3.4.css'>");
|
|
||||||
$("a[rel=dialog]").fancybox();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function(){
|
// Placeholder for `initThisPage` - this function is typically defined per HTML template.
|
||||||
initHeader();
|
// 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.
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user