
// Place your application-specific JavaScript functions and classes here
// This file is automatically included by javascript_include_tag :defaults


var FULL_CIRCLE = 2 * Math.PI

var pointing_at_column = 500
var pointing_at_row    = -1
var focus_on_column    = -1
var focus_on_row       = -1

var main_nav_icons
var main_nav_icon   = new Array()
var centre_of_icon  = new Array()
var middle_of_icon  = new Array()
var opacity_of_icon = new Array()
var changing_icons = 0 // the number of icons in the last animation cycle that changed, i.e. were providing feedback on the location of the pointer


var Cookie = {
  set: function(name, value, daysToExpire) {
    var expire = '';
    if (daysToExpire != undefined) {
      var d = new Date();
      d.setTime(d.getTime() + (86400000 * parseFloat(daysToExpire)));
      expire = '; expires=' + d.toGMTString();
    }
    return (document.cookie = escape(name) + '=' + escape(value || '') + expire + ';path=/');
  },
  get: function(name) {
    var cookie = document.cookie.match(new RegExp('(^|;)\\s*' + escape(name) + '=([^;\\s]*)'));
    return (cookie ? unescape(cookie[2]) : null);
  },
  erase: function(name) {
    var cookie = Cookie.get(name) || true;
    Cookie.set(name, '', -1);
    return cookie;
  },
  accept: function() {
    if (typeof navigator.cookieEnabled == 'boolean') {
      return navigator.cookieEnabled;
    }
    Cookie.set('_test', '1');
    return (Cookie.erase('_test') === '1');
  }
};


Object.extend( Date.prototype,
  {
    // provides a representation of the date in the form: dd/MM/YYYY
    representation: function() {
      var month = 1 + this.getMonth()
      if ( month <= 9) {
        month = '0' + month
      }
      var day = this.getDate()
      if ( day <= 9) {
        day = '0' + day
      }
      return day + '/' + month + '/' + this.getFullYear()
    }
  }
)


function page_init()
{
  // set up page keyboard shortcuts
  shortcut.add("ctrl+space", function() { toggle_shortcut_overlay('shortcut_name'); } );
  shortcut.add("ctrl+k",     function() { toggle_shortcut_overlay('shortcut_customer'); } );
  shortcut.add("ctrl+e",     function() { toggle_shortcut_overlay('shortcut_invoice'); } );
  shortcut.add("alt+p",      function() { toggle_shortcut_overlay('shortcut_app'); } );
  // unused ctrl shortcuts in FireFox: q,m,k,e,space
  shortcut.add("escape",     function() { toggle_shortcut_overlay('shortcut_name'); } );
  shortcut.add("s",          function() { focus_general_search(); }, {'disable_in_input':true} );
  // fire up the animator
  var frame_rate = 30
  new PeriodicalExecuter(animate, 1 / frame_rate )

  // pay attention to the location of the pointer
  Event.observe( window.document, 'mousemove', pointer_moved)
  Event.observe( window.document, 'click', pointer_clicked)

  // look ahead for the icons in the main navigation so the animator does not spend time walking the DOM
  var icon_array = $('main_navigation').getElementsByTagName('img')
  main_nav_icons = icon_array.length
  for ( i = 0;  i < icon_array.length;  i ++)
  {
    icon = icon_array[i]
    main_nav_icon[i] = icon
    icon_location = Position.cumulativeOffset( icon)
    centre_of_icon[i] = icon_location[0] + Element.getWidth( icon) / 2
    middle_of_icon[i] = icon_location[1] + Element.getHeight( icon) / 2

    opacity_of_icon[i] = 0.0
  }

  // show the tab that was last shown when this view was last seen
  show_last_viewed_tab()
}


function customer_page_has_loaded()
{
  Event.observe( window.document, 'click', pointer_clicked)
}


var times_alerted = 0

function alert_at_most( times, message)
{
  if ( times_alerted < times)
  {
    alert( message)
    ++ times_alerted
  }
}


function indicate_busy()
{
  $('spinner').style.display = ''
}


function indicate_ready()
{
  $('spinner').style.display = 'none'
}


function pointer_moved( event)
{
  pointing_at_column = event.clientX
  pointing_at_row    = event.clientY
}


var focus_element
var blur_function

function pointer_clicked( event)
{
  if ( focus_element && blur_function)
  {
    if ( ! Position.within( focus_element, Event.pointerX(event), Event.pointerY(event)))
    {
      blur_function( focus_element)
    }
  }
}


// remember when the animate function was last invoked so it can work out how
// much time has passed since it was last invoked
//
var when_last_animated = new Date().getTime()

function animate()
{
  // work out how long it has been since this method was last invoked
  var now = new Date().getTime()
  var time_passed = (now - when_last_animated) / 1000
  when_last_animated = now

  var focus_is_lagging = focus_on_column != pointing_at_column  ||
                         focus_on_row    != pointing_at_row

  var should_animate = focus_is_lagging  ||  0 < changing_icons

  if ( should_animate)
  {
    // move the focus sluggishly closer to the pointer
    if ( focus_is_lagging)
    {
      var delta = pointing_at_column - focus_on_column
      var move = delta - delta * Math.pow( 0.015, time_passed)
      focus_on_column += move
      delta = pointing_at_row - focus_on_row
      move = delta - delta * Math.pow( 0.015, time_passed)
      focus_on_row += move
    }

    changing_icons = 0
    for ( i = 0;  i < main_nav_icons;  ++ i )
    {
      var icon = main_nav_icon[i]
      var opacity

      // work out the distance in pixels from the focus column
      var distance = Math.abs( centre_of_icon[i] - focus_on_column) + Math.abs( middle_of_icon[i] - focus_on_row)

      // decide how opaque the icon should appear
      opacity = 1.0 - distance / 100

      // ensure that no icon is less opaque than the minimum opacity
      opacity = Math.max( opacity, 0.1)

      if ( opacity != opacity_of_icon[i])
      {
        ++ changing_icons

        // remember how wide each button is rather than asking the document each time
        opacity_of_icon[i] = opacity

        Element.setOpacity( icon, opacity)
      }
    }
  }

/*
  var period = 4000
  var alpha = new Date().getTime() % period / period
*/

}

function focus_general_search() {
  if (!(ta = document.getElementById('g_search1'))) {
    ta = document.getElementById('g_search');
  }
  ta.focus();
  new Effect.Highlight(ta.id, {duration: 1.0});
  ta.select();
}

function toggle_shortcut_overlay(element_to_focus)
{
  f = $("shortcut_pane");

  if(f.style.display == 'block') {
    new Effect.BlindUp('shortcut_content', {duration: 0.5});

    hide_shortcut_pane = function()
    {
      f.style.display = 'none';
    }

    window.setTimeout(hide_shortcut_pane, 550);
  }
  else
  {
    f.style.display = 'block';

    new Effect.BlindDown('shortcut_content', {duration: 0.5});

    window.setTimeout("$('" + element_to_focus + "').focus()", 550);
  }
}


function ancestorNode( node, type_of_tag)
{
  if ( node.nodeName == type_of_tag) {
    return node
  }
  else if ( node.parentNode != null) {
    return ancestorNode( node.parentNode, type_of_tag)
  }
  return null
}


var running_fades = new Array();

function toggle_visibility(id, effect)
{
  if(!get_running_state(id))
  {
    var f = document.getElementById(id).style;

    if(f.display == 'none')
    {
      if ( ! effect) {
        f.opacity = '0';
        f.display = '';
      }
    }

    if ( 'roll' == effect) {
      f.display == 'none' ? (new Effect.BlindDown(id, {duration: 0.5, scaleContent: false, scaleX: true})) : (new Effect.Puff(id, {duration: 0.3}));
    }
    else {
      f.opacity == '0' ? (new Effect.Fade(id, {duration: 0.5, from:0.0, to:1.0})) : (new Effect.Fade(id, {duration: 0.5, from:1.0, to:0.0}));
    }

    change_running_state(id);

    window.setTimeout(change_running_state, 1100, id);
  }
}

function get_running_state(key)
{
  if(running_fades[key] == null)
    running_fades[key] = false;

  return running_fades[key];
}

function change_running_state(key)
{
  running_fades[key] = !running_fades[key];
}

function toggle_contents(id, a, b)
{
  var f = document.getElementById(id);
  f.innerHTML == a ? f.innerHTML = b : f.innerHTML = a;
}

function enable_textarea(id)
{
  document.getElementById(id).disabled=false;
}

function change_background(id, colour)
{
  $(id).style.background = colour;
}


// the element ID of the tab that is currently shown, or null if no tab has been
// selected since the document was loaded
var now_showing_tab
// set this to a unique value for every page that wishes to independently record which tab was last shown
var page_id = ''

// provides the name of the cookie that remembers the focus tab for the open page
function focus_tab_cookie()
{
  return page_id + '.focus_tab'
}

function show_tab( element_id)
{
  if ( element_id != now_showing_tab)
  {
    if ( now_showing_tab)
    {
      Effect.Puff( now_showing_tab, {duration: 0.5})
    }
    Effect.Appear( element_id, {duration: 0.5})
    now_showing_tab = element_id
    Cookie.set( focus_tab_cookie(), now_showing_tab, 1)
  }
}


// invoke this once the page loads and the tab the user last viewed will be shown
function show_last_viewed_tab()
{
  last_viewed_tab_id = Cookie.get( focus_tab_cookie())

  if ( last_viewed_tab_id)
  {
    show_tab( last_viewed_tab_id)
  }
}


function unlock_form( form_id )
{
  var f = document.getElementById(form_id);

  for(i = 0; i < f.elements.length; i++)
    f.elements[i].disabled = false;
}


// ---------------------------------------------------------------- date chooser

DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
NAME_OF_MONTH = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 
                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']


// provides the index of the first position in "list" that contains "item"
//
function position_of_item_on_list( item, list)
{
  for ( i = 0;  i < list.length;  ++ i)
  {
    if ( list[i] == item)
    {
      return i
    }
  }
  return -1
}


// determines if the specified year is a leap year or not
//
function is_leap_year( year)
{
  // if year is divisible by 400 then it IS a leap year
  // otherwise if the year is divisible by 100 then it is NOT a leap year
  // otherwise if the year is divisible by 4 then it IS a leap year
  // otherwise the year is NOT a leap year
  return ((0 == year % 4) && (year % 100 != 0)) || (0 == year % 400)
}


// provides the number of days in a month specified by the year in which the
// month falls and the position of the month within that year
// year  - a four digit year, ex. 2008
// month - the position of a month within the year, indexed from zero
//
function days_in_month( year, month)
{
  var days = DAYS_IN_MONTH[month]
  FEBRUARY = 1
  if ( FEBRUARY == month  &&  is_leap_year( year)) {
    ++ days
  }
  return days
}


// provides the day of the week on which the given date falls, where 0 indicates
// Monday, 1 indicates Tuesday and 6 indicates Sunday
//
function weekday_of( date)
{
  var weekday = date.getDay()
  return (0 < weekday) ? (weekday - 1) : 6
}


// provides the date in the text field with the given element ID or today's date
// if the text field is empty
//
function chosen_date( chooser_element_id)
{
  var text_field = window.document.getElementById( chooser_element_id)
  var parts = text_field.value.split('/')
  var chosen_date = new Date()
  if ( 3 == parts.length)
  {
    chosen_date.setTime( 0)
    chosen_date.setFullYear( parts[2])
    chosen_date.setMonth( parts[1] - 1)
    chosen_date.setDate( parts[0])
  }
  return chosen_date
}


// updates the calendar display
// only a 28-day February where the 1st of February is a Monday will fit in four
// rows of cells.  since this case is so uncommon, it is not catered for and the
// display will show seven days from the month after when the case arises
//
function show_month( chooser_element_id, day)
{
  var _chosen_date = chosen_date( chooser_element_id)
  var focus_month = day.getMonth()

  var focus_display = window.document.getElementById( chooser_element_id + '_focus')
  focus_display.firstChild.nodeValue = NAME_OF_MONTH[day.getMonth()] + ' ' + day.getFullYear()

  // work out the day of the week on which the first day of the focus month falls
  day.setDate( 1)
  var first_falls_on = weekday_of( day)

  // the first row will show N days from the month before, where N is the
  // weekday (zero-based) of the first day of the focus month
  day.setDate( day.getDate() - first_falls_on)

  // for each cell:
  for ( cell_id = 0;  cell_id < 7 * 5;  ++ cell_id)
  {
    var cell = window.document.getElementById( chooser_element_id + '_cell_' + cell_id)
    var link = cell.firstChild

    // make the link show the day of the month
    link.firstChild.nodeValue = day.getDate()

    // make the link action change contents of the hidden field
    link.href = "javascript:chose_date('" + chooser_element_id + "', '" + day.representation() + "')"
    var styles = []
    if ( 4 < weekday_of(day)) {
      styles.push( 'weekend')
    }
    if ( day.getMonth() == focus_month) {
      styles.push( 'focus_month')
    }
    if ( day.getTime() == _chosen_date.getTime()) {
      styles.push( 'chosen_date')
    }
    link.className = styles.join(' ')

    // advance the date one day forward
    day.setDate( day.getDate() + 1)
  }
}


// provides the year and month currently shown on screen
//
function focus_month( chooser_element_id)
{
  var focus_display = window.document.getElementById( chooser_element_id + '_focus')
  var parts = focus_display.firstChild.nodeValue.split(' ')
  var focus_month = new Date()
  if ( 2 == parts.length) {
    var month = position_of_item_on_list( parts[0], NAME_OF_MONTH)
    var year = parts[1]
    focus_month.setTime( 0)
    focus_month.setFullYear( year)
    focus_month.setMonth( month)
    focus_month.setDate( 1)
  }
  return focus_month
}


// advances the given date one month ahead
//
function goto_next_month( date)
{
  date.setMonth( date.getMonth() + 1)
}


// regresses the given date to the month before
//
function goto_previous_month( date)
{
  date.setMonth( date.getMonth() - 1)
}


// invoked when the user clicks on the date field
//
function date_field_clicked( chooser_element_id)
{
  var calendar_id = chooser_element_id + '_calendar'
  var calendar = $( calendar_id)
  var calendar_is_hiding = 'none' == calendar.style.display

  if ( calendar_is_hiding) {

    show_chosen_month( chooser_element_id)
    var date_field = $( chooser_element_id)
    var field_location = Position.positionedOffset( date_field)
    var field_size = Element.getDimensions( date_field)
    calendar_size = Element.getDimensions( calendar)
    var x = field_location[0] + (field_size.width - calendar_size.width) / 2
    var y = field_location[1] + (field_size.height - calendar_size.height) / 2
    $( calendar_id).style.left = x + 'px'
    $( calendar_id).style.top = y + 'px'
    Effect.Appear( calendar_id, {duration: 0.3})
    focus_element = $( calendar_id)
    blur_function = hide_element
  }
  else {
    Effect.SwitchOff( calendar_id, {duration: 0.8})
  }
}


// focuses on the month of the chosen date and updates the display
//
function show_chosen_month( chooser_element_id)
{
  var day = chosen_date( chooser_element_id)
  show_month( chooser_element_id, day)
}


// shifts the focus one month forwards and updates the display
//
function show_next_month( chooser_element_id)
{
  var m = focus_month( chooser_element_id)
  goto_next_month( m)
  show_month( chooser_element_id, m)
}


// shifts the focus to the month before and updates the display
//
function show_month_before( chooser_element_id)
{
  var m = focus_month( chooser_element_id)
  goto_previous_month( m)
  show_month( chooser_element_id, m)
}


// invoked when the user chooses a date from the calendar
//
function chose_date( chooser_element_id, chosen_date)
{
  var text_field = window.document.getElementById( chooser_element_id)
  text_field.value = chosen_date
  var m = focus_month( chooser_element_id)
  show_month( chooser_element_id, m)
  Effect.Puff( chooser_element_id + '_calendar', {duration: 0.5})
}


function hide_element( element)
{
  element.style.display = 'none'
}

// -------------------------------------------------------------- to get labels

function getLabelForId(id) {
 var label, labels = document.getElementsByTagName('label');
 for (var i = 0; (label = labels[i]); i++) {
   if (label.htmlFor == id) {
     return label;
   }
 }
 return false;
} 

// ---------------------------------------- check/uncheck check boxes of a class

function checkCheckBoxesWithClass(c) {
  checks = document.getElementsByClassName(c);
  for (var i = 0; i < checks.length; i++) {
    checks[i].checked = true;
  }
}

function uncheckCheckBoxesWithClass(c) {
  checks = document.getElementsByClassName(c);
  for (var i = 0; i < checks.length; i++) {
    checks[i].checked = false;
  }
}


// -------------------------------------------------------------- 80 column rule
