var browser = function() { var history = [{ source: "", artist: { name: "", }, album: { name: "", artist: "", }, track: { name: "", } }]; var displaypointer = 0; var panelclosed = {artist: false, album: false, track: false}; var waitingon = {artist: false, album: false, track: false, index: -1, source: null}; var extraPlugins = []; var maxhistorylength = 20; var sources = nowplaying.getAllPlugins(); var doneone = false; function displayTheData(ptr, showartist, showalbum, showtrack) { var a = waitingon.artist; var b = waitingon.album; var c = waitingon.track; waitingon = { artist: a || showartist, album: b || showalbum, track: c || showtrack, index: history[ptr].mastercollection.nowplayingindex, source: history[ptr].source }; debug.log("BROWSER", "Waiting on source",waitingon.source,"for index", waitingon.index); if (waitingon.source != prefs.infosource) { // Need to do this here rather than in switchsource otherwise it prevents // the browser from accepting the update $("#button_source"+prefs.infosource).removeClass("currentbun"); prefs.save({infosource: waitingon.source}); debug.log("BROWSER", "Source switched to",prefs.infosource); $("#button_source"+prefs.infosource).addClass("currentbun"); } for (var i in waitingon) { if (waitingon[i] === true) { $("#"+i+"information").html(waitingBanner(i)); } } for (var i = 1; i < history.length-1; i++) { history[i].mastercollection.stopDisplaying(); } // Remember, here we tell artist, album, and track to display even if we only want one of them. // This is because we need the new collections to handle clicks and other stuff, // as otherwise it all gets very out of hand and impossible to follow, // mainly because it's super tricky to keep the stopDisplaying/displayData displaying flags // all in sync since they're global to one dataCollection and not individual for artist, // album, and track. (This was tried before and got stupid). history[ptr].mastercollection.sendDataToBrowser(waitingon); if (displaypointer == history.length-1) { // We only allow artist switching on the current playing track. // It's not that it doesn't work, but it means the artist switch gets added to // the end of history and then you have to go back to get to the current track, // which means things stop auto-updating. // Also it makes truncating the history really hard. // TODO perhaps artist switches should be spliced in? history[ptr].mastercollection.doArtistChoices(); } else { if ($("#artistchooser").is(':visible')) { $("#artistchooser").slideUp('fast'); } } } function waitingBanner(which) { var html = ''; return html; } function banner(data, title, hidden, source, close) { var html = ''; html += '
' +language.gettext("button_history") +'
' ); if (displaypointer == 1) { $("#backbutton").off('click').addClass('button-disabled'); } if (displaypointer > 1 && $("#backbutton").hasClass('button-disabled')) { $("#backbutton").on('click', browser.back ); $("#backbutton").removeClass('button-disabled'); } if (displaypointer == (history.length)-1) { $("#forwardbutton").off('click').addClass('button-disabled'); } if (displaypointer < (history.length)-1 && $("#forwardbutton").hasClass('button-disabled')) { $("#forwardbutton").on('click', browser.forward ); $("#forwardbutton").removeClass('button-disabled'); } var bits = ["artist","album","track"]; var t = $('', {class: 'histable', width: '100%'}).appendTo('#historypanel'); for (var i = 1; i < history.length; i++) { var clas = "top clickable clickicon"; if (i == displaypointer) { clas = clas + " current"; } var r = $('', {class: clas, name: i}).appendTo(t); r.append(''); var td = $('
').appendTo(r); var html = ''; bits.forEach(function(n) { if (history[i][n].collection) { html += history[i][n].collection.bannername()+'
'; } else { html += language.gettext("label_"+n)+' : '+history[i][n].name+'
'; } }); td.html(html); } $('#historypanel').on('click', browser.historyClicked); } function removeSection(section) { extraPlugins[section].parent.close(); extraPlugins[section].div.fadeOut('fast', function() { extraPlugins[section].div.empty(); extraPlugins[section].div.remove(); extraPlugins[section].div = null; if ($('#pluginholder').length > 0 && openPlugins() == 0) { layoutProcessor.sourceControl('specialplugins'); } }); } function openPlugins() { var c = 0; for (var i in extraPlugins) { if (extraPlugins[i].div !== null) { c++; } } return c; } function checkHistoryLength() { if (history.length > maxhistorylength) { debug.shout("BROWSER", "Truncating History"); var np = history[1].mastercollection.nowplayingindex; history.splice(1,1); displaypointer--; for (var i = 1; i < history.length; i++) { // Scan our history to see if this nowplayingindex is being used anywhere else if (history[i].mastercollection.nowplayingindex == np) { return; } } debug.log("BROWSER","Telling nowplaying to remove nowplayingindex",np); nowplaying.remove(np); } } return { historyClicked: function(event) { var clickedRow = $(event.target); while (!clickedRow.hasClass('clickable') && !clickedRow.is('#historypanel')) { clickedRow = clickedRow.parent(); } if (clickedRow.hasAttr('name')) { browser.doHistory(clickedRow.attr('name')); } }, areweatfront: function() { debug.log("BROWSER","displaypointer:",displaypointer,"historylength",history.length); return (displaypointer == history.length - 1); }, createButtons: function() { for (var i in sources) { if (sources[i].icon !== null) { debug.log("BROWSER", "Found plugin", i,sources[i].icon); layoutProcessor.addInfoSource(i, sources[i]); } } layoutProcessor.setupInfoButtons(); }, nextSource: function(direction) { var s = new Array(); for (var i in sources) { if (sources[i].icon !== null) { s.push(i); } } var cursourceidx = s.indexOf(prefs.infosource); var newsourceidx = cursourceidx+direction; if (newsourceidx >= s.length) newsourceidx = 0; if (newsourceidx < 0) newsourceidx = s.length-1; browser.switchsource(s[newsourceidx]); }, dataIsComing: function(mastercollection, isartistswitch, nowplayingindex, source, trackartist, artist, albumartist, album, track) { debug.log("BROWSER","Data is coming",isartistswitch, nowplayingindex, source, artist, albumartist, album, track) if (prefs.hidebrowser) { debug.log("BROWSER","Browser is hidden. Ignoring Data"); return; } var showalbum = (album != history[displaypointer].album.name || albumartist != history[displaypointer].album.artist || source != prefs.infosource); var showartist = (isartistswitch || artist != history[displaypointer].artist.name || source != prefs.infosource || (showalbum && artist != history[displaypointer].artist.name)); var showtrack = (track != history[displaypointer].track.name || showalbum || source != prefs.infosource); checkHistoryLength(); history.push( { mastercollection: mastercollection, source: source, trackartist: trackartist, artist: { name: artist, collection: null }, album: { name: album, artist: albumartist, collection: null }, track: { name: track, collection: null } }); // Display the new data only if either: // We are currently displaying the most recent track (ie continuous updates) // This is an artist switch request // This is a source switch request // History has been truncated such that the currently displayed info needs to be removed if (displaypointer == history.length - 2 || isartistswitch || source != prefs.infosource || displaypointer < 1) { displaypointer = history.length - 1; // Hack timeout to get around a problem where Masonry doesn't layout properly // during page load. if (doneone) { displayTheData( displaypointer, showartist, showalbum, showtrack ); } else { setTimeout(function() { displayTheData( displaypointer, showartist, showalbum, showtrack ); doneone = true; }, 1000); } } updateHistory(); }, Update: function(collection, type, source, nowplayingindex, data, scrollto, force) { if (prefs.hidebrowser) { return false; } debug.mark("BROWSER", "Got",type,"info from",source,"for index",nowplayingindex,force,waitingon); if (force === true || (source == waitingon.source && nowplayingindex == waitingon.index)) { if (force === true || waitingon[type]) { debug.trace("BROWSER", " .. and we are going to display it"); if (data.data !== null && (source == "file" || data.name !== "")) { if ($("#"+type+"information").is(':hidden')) { $("#"+type+"information").show(); } $("#"+type+"information").html(banner(data, (collection === null) ? type : collection.bannertitle(), panelclosed[type], source)+data.data); } else { $("#"+type+"information").empty(); if ($("#"+type+"information").is(':visible')) { $("#"+type+"information").hide(); } } waitingon[type] = false; if (scrollto) { layoutProcessor.goToBrowserPanel(type); } return true; } else { return false; } } }, reDo: function(index, source) { if (history[displaypointer].mastercollection && index == history[displaypointer].mastercollection.nowplayingindex && source == prefs.infosource) { debug.log("BROWSER","Re-displaying data for",source,"index",index); displayTheData(displaypointer, true, true, true); } }, switchsource: function(src) { debug.log("BROWSER","Switching to",src); if (displaypointer >= 1) { displaypointer = history.length - 1; history[displaypointer].mastercollection.populate(src, true); updateHistory(); } }, handleClick: function(source, element, event) { debug.log("BROWSER","Was clicked on",source,element); if (element.hasClass('frog')) { toggleSection(element); } else if (element.hasClass('tadpole')) { removeSection(source); } else if (element.hasClass('plugclickable')) { extraPlugins[source].parent.handleClick(element, event); } else if (element.hasClass('clickartistchoose')) { nowplaying.switchArtist(history[displaypointer].source, element.next().val()); } else { history[displaypointer].mastercollection.handleClick(history[displaypointer].source, source, element, event); } }, // This function is for links which are followed internally by one of the panels // eg wikipedia speciaUpdate: function(source, panel, data) { debug.mark("BROWSER","Special Update from",source,"for",panel); var n = new specialUpdateCollection(source, panel, data); history.splice(displaypointer+1,0, { mastercollection: history[displaypointer].mastercollection, source: source, trackartist: history[displaypointer].trackartist, artist: { name: history[displaypointer].artist.name, collection: (panel == "artist") ? n : history[displaypointer].artist.collection }, album: { name: history[displaypointer].album.name, albumartist: history[displaypointer].albumartist, collection: (panel == "album") ? n : history[displaypointer].album.collection }, track: { name: history[displaypointer].track.name, collection: (panel == "track") ? n : history[displaypointer].track.collection } }); waitingon[panel] = true; waitingon.source = source; waitingon.index = history[displaypointer].mastercollection.nowplayingindex; displaypointer++; updateHistory(); browser.Update(n, panel, source, waitingon.index, data, true); }, doHistory: function(index) { debug.log("BROWSER", "Doing history, index is",index); var showartist = (history[index].artist.collection === null && (history[index].artist.name != history[displaypointer].artist.name || history[index].source != history[displaypointer].source || history[displaypointer].artist.collection !== null)); var showalbum = (history[index].album.collection === null && (history[index].album.name != history[displaypointer].album.name || history[index].album.artist != history[displaypointer].album.artist || history[index].source != history[displaypointer].source || history[displaypointer].album.collection !== null)); var showtrack = (history[index].track.collection === null && (history[index].track.name != history[displaypointer].track.name || history[index].album.name != history[displaypointer].album.name || history[index].album.artist != history[displaypointer].album.artist || history[index].source != history[displaypointer].source || history[displaypointer].track.collection !== null)); displaypointer = index; debug.log("BROWSER","History flags are",showartist,showalbum,showtrack); // Calling displayTheData is important even if all the showxxx flags are false // since it makes sure the correct trackDataCollection gets its displaying flag set. displayTheData(displaypointer, showartist, showalbum, showtrack); updateHistory(); var bits = ["artist","album","track"]; bits.forEach(function(n) { debug.log("BROWSER","Updating",n); if (history[index][n].collection) { waitingon[n] = true; waitingon.source = history[index].source; waitingon.index = history[index].mastercollection.nowplayingindex; browser.Update(history[index][n].collection, n, waitingon.source, waitingon.index, history[index][n].collection.getData()); } }); layoutProcessor.afterHistory(); }, forward: function() { browser.doHistory(displaypointer+1); return false; }, back: function() { browser.doHistory(displaypointer-1); return false; }, registerExtraPlugin: function(id, name, parent, help) { var displayer; if (prefs.hidebrowser) { $("#hidebrowser").prop("checked", !$("#hidebrowser").is(':checked')); prefs.save({hidebrowser: $("#hidebrowser").is(':checked')}, hideBrowser); } if ($('#pluginholder').length > 0 && !($('#pluginholder').is(':visible'))) { displayer = $('
', {id: id+"information", class: "infotext invisible"}).appendTo('#pluginholder'); } else { displayer = $('
', {id: id+"information", class: "infotext invisible"}).insertBefore('#artistchooser'); } var opts = {name: name}; if (help) { opts.help = help; } displayer.html(banner(opts, id, false, false, true)); panelclosed[id] = false; displayer.off('click'); extraPlugins[id] = { div: displayer, parent: parent }; displayer.on('click', '.infoclick', onBrowserClicked); return displayer; }, goToPlugin: function(id) { layoutProcessor.goToBrowserPlugin(id); }, rePoint: function(panel, params) { if (prefs.hidebrowser || $('#infopane').width() == 0) { return } debug.debug("BROWSER","Repointing"); layoutProcessor.updateInfopaneScrollbars(); $('#infopane .masonified:visible').each(function() { var h = $(this); var width = calcPercentWidth(h, '.tagholder', 500, h.width()); h.find(".tagholder").css('width', width.toString()+'%'); // Check if it is being handled by Masonry, prevents early init with no paramerts if (typeof(params) == 'undefined' && h.css('position') == 'relative') { h.masonry(); } }); $('#infopane .masonified5.filled:visible').each(function() { var h = $(this); var width = calcPercentWidth(h, '.tagholder5', 260, h.width()); h.find(".tagholder5").css('width', width.toString()+'%'); h.find(".sizer").css('width', width.toString()+'%'); if (typeof(params) == 'undefined' && h.css('position') == 'relative') { h.masonry(); } }); $('#infopane .masonified2:visible').each(function() { var h = $(this); var width = calcPercentWidth(h, '.tagholder2', 260, h.width()); h.find(".tagholder2").css('width', width.toString()+'%'); h.find(".sizer").css('width', width.toString()+'%'); h.find(".tagholder_wide").css("width", "100%"); h.find(".brick_wide").css("width", "100%"); if (typeof(params) == 'undefined' && h.css('position') == 'relative') { h.masonry(); } }); $('#infopane .masonified7:visible').each(function() { var h = $(this); if (h.width() > 800) { var width = 48; } else { var width = 99; } h.find(".tagholder2").css('width', width.toString()+'%'); h.find(".tagholder_wode").css("width", "100%"); if (typeof(params) == 'undefined' && h.css('position') == 'relative') { h.masonry(); } }); $('#infopane .masonified4:visible').each(function() { var h = $(this); var width = calcPercentWidth(h, '.tagholder4', 140, h.width()); h.find(".tagholder4").css('width', width.toString()+'%'); if (typeof(params) == 'undefined' && h.css('position') == 'relative') { h.masonry(); } }); $('#infopane .mixcontainer:visible').each(function() { var h = $(this); var w = h.width(); var m = h.children('.mixbox'); if (m.length == 1 || w < 700) { m.css('width', '100%'); } else { m.css('width', '50%'); } }); if (typeof(params) != 'undefined') { params.gutter = masonry_gutter; panel.masonry(params); } } } }(); function specialUpdateCollection(source, panel, data) { this.bannertitle = function() { return source; } this.bannername = function() { return data.name; } this.getData = function() { return data; } }