scripts/ajax.js
author Dan
Tue, 23 Sep 2008 23:26:18 -0400
changeset 50 1b4288399b1f
parent 39 38dbcda3cf20
child 78 08f8a72b1f7b
permissions -rw-r--r--
Added graphical configuration, at this point only for the grey theme but others will follow soon. (This has been nearly done for two weeks or more but was on hold due to the bugs with multithreading)

/**
 * 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() - 164;
  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;
}