Experimental: upon change to playlist, send SIGUSR1 to parent process and branch out to all children to force a playlist refresh
/**
* AJAX functions
*
* Greyhound - real web management for Amarok
* Copyright (C) 2008 Dan Fuhry
*
* This program is Free Software; you can redistribute and/or modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for details.
*/
var ajax;
var is_playing = false, current_track = -1, current_track_length, current_track_pos, ct_advance_timeout = false, ct_counter = false, playlist_md5 = false, first_load = true;
var onload_hooks = new Array();
function addOnloadHook(func)
{
if ( typeof ( func ) == 'function' )
{
if ( typeof(onload_hooks.push) == 'function' )
{
onload_hooks.push(func);
}
else
{
onload_hooks[onload_hooks.length] = func;
}
}
}
function runOnloadHooks(e)
{
var _errorTrapper = 0;
for ( var _oLc = 0; _oLc < onload_hooks.length; _oLc++ )
{
_errorTrapper++;
if ( _errorTrapper >= 1000 )
break;
var _f = onload_hooks[_oLc];
if ( typeof(_f) == 'function' )
{
_f(e);
}
}
}
function ajaxGet(uri, f)
{
if ( ajax_panicked )
return false;
if (window.XMLHttpRequest)
{
ajax = new XMLHttpRequest();
}
else
{
if (window.ActiveXObject) {
ajax = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
alert('AmaroK client-side runtime error: No AJAX support, unable to continue');
return;
}
}
ajax.onreadystatechange = f;
ajax.open('GET', uri, true);
ajax.setRequestHeader( "If-Modified-Since", "Sat, 1 Jan 2000 00:00:00 GMT" );
ajax.send(null);
}
function ajaxPost(uri, parms, f)
{
if ( ajax_panicked )
return false;
if (window.XMLHttpRequest)
{
ajax = new XMLHttpRequest();
}
else
{
if (window.ActiveXObject)
{
ajax = new ActiveXObject("Microsoft.XMLHTTP");
}
else
{
alert('AmaroK client-side runtime error: No AJAX support, unable to continue');
return;
}
}
ajax.onreadystatechange = f;
ajax.open('POST', uri, true);
ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
// Setting Content-length in Safari triggers a warning
if ( !is_Safari )
{
ajax.setRequestHeader("Content-length", parms.length);
}
ajax.setRequestHeader("Connection", "close");
ajax.onerror = function()
{
ajax_panic();
}
ajax.send(parms);
}
function setAjaxLoading()
{
$('ajax_status').object.src = img_ajax;
$('ajax_status').object.style.display = 'block';
}
function unsetAjaxLoading()
{
$('ajax_status').object.src = 'about:blank';
$('ajax_status').object.style.display = 'none';
}
var refresh_playlist = function()
{
setAjaxLoading();
ajaxGet('/action.json/refresh', function()
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
unsetAjaxLoading();
var response = (' ' + ajax.responseText).substr(1);
// quickie JSON parser :)
response = eval('(' + response + ')');
// has the playlist been modified?
if ( playlist_md5 )
{
if ( response.playlist_hash != playlist_md5 )
{
// playlist has changed, reload
window.location.reload();
return false;
}
}
playlist_md5 = response.playlist_hash;
// update track number
if ( response.current_track != current_track )
{
var ot_id = 'track_' + current_track;
var nt_id = 'track_' + response.current_track;
current_track = response.current_track;
if ( $(ot_id).hasClass('current') )
{
$(ot_id).rmClass('current');
}
if ( ! $(nt_id).hasClass('current') )
{
$(nt_id).addClass('current');
}
pulsar_reset();
}
// update playing status
is_playing = response.is_playing;
if ( allow_control )
{
var img = $('btn_playpause').object.getElementsByTagName('img')[0];
if ( is_playing )
{
img.src = img_pause;
}
else
{
img.src = img_play;
}
}
// update volume
if ( response.volume != current_volume )
{
set_volume_fill(response.volume);
current_volume = response.volume;
}
// auto-refresh on track advance
if ( ct_advance_timeout )
{
clearTimeout(ct_advance_timeout);
}
// countdown/up timer
var time_remaining = response.current_track_length - response.current_track_pos;
current_track_length = response.current_track_length;
current_track_pos = response.current_track_pos;
if ( ct_counter )
clearInterval(ct_counter);
update_clock();
// set page title
updateTitle(response.current_track_artist, response.current_track_album, response.current_track_title);
// if not playing, set the position slider to zero
if ( !is_playing && !response.is_paused )
{
posslide_set_position(0);
}
// set advance timer
if ( is_playing && time_remaining > 0 )
{
ct_advance_timeout = setTimeout(refresh_playlist, ( 1000 * time_remaining ));
ct_counter = setInterval(update_clock, 1000);
}
if ( first_load )
{
first_load = false;
jump_current_track();
}
}
else if ( ajax.readyState == 4 && ajax.status != 200 )
{
ajax_panic();
console.debug(ajax);
}
});
}
var ajax_panicked = false;
function ajax_panic()
{
// set error flag
ajax_panicked = true;
// scroll to the top
window.scroll(0, 0);
// stop events
pulsar_reset();
window.clearInterval(pl_refresh_id);
if ( ct_counter )
window.clearInterval(ct_counter);
if ( pulsar_interval_id )
window.clearInterval(pulsar_interval_id);
// show error message
var floater = document.createElement('div');
floater.style.backgroundColor = '#ffffff';
floater.style.opacity = 0.7;
floater.style.filter = 'alpha(opacity=70)';
floater.style.textAlign = 'center';
floater.style.paddingTop = '120px';
floater.style.position = 'fixed';
floater.style.zIndex = '999';
floater.style.width = '100%';
floater.style.height = '100%';
floater.style.top = '0px';
floater.style.left = '0px';
floater.style.color = '#000000';
floater.innerHTML = 'There was a problem with a refresh request to the server. Please reload the page.';
var body = document.getElementsByTagName('body')[0];
body.appendChild(floater);
}
function player_action(action)
{
var act2 = action;
setAjaxLoading();
ajaxGet('/action.json/' + action, function()
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
unsetAjaxLoading();
refresh_playlist();
}
});
}
function jump_to_song(tid)
{
setAjaxLoading();
if ( tid == current_track )
return false;
if ( !allow_control )
return false;
ajaxGet('/action.json/jump/' + tid, function()
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
unsetAjaxLoading();
var response = (' ' + ajax.responseText).substr(1);
// quickie JSON parser :)
response = eval('(' + response + ')');
// update track number
var ot_id = 'track_' + current_track;
var nt_id = 'track_' + tid;
current_track = tid;
if ( $(ot_id).hasClass('current') )
{
$(ot_id).rmClass('current');
}
if ( ! $(nt_id).hasClass('current') )
{
$(nt_id).addClass('current');
}
// update pulsar
pulsar_reset();
// update playing status
var img = $('btn_playpause').object.getElementsByTagName('img')[0];
is_playing = true;
img.src = img_play;
// auto-refresh on track advance
if ( ct_advance_timeout )
{
clearTimeout(ct_advance_timeout);
}
if ( ct_counter )
clearInterval(ct_counter);
var time_remaining = response.current_track_length - response.current_track_pos;
current_track_length = response.current_track_length;
current_track_pos = response.current_track_pos;
if ( is_playing )
{
ct_advance_timeout = setTimeout(refresh_playlist, ( 1000 * time_remaining ));
update_clock();
ct_counter = setInterval(update_clock, 1000);
}
updateTitle(response.current_track_artist, response.current_track_album, response.current_track_title);
}
});
}
function set_playback_position(pos)
{
pos = Math.round(( pos / 100 ) * current_track_length);
setAjaxLoading();
if ( !allow_control )
return false;
ajaxGet('/action.json/seek/' + pos, function()
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
unsetAjaxLoading();
current_track_pos = pos;
update_clock();
}
});
}
function update_clock()
{
posslide_set_position((100 * (current_track_pos / current_track_length)));
var str = secs_to_string(current_track_pos) + '/' + secs_to_string(current_track_length);
$('playmeter').object.innerHTML = str;
current_track_pos++;
}
function secs_to_string(time)
{
var count_seconds = time % 60;
var count_minutes = ( time - count_seconds ) / 60;
if ( isNaN(count_seconds) )
count_seconds = 0;
if ( isNaN(count_minutes) )
count_minutes = 0;
return fill_zeroes(count_minutes) + ':' + fill_zeroes(count_seconds);
}
function fill_zeroes(str, len)
{
if ( !len )
len = 2;
if ( typeof(str) == 'number' && str == 0 )
str = '0';
str = String(str);
while ( str.length < len )
{
str = '0' + str;
}
return str;
}
var pl_refresh_id = setInterval(refresh_playlist, 10000);
window.onload = function(e)
{
runOnloadHooks(e);
}
addOnloadHook(refresh_playlist);
// scroll to the current track
function jump_current_track()
{
var top = $('track_' + current_track).Top() - 138;
window.scroll(0, top);
if ( typeof(fix_scroll) == 'function' )
{
fix_scroll();
}
}
// pulse the current track
var pulsar_current = 0, pulsar_tdlist = [], pulsar_direction = 1;
var pulsar_reset = function()
{
// remove any pulsar classes from items that aren't "current"
var boobylist = document.getElementsByTagName('tr');
for ( var i = 0; i < boobylist.length; i++ )
{
var booby = boobylist[i];
var match = booby.className.match(/(^| )(pulsar[0-9])( |$)/);
if ( match && !$(booby).hasClass('current') )
{
$(booby).rmClass(match[2]);
}
}
// recalculate list of rows that should pulse
var tdlist_new = document.getElementsByClassName('current', 'tr');
if ( pulsar_current == 0 && tdlist_new == pulsar_tdlist )
{
return true;
}
// reset everything to 0
pulsar_tdlist = tdlist_new;
pulsar_current = 0;
pulsar_direction = 1;
for ( var i = 0; i < pulsar_tdlist.length; i++ )
{
var td = pulsar_reset[i];
for ( var i = 1; i < 10; i++ )
{
if ( $(td).hasClass('pulsar' + i) )
{
$(td).rmClass('pulsar' + i);
}
}
if ( ! $(td).hasClass('pulsar0') )
{
$(td).addClass('pulsar0');
}
}
}
var pulsar_advance = function()
{
// this should be as optimized as possible, it should use a precalculated
// list of elements to pulse and whatnot... heck even right now it's not
// really as optimized as it should be due to the logic, but a lot of it's
// kinda more or less necessary.
if ( !is_playing )
return true;
if ( pulsar_current + pulsar_direction == 10 )
{
pulsar_direction = -1;
}
else if ( pulsar_current + pulsar_direction == -1 )
{
pulsar_direction = 1;
}
var nc = pulsar_current + pulsar_direction;
for ( var i = 0; i < pulsar_tdlist.length; i++ )
{
$(pulsar_tdlist[i]).rmClass('pulsar' + pulsar_current).addClass('pulsar' + nc);
}
pulsar_current = nc;
}
addOnloadHook(pulsar_reset);
var pulsar_interval_id = setInterval(pulsar_advance, 50);
function updateTitle(artist, album, track)
{
var sep = '';
var str = '';
if ( track )
{
str += sep + track;
sep = ' - ';
}
if ( artist )
{
str += sep + artist;
sep = ' - ';
}
if ( album )
{
str += sep + album;
sep = ' - ';
}
str += sep + 'AmaroK Playlist';
document.title = str;
}