/** \file
This file contains javascript functions that are loaded as an external javascript file for every Outreach page.
The reference to the file is included in the html in make_page.php

This file includes javascript functions for:
  \li javascript debugging (dump())
  \li trimming a string
  \li preventing resubmission of a form
  \li highlighting form fields in objectfinder when they have non-default values
  \li resetting an objectfinder form fields to default values
  \li checking and highlighting rows in an objectfinder
  \li hiding and showing block and inline elements based on element ID
  \li providing the timer functionality for recording time in notes
  \li adding checkboxes dynamically (eg. the category membership interface)
  \li resetting the quickfind search controls
  \li highlighting the selected actions in actions menu
  \li highlighting notes in a to-do list
  \li multiple functions for a colour picker
*/

///Debugging function. Display the contents of the 'variable' object in an alert box.
function dump( variable ){
  result = '';
  for (member in variable) {
    result += member+':'+variable.member+', ';
  }
  alert( result );
}

///Trim any whitespace from the start and end of 'str'.
function trim( str ) {
   if (str == null) {
      return false;
   }
   return str.replace(/(^\s+)|(\s+$)/g,"");
}

///Record whether a form as been submitted yet, don't allow a submit on multiple clicks. This method should be called with the onSubmit action of a form.
/**
Just one global switch per page because once you click a submit button once then the form will be submitted and a new page will load.
You'll be doing well to submit a second different form before the page loads from the first form submit anyway.
*/
form_submitted = false;
function preventResubmit(){
  changes_made(); // for the save prompt
  if( form_submitted == false ){
    form_submitted = true;
    return true;
  }
  return false;
}

///Highlighting of input fields for form if they don't have a default value.
/**
Originally the highlight colour was pink (it is now configurable in site settings) hence the name of the function.
Loop through each input field in 'form' pinkise as needed.
*/
function pinkise_form(form) {
  if( !form ){
    return;
  }
  for(i=0;i<form.length;i++) {
    pinkise( form[i] );
  }
}

///Highlight a single input field if is value is not the default.
/**
Use the custom tag attribute 'dfValue' and compares it to the current field value.
If they are different then the field is highlighted.
Note that with radio buttons and check boxs a span around the field is highlighted.
This method was used because of the unreliable way these fields were highlighted in IE and Firefox.

PINKISE_COLOUR is used as the colour, it should be set in the head of document (to allow it to be easily changed) and should be a valid css colour definition.
*/
function pinkise( field ) {
  if( field.getAttribute('dfValue')!==null ) {
    if( field.type=='radio' || field.type=='checkbox' ) {
      fieldspan = document.getElementById( field.id+'span' );
      fieldspan.style.background=(field.checked==field.getAttribute('dfValue'))?'':PINKISE_COLOUR;
      if(field.getAttribute('dfValue')=='PINKISE_ME'){
        fieldspan.style.background= PINKISE_COLOUR;
      }
    //internet explorer returns true/false for multiple attribute, firefox returns empty string/null
    } else if( field.options && ((multiple = field.getAttribute('multiple'))==true || multiple === '' )){
      dfValues = new Array;
      dfValues[field.getAttribute('dfValue')] = true;
      j = 1;
      while( dfValue = field.getAttribute( 'dfValue'+j ) ){
        dfValues[dfValue] = true;
        j++;
      }
      mpinkise = false;
      //if any of the non default values are selected then change the background, even if there are default values selected also
      for( k=0;k<field.options.length;k++ ){
        if( !dfValues[field.options[k].value] && field.options[k].selected ){
          //alert( field.options[k].value );
          //alert( field.options[k].selected );
          mpinkise = true;
          break;
        }
      }
      field.style.background=(mpinkise)?PINKISE_COLOUR:'white';
    } else {
      field.style.background=(field.value==field.getAttribute('dfValue'))?'white':PINKISE_COLOUR;
    }
  }
}

///Reset all input fields in a form to their default value (as in the dfValue attribute).
/**
Loops through all the input fields, checks the dfValue attribute and sets the field value as needed.
Also sets the hidden field element with id RESET_ORDER to true so that when the form is submitted the order of the results will be reset to the default.
*/
function rset(form) {
  for(i=0;i<form.length;i++) {
    if( form[i].getAttribute('dfValue')!==null ) {
      if( form[i].type=='radio' || form[i].type=='checkbox' ) {
        form[i].checked= (form[i].getAttribute('dfValue')=='1'?"true":null);
      } else if( form[i].options && ((multiple = form[i].getAttribute('multiple'))==true || multiple === '' )){
        dfValues = new Array;
        dfValues[form[i].getAttribute('dfValue')] = true;
        j = 1;
        while( dfValue = form[i].getAttribute( 'dfValue'+j ) ){
          dfValues[dfValue] = true;
          j++;
        }
        for( k=0;k<form[i].options.length;k++ ){
          if( dfValues[form[i].options[k].value] ){
            form[i].options[k].selected = true;
          }else{
            form[i].options[k].selected = false;
          }
        }
        //internet explore doesn't like the value being set for a multiple select field
        //and this won't if there are multiple dfvalues anyway
        //form[i].value=form[i].getAttribute('dfValue');
      } else {
        form[i].value=form[i].getAttribute('dfValue')
      }
    }
  }
  document.getElementById( 'RESET_ORDER' ).value = 'HTTP-TRUE';
}

///Changes the foreground & background colours based on the boolean 'rowon' argument.
/**
The parameters are:
  \li rowid - the element of id of the table row
  \li rowon - a boolean for whether the row background should be on or off
  \li backgroundon - the colour to highlight a row if the row is on
  \li backgroundoff - the background colour to set for the row if the row is off
*/
function blink( rowid, rowon, backgroundon, backgroundoff ) {
  if( rowon ) {
    document.getElementById( rowid ).style.background = backgroundon;
  }else{
    document.getElementById( rowid ).style.background = backgroundoff;
  }
}

function highlight_row( rowid, hover, last_elem ){
  var cols = document.getElementById( rowid ).getElementsByTagName('td');
  if( last_elem ){
    var idx = cols.length - 1; 
  }else{
    var idx = 0;
  }
  var checked = cols[idx].getElementsByTagName('input');
  if( checked[0] ){
    checked = checked[0].checked;
  }else{
    checked = false;
  }
  //checked = document.getElementById( rowid ).getElementsByTagName('td')[0].getElementsByTagName('input')[0].checked;
  //checked = document.getElementById( rowid ).firstChild.firstChild.checked;
  //checked = false;
  if( checked ) {
    document.getElementById( rowid ).className='eq_rou-selected';
  } else {
    if( hover ) {
      document.getElementById( rowid ).className='eq_rou-hover';
    } else {
      document.getElementById( rowid ).className='eq_rou';
    }
  }
}

function set_custom_control_values(klass, property) {
    //  alert( document.getElementById(property) );
  var val = document.getElementById(property).value;
  //val = property.value;
  ajaxCaller.getPlainText("/?Na=module-ajax-custom-controls&Aclass="+klass+"&Aselectfield="+property+"&Aselected="+val, show_custom_values);
}

function set_set_property_control_values(klass, property) {
  //alert(property);
  //  alert( document.getElementById(property) );
  //val = document.getElementById(property).value;
  var val = property.value;
  ajaxCaller.getPlainText("/?Na=module-ajax-custom-controls&Aclass="+klass+"&Aselectfield="+property.id+"&Aselected="+val, show_set_property_values);
}

// Merge the show_custom_values & show_set_property_values, since the core of them is the same, and it's only the select element & the element to replace that are different
function show_custom_values(text) {
    var info = eval( "(" + text + ")" );
    //alert(text);
    var type = info['type'];
    var comparisons = info['comparisons'];

    // Stack both the comparisons selector & change the final search field
    var element = document.getElementById('custom_entry_' +info['control_index']); // Need to find out which custom_entry_ it is
    var eleName = 'custom_entry_' +info['control_index'];
    var parent = element.parentNode;
    var replace = false;

    if( type == 'select' ) {
        replace = true;
        var newElement = document.createElement('select');
        for(key in info['options']) {
            var newOption = document.createElement('option');
            newOption.text = info['options'][key];
            newOption.value = key;
            try {
                newElement.add(newOption,null); // standards compliant
            }
            catch(ex) {
                newElement.add(newOption); // IE is evils
            }
        }
    } else if( type == 'boolean' ){
        // Simple yes/no for custom_entry_foo
        replace = true;
        var newElement = document.createElement('select');
        var trueOption = document.createElement('option');
        var falseOption = document.createElement('option');
        trueOption.text = 'Yes';
        trueOption.value = 1;
        falseOption.text = 'No';
        falseOption.value = 0;
        try {
            newElement.add(trueOption,null); // standards compliant
            newElement.add(falseOption,null); // standards compliant
        }
        catch(ex) {
            newElement.add(trueOption); // IE is evils
            newElement.add(falseOption); // IE is evils
        }
    } else if( type == 'date' ) {
        // Add in the date field by magic incantations
        // Consists of an input field, an image, and hooking up javascript onclick events via setting innerHTML
        replace = true;
        newElement = document.createElement('span');
        newElement.innerHTML = "<input id='"+eleName+"_field' name='"+element.name+"'><img src='/imgs/i_vcalendar_small.gif' class='icon_click' id='"+eleName+"_field_cal_img'><span id='testtest'></span>";
    } else if( type == 'input' ) { // Usually the default text entry field
        replace = true;
        newElement = document.createElement('input');
        // Need to copy other attributes
    }

    // Now the same for the comparison properties
    var newComparison = document.createElement('select');
    for(key in info['comparisons']) {
        var newOption = document.createElement('option');
        newOption.text = info['comparisons'][key];
        newOption.value = key;
        try {
            newComparison.add(newOption,null); // standards compliant
        }
        catch(ex) {
            newComparison.add(newOption); // IE is evils
        }
    }
    override_element( document.getElementById('custom_comparison_'+info['control_index']) , newComparison, 'custom_comparison_'+info['control_index']);

    if( replace ) {
        override_element(element, newElement, 'custom_entry_'+info['control_index']);
        // need to attach the event to the image after the image is in DOM
        if( type == "date" ){
          add_calendar_event( eleName, eleName + "_field_cal_img" );
        }
    }

}


function show_set_property_values(text) {

    // Based on the set of arguments passed into this function, the 'set property' input field can be changed into a dropdown, a check box, etc
    info = eval( "(" + text + ")" );
    //alert(text);
    type = info['type'];
    // Change set_property_input field based on the value of type
    var element = document.getElementById('set_property_input');
    var parent = element.parentNode;
    var replace = false;
    if( type == 'select' ) {
        replace = true;
        var newElement = document.createElement('select');
        for(key in info['options']) {
            var newOption = document.createElement('option');
            newOption.text = info['options'][key];
            newOption.value = key;
            try {
                newElement.add(newOption,null); // standards compliant
            }
            catch(ex) {
                newElement.add(newOption); // IE is evils
            }
        }
    } else if( type == 'boolean' ){
        // Simple yes/no for custom_entry_foo
        replace = true;
        var newElement = document.createElement('select');
        var trueOption = document.createElement('option');
        var falseOption = document.createElement('option');
        trueOption.text = 'Yes';
        trueOption.value = 1;
        falseOption.text = 'No';
        falseOption.value = 0;
        try {
            newElement.add(trueOption,null); // standards compliant
            newElement.add(falseOption,null); // standards compliant
        }
        catch(ex) {
            newElement.add(trueOption); // IE is evils
            newElement.add(falseOption); // IE is evils
        }

    } else if( type == 'date' ) {
        // Add in the date field by magic incantations
        // Consists of an input field, an image, and hooking up javascript onclick events via setting innerHTML
        replace = true;
        newElement = document.createElement('span');
        newElement.innerHTML = "<input id='set_property_input_field' name='"+element.name+"'><img src='/imgs/i_vcalendar_small.gif' class='icon_click' id='set_property_input_field_cal_img'><script type='text/javascript'>Calendar.setup({inputField : 'set_property_input_field', ifFormat : '%d/%m/%Y %I:%M %p', showsTime : true, button : 'set_property_input_field_cal_img', step : 1 });</script>";
    } else if( type == 'input' ) { // Usually the default text entry field
        replace = true;
        newElement = document.createElement('input');
        // Need to copy other attributes
    }

    if( replace ) {
        override_element(element, newElement, 'set_property_input');
    }
}

function override_element(element, newElement, id) {
    newElement.name = element.name;
    var cssString;
    cssString = element.style.cssText;
    if( typeof(cssString) != 'string' ) {
        cssString = element.getAttribute('style');
    }
    newElement.setAttribute('style',cssString);
    newElement.setAttribute('id',id);
    try {
        element.parentNode.replaceChild(newElement, element);
    } catch(ex) {
        alert(ex);
    }
}
///Selects or unselects all the rows in an objectfinder.
/**
Each row in an objectfinder has it's own checkbox.
If checkflag is true then select all rows (check the rows checkbox), if false then deselect each row (uncheck the checkbox).
form_name is the element id of the form.

Relies on every checkbox element having the checkbox_id id.
Technically there probably shouldn't be more than one element with an id, but this seems to work.

There shouldn't more than one or two elements before the checkboxes start.
Traverse the elements array instead of using the object id, which doesn't work with mac ie5.
Assumes that checkboxes won't be more that starting fifth in the elements array and that there won't be any other fields inbetween the row checkbox.
*/
function Check(form_name,checkflag) {
  //access the form by id as mac ie5 doesn't seem to like the form as global object
  form = document.getElementById( form_name );

  done = false;
  for( i=0;i<form.elements.length;i++ ){
    if( form.elements[i].id == 'CHECKBOX_ID' ){
      if( (checkflag && form.elements[i].checked) || (!checkflag && !form.elements[i].checked) ) {
        form.elements[i].click();
        done = true;
      }
    }else if( done || ( !done && i>5 ) ){
      break;
    }
  }
}


///An attempt at reimplementing the blink() function so that it is quicker. Currently not used anywhere.
/**
A slightly faster version of the blink() function that isn't invoked by the event handler for each checkbox
(checkbox.click() is called in the Check() and Toggle() functions which fires an event [for every checkbox]).
Relies on the fact that each row has alternate colours, that the first row is ROW_BACKGROUND_COLOUR
and that each row has a unique id with the same number as the form.elements index for the checkbox.

Row colours are set in objectfinder->show_found_rows.
The biggest improvements in speed are seen in Mozilla, not in IE.
Using getelementbyid speeds up IE, but using blink_quick doesn't seem to that much.
Blink_quick is the same speed on the mac as firing events for each checkbox.

To change to using this function the lines in the Check() and Toggle() functions that call checkbox.click() to instead call blink_quick() with the forms.elements index.
*/
function blink_quick( form, i, check_value ){
    if( !check_value ){
      row_bg = (i%2 == 1) ? ALT_ROW_BACKGROUND_COLOUR : ROW_BACKGROUND_COLOUR;
    }else{
      row_bg = ROW_HIGHLIGHT_COLOUR;
    }
    document.getElementById('eq_row'+i).style.background = row_bg;
    form.elements[i].checked = check_value;
}

///Toggles all the row (and checkbox) selection in the object finder form identified by form_name.
/**
Basically the same as Check() but inverts all of the checkbox status (rather than turning all on or all of like Check()).
*/
function Toggle(form_name) {
  //access the form by id as mac ie5 doesn't seem to like the form as global object
  form = document.getElementById( form_name );
  done = false;
  //there shouldn't more than one or two elements before the checkboxes start
  //traverse the elements array instead of using the object id, which doesn't work with mac ie5
  //assumes that checkboxes won't be more that starting fifth in the elements array
  for( i=0;i<form.elements.length;i++ ){
    if( form.elements[i].id == 'CHECKBOX_ID' ){
      form.elements[i].click();
      done = true;
    }else if( done || ( !done && i>5 ) ){
      break;
    }
  }
}

///Shows or hides the element identified by 'id'.
function sh(id,show) {
  if( show ) {
    document.getElementById( id ).style.height = 'auto';
    document.getElementById( id ).style.display = 'block';
  } else {
    document.getElementById( id ).style.height = '1px';
    document.getElementById( id ).style.display = 'none';
  }
}

///Like sh() except that it sets the element style.display property to inline not block (doesn't break the element out onto it's own line).
function sh_inline(id,show) {
  if( show ) {
    document.getElementById( id ).style.height = 'auto';
    document.getElementById( id ).style.display = 'inline';
  } else {
    document.getElementById( id ).style.height = '1px';
    document.getElementById( id ).style.display = 'none';
  }
}

///Toggle the font weight and colour for the element identified by 'id'.
/**
Doesn't acutally show or hide the captions just sets them to highlight or not.
Intended for use with the little tabs on the user quickref interface (eg. general, work, home, userdef etc.)
*/
function sh_caption(id, show){
  if(show){
    document.getElementById( id+'cap' ).style.fontWeight = 'bold';
    document.getElementById( id+'cap' ).style.color = 'red';

  } else {
    document.getElementById( id+'cap' ).style.fontWeight = 'normal';
    document.getElementById( id+'cap' ).style.color = '';
  }
}

///Takes two array, first of elements to show, second of elements to hide.
function show_hide(show,hide){
  for (i=0;i<hide.length;i++) {
    sh(hide[i],false);
  }
  for (i=0;i<show.length;i++) {
    sh(show[i],true);
  }
}
///Takes two arrays, first of captions to show (with sh_caption()) and second of captions to hide.
function show_hide_caption(show,hide){
  for (i=0;i<hide.length;i++) {
    sh_caption(hide[i],false);
  }
  for (i=0;i<show.length;i++) {
    sh_caption(show[i],true);
  }
}

///Just in case you want to show a bunch of elements (takes an array) and not hide any.
function show(show){
  show_hide(show,Array())
}

///Just in case you want to hide a bunch of elements (takes an array) and not show any.
function hide(hide){
  show_hide(Array(),hide)
}

///Toggles the element (with id), so shows if hidden and hides if showing.
/**
Depends on the sh() method setting the height to auto when showing.
*/
function toggle_sh( id ){
  if(document.getElementById( id ).style.height == 'auto' ){
    sh(id,false)
  }else{
    sh(id,true)
  }
}

///Pops up a window with the Outreach help contents for screencode.
function getHelp(screencode) {
  anewwindow=window.open('help.php?Ns=' + screencode,'Equillia_Help','toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable,width=400,height=500');
}

///Global variable with the time count in seconds so far (for this page).
seconds = 0;
//Global variable telling the counter whether it should count or not.
count = false;

///Updates the currently displayed time value for the TIMER_CLOCK element.
/**
Only works with one element (eg. TIMER_CLOCK).
Displays the current number of seconds recorded in the global variable 'seconds' as a value with hours, minutes and seconds.
*/
function tick_tock(){
  negative = (seconds < 0) ? '-' : '';
  secs = Math.abs(seconds);

  mins = Math.floor(secs/60);
  hours = Math.floor(mins/60);
  mins = mins % 60;
  secs = secs % 60;

  if(0 <= secs && secs < 10){
    secs = '0'+secs;
  }
  if(hours != 0){
    hours = hours+':';
    if(0 <= mins && mins < 10 ){
      mins = '0'+mins;
    }
  }else{
    hours = '';
  }
  clock_time = negative + hours + mins + ':' + secs;

  document.getElementById( 'TIMER_CLOCK' ).innerHTML = clock_time;
}

///Function that calls itself every second, updates the current second count and updates the displayed time count.
/**
The note timer function displays an input field (a text field, TIMER_FIELD),
a button for turning the timer on an off (an animated gif) and the current time count (displayed in hours minutes and seconds).

If the timer is on (button has been clicked) then 'count' is set to true and timer() is called.
Timer() then keeps calling itself until each second until 'count' is false (which happens if the button is clicked again).
Each second timer() has to check whether the value in the text field has changed and update the current seconds count accordingly.
It the input value hasn't changed then it is updated as soon as 'seconds' moves to the next time unit (as determined by TIMER_UNITS, which comes from site settings).
*/
function timer(){
  if(count){
    //set the timeout as the first thing so that each timeout is as close to 1 second together as possible
    window.setTimeout('timer()',1000);
    seconds++;

    newValue = Math.ceil((seconds/60)/TIMER_UNITS);
    currentValue = document.getElementById( 'TIMER_FIELD' ).value

    if( currentValue == newValue-1 ){
      document.getElementById( 'TIMER_FIELD' ).value = newValue;
    }
    else if( currentValue != newValue ){
      recalculate();
    }
    if( document.getElementById('TRACK_CHANGES' )){
      //trigger the onChange event manually, event is defined in screens/admin-users-notes
      document.getElementById( 'TRACK_CHANGES' ).value='NOTE-1';
    }
    tick_tock();
  }
}

///Take the value in the timer text input field and convert it to a number seconds based on TIMER_UNITS.
/**
Assign seconds with the new value based on the input field value, assign zero if the text value isn't a number.
Then update the displayed time value by calling tick_tock().

This function is designed to used with onChange event of the input field (TIMER_FIELD) and also from the timer() function.
*/
function recalculate(){
  value = trim( document.getElementById( 'TIMER_FIELD' ).value );
  secs_temp = Math.ceil((value * TIMER_UNITS)*60);
  //only change second if value is not a number, otherwise just leave the same
  if( !isNaN( secs_temp ) ){
    seconds = secs_temp;
  }else if( value == '' || value == RESET_UNITS_VALUE ){
    seconds = 0;
  }
  //if it is NaN make sure the we put an actual value back in the field so that we can keep counting
  document.getElementById( 'TIMER_FIELD' ).value = Math.ceil((seconds/60)/TIMER_UNITS);
  tick_tock();
}

///Update the image for timer button. With an animated gif if the timer is on and a static gif if the timer is off.
/**
Works out whether the current state of the image based on 'count'.
Also invert 'count' at the same time.
Then calls timer() which will update the time (if count is true).
*/
function switch_timer(){
  //there must be a javascript reserved work stop or something, because mac ie5 doesn't like the image being called stop, so call it stop_img
  start_img = new Image();
  start_img.src = IMG_URL + "i_timerstart.gif";
  start_img.alt = "start timer";
  stop_img = new Image();
  stop_img.src = IMG_URL + "i_timerstop.gif";
  stop_img.alt = "stop timer";

  count = !count;
  image = (count) ? stop_img : start_img;

  document.getElementById( 'TIMER_TEXT' ).src = image.src;
  document.getElementById( 'TIMER_TEXT' ).alt = image.alt;

  timer();
}

///Used to record the number of checkboxes that have been added for particular elements. A seperate count is maintained for each different element id.
/**
array.length just gives value of largest index, and indexes are oids so this doesn't work for labels.
I'm not exactly sure why a count of the number of new checkboxes has to be kept.
*/
checkbox_counts = new Array();

///Array of array for all the new checkboxes. Used to record the ids new checkboxes that have been added so that a checkbox with the same value is not added more than once.
/**
Means that more arrays of checkboxes can be added dynamically
*/
new_checkboxes = new Array();

///Add a new checkbox to the element (usually a div) with element_id. The new checkbox will have the currently selected value from select_field.
/*
Each new checkbox that is added has the following characteristics:
  \li It is checked when it is added.
  \li It is given an id of NEW_CHECKBOX+element_id+checkbox_value.
  \li Its value is taken from the currently selected value in select_field.
  \li Its label is taken from the text of the currently selected value in select_field.
  \li It is given the same name as the select field, it is assumed that the select field name with have [] (ie. so values will be submitted as an array to PHP).
  \li It is recorded in new_checkboxes and if the same value is selected again then a second checkbox is not added for the same value.

This function is intended for use in the onChange event of a select box (eg. <select onChange="add_checkbox(this,div_id_to_add_checkbox_to)">).

Firefox will recheck unchecked checkboxes in the active div if more checkboxes are added.
So we make note of the unchecked checkboxes before adding any new checkboxes and then uncheck them again at the end of the function.
Note this doesn't uncheck checkboxes that already existed in the div when the page was loaded (ie. it only effects checkboxes that weren't created by this function).
*/
function add_checkbox( select_field, element_id, special_values, add_on ){

  if( checkbox_counts[element_id] == undefined ){
    checkbox_counts[element_id] = 0;
  }
  if( new_checkboxes[element_id] == undefined ){
    new_checkboxes[element_id] = new Array();
  }

  checkbox_id_prefix = "NEW_CHECKBOX"+element_id;

  unchecked_checkboxes = new Array();

  //Record all the unchecked checkboxes.
  for ( checkbox_id in new_checkboxes[element_id] ){
    if( document.getElementById( checkbox_id_prefix+checkbox_id ) && document.getElementById( checkbox_id_prefix+checkbox_id ).checked == false ){
      unchecked_checkboxes[checkbox_id] = true;
    }
  }
  //work out the currently selected value in select_field
  for (i=0; i< select_field.options.length ; i++){
    if( select_field.options[i].selected && (select_field.options[i].value != 'HTTP-NON_ENTRY') ){
      if( select_field.options[i].value == 'NOTE_CUSTOM_EMAIL' ){
        custom_email = document.getElementById( 'NOTE_CUSTOM_EMAIL' ).style;
        custom_email.visibility = "visible";
        custom_email.height = '20px';
      }else{
        checkbox_text = select_field.options[i].text;
        checkbox_value = select_field.options[i].value;

        if( new_checkboxes[element_id][checkbox_value] == undefined ){
          new_checkboxes[element_id][checkbox_value] = true;

          //check to see if there is a prefix value defined for this select field
          if( checkbox_prefix =  select_field.getAttribute('prefixValue') ){
            checkbox_text = checkbox_prefix + checkbox_text;
          }

          checkbox_counts[element_id]++;
          checkbox_name = select_field.getAttribute('name') ;
          checkbox_html = '<input id="'+checkbox_id_prefix+checkbox_value+'" class="noborder" name="'+ checkbox_name +'" value="'+ checkbox_value +'" checked type="checkbox" />&nbsp;';
          checkbox_html += '<label for="'+checkbox_id_prefix+checkbox_value +'">'+ checkbox_text;

          // if special_value and add_on is set, add add_on to the end of this lable
          if( special_values && special_values.length > 0 && add_on ){
            if( in_array( special_values, checkbox_value ) ){
              checkbox_html += add_on;
            }
          }

          checkbox_html += '</label>';
          checkbox_html += '<br />';
          document.getElementById( element_id ).innerHTML += checkbox_html;
        }
      }
      select_field.value = 'HTTP-NON_ENTRY';
      break;
    }
  }
  //Uncheck all the previously unchecked checkboxes
  for( unchecked_id in unchecked_checkboxes ){
    document.getElementById( "NEW_CHECKBOX"+element_id+unchecked_id ).checked = false;
  }
}

///Add $label (as bold text) to the element with element_id before adding a new checkbox with add_checkbox().
function add_labelled_checkbox( select_field, element_id, label ){
  if( new_checkboxes[element_id] == undefined ){
    new_checkboxes[element_id] = new Array();
  }
  if( new_checkboxes[element_id].length == 0 ){
    document.getElementById( element_id ).innerHTML += '<b>'+label+'</b><br />';
  }
  add_checkbox( select_field, element_id );
}

///Resets the values in the name and category field of the quickfind bar.
/**
Also set a hidden input field value in the objectfinder search form so that when an objectfinder search form is submitted the name and category in the quickfind bar will be reset.
This is needed because if quickfind form is not submitted with the new empty values then the quickfind values will be loaded from the session for the new page.
The value of the RESET_QUICKFIND hidden field is checked on the backend (in make_page.php) and the current quickfind values are removed from the session if it is true.

This function is intended to be used with the onClick event of the reset button for an objectfinder search form.
*/
function reset_quickfind(){
  document.getElementById( 'QUICKFIND_NAME' ).value = '';
  if( document.getElementById( 'QUICKFIND_CATEGORY' ) ) {
    document.getElementById( 'QUICKFIND_CATEGORY' ).value = 'HTTP-EMPTY';
  }
  document.getElementById( 'RESET_QUICKFIND' ).value = 'HTTP-TRUE';
}

///Check the radio button (with id) for an objectfinder action (each action has its own button) and then highlight the selected action.
/**
This function is intended to be used in the onChange or onClick event of every action field.
Whenever an action field is changed then that action should be selected (its radio button checked) and the action should be highlighted.

Always make as true and change the background colour (even if already true), so that if the radio is already checked then the colour will change no matter what.
This is useful for Firefox which sets the radio on a page reload, if it was set before.
*/
function set_action_radio( id, backgroundon ){
  document.getElementById( id ).checked = true;
  blink_action_row( id, true, backgroundon );
}

///Highlight the currently selected objectfinder action and unhighlight all the other actions.
/**
Assumes that there will be maximum of 50 actions.
And assumes that each action row will have an id of 'action'+row_number+'row'.
*/
function blink_action_row( id, checked, backgroundon ){
  if( checked ){
    i = 50;
    while( document.getElementById( 'action'+i+'row' ) ){
      document.getElementById( 'action'+i+'row' ).style.background = '';
      i++;
    }
    document.getElementById( id+'row' ).style.background = backgroundon;
  }
}

///Function for highlighting rows on the todo list screen. Highlight a row if all properties of the note have been changed from when the page loaded.
/**
row_id is the table row to highlight.
oid is the oid of the note which the row represents (one note per row on the todo screen).
colour is the colour to highlight the row.

It no properties have changed for a note then the note will be note be saved (the hidden field with the note oid is set to HTTP-DUMMY_OBJECT).
It properties have changed then the field is set to the oid of the note so that note will be saved.

The original note properties values are taken from the dfValue attribute of the note fields.
Only the priority and resolved properties of notes can be changed on this screen so this are the only field that need to checked.

Keeps a record of the original background colours so that rows can be changed to non-highlighted if the note values are changed back to default.
This needed because the rows are alternate colour.

The backend code that sets up all the fields with the correct element ids and so on is in screens/_to-do-list.php.
*/
original_backgrounds = new Array();
new_backgrounds = new Array();
function highlight_todo( row_id, oid, colour ){
  todo_note = document.getElementById( 'todo_note'+oid );
  todo_priority = document.getElementById( 'todo_priority'+oid );
  todo_resolved = document.getElementById( 'todo_resolved'+oid );
  row = document.getElementById( row_id );

  if( ((''+todo_priority.value) != todo_priority.getAttribute('dfValue')) || (todo_resolved.checked) ){
    //when we assign colour it is converted to some sort of normal form, so compare against that normal form as well
    if( row.style.background != colour && row.style.background != new_backgrounds[oid]){
      original_backgrounds[oid] = row.style.background;
    }
    row.style.background = colour;
    new_backgrounds[oid] = row.style.background;
    todo_note.value = 'NOTE'+oid;
  }else if( original_backgrounds[oid] != undefined ){
    row.style.background = original_backgrounds[oid];
    todo_note.value = 'HTTP-DUMMY_OBJECT';
  }
}










/****************************************
  Below is functions newly added for 3.0
  Will need to tidy up JSs before this comment
*****************************************/
///Same as PHP in_array(). Set third parameter to true will also check data type
/**
  @param arr: the array to go through
  @data data:  the data that need to be find in the array
  @strick: 1 or 0, to use == or === when doing the checking
*/
function in_array( arr, data, strict ){
  for( var i in arr ){
    if( strict ){
      if( arr[i] === data ){
        return true;
      }
    }else{
      if( arr[i] == data ){
        return true;
      }
    }
  }
  return false;
}


///Return true if obj is an array
function is_array( obj ){
  return ( obj.constructor && obj.constructor.toString().indexOf( "Array" ) != -1 );
}


///Return if browser is IE, this is just a lazy function that need to be completed
function is_ie(){
  if( document.styleSheets[0].addRule ){
    return 1;
  }else{
    return 0;
  }
}


///display the html code size (innerHTML.length)
/**
  @param id: the element id that need to get size
  @param id_in: the element id that will display the size
*/
function sizeof_elem( id, id_in ){
  elem = document.getElementById( id );
  if( !elem ){
    return;
  }
  size = ( elem.innerHTML.length / 1024 );
  size = size.toFixed(2) + "KB";
  if( id_in ){
    document.getElementById( id_in ).innerHTML = size;
  }else{
    document.write( size );
  }
}

///Detect if escape is pressed by a user, need to set form_submitted to false again since the page is stopped reloading
function detect_key_press( e ){
  evt = e || window.event;
  if( evt.keyCode == 27 ){
    form_submitted = false;
  }
}

/// Remove element 'name_id' from element 'target_id'
/**
  @param target_id: the element which contains the element that need to be removed
  @param name_id: the element id that need to be removed
*/
function remove_element( target_id, name_id ){
  target = document.getElementById( target_id );
  to_del = document.getElementById( name_id );
  if( target && to_del ){
    target.removeChild( to_del );
  }
}
/// remove every child from a html element
/**
  @param elem: the element that need to be emptyed
  @param is_id: if the 'elem' is a id instead of an element
*/
function remove_all_child( elem, is_id ){
  if( is_id ){
    elem = document.getElementById( elem );
  }
  while( elem.hasChildNodes() ){
    elem.removeChild( elem.lastChild );
  }
}

/// display a div to use like a pop up window
/**
  @param htmls: the html displayed in the div
  @param show: 1 or 0 for show/hide the div
  @param top_px: the margin from top of the div to top of the screen
*/
var popup_div_show = 0;
function pop_up_div( htmls, show, top_px ){
  popup_div = document.getElementById( 'POPUP_DIV' );
  cover_div = document.getElementById( 'POPUP_COVER' );
  if( !top_px ){
    top_px = 100;
  }
  if( !popup_div_show || show ){
    // special fix for IE that always display select box on top of everything.
    if( document.styleSheets[0].addRule ){
      document.styleSheets[0].addRule( 'select', 'visibility:hidden;', 0 );
    }

    popup_div.style.top = top_px + ( document.documentElement.scrollTop || document.body.scrollTop ) + "px";
    cover_div.style.height = ( (document.body.scrollHeight < document.documentElement.scrollHeight) ?
                                document.documentElement.scrollHeight : document.body.scrollHeight) + "px";
    cover_div.style.width = ( (document.body.scrollWidth < document.documentElement.scrollWidth) ?
                               document.documentElement.scrollWidth : document.body.scrollWidth) + "px";

    if( htmls ){
      popup_div.innerHTML = htmls;
    }
    popup_div.style.zIndex = '10';
    popup_div.style.visibility = "visible";
    cover_div.style.visibility = "visible";
    popup_div_show = 1;
  }else{
    // Remove the rule in IE that hided all select box.
    if( document.styleSheets[0].removeRule ){
      document.styleSheets[0].removeRule(0);
    }
    cover_div.style.visibility = "hidden";
    popup_div.style.visibility = "hidden";
    popup_div.innerHTML = '';
    popup_div.style.zIndex = '-1';
    popup_div_show = 0;
  }
}


/********************************
 Functions and vars for object selector
 ********************************/
/// The function that will be called when user clicked a row in the selector, see note_email_callback() for a example
var object_selector_callback = null;
/// Parameters to the object selector, which will be an array of settings, see note_email_to_parms() for a list of parameters
var object_selector_parms = null;
/// The function that will determine whether a row is selected or not, see note_email_select_func() for a example
var object_selector_select_func = null;
/// The search results
var object_selector_results = Array();
/// Create a pop up div holds the object selector
/**
  @param parms_func: The function that will set the object_selector_parms
  @param call_back: The funtion that will link to object_selector_callback
  @param select_func: The function that will link to object_selector_select_func
*/
function object_selector( parms_func, call_back, select_func ){
  object_selector_callback = call_back;
  object_selector_parms = parms_func();
  object_selector_select_func = select_func;
  html = '<iframe width="100%" height="500px" frameborder="0" scrolling="no" src="/main/javascript/object_selector/object_selector.php"></iframe>';
  pop_up_div( html );
}
/// Set the object_selector_results. This function will be called after the object selector perform a search
/**
  The result will be an array, each entry of the array holds an array of the properties requested in object_selector_parms['parms'].
*/
function set_object_selector_results( result ){
  object_selector_results = result;
}


/*******************************
 Functions for note email_to that uses the object selector
 *******************************/
/// The function that will return the parameters for object selector
function note_email_to_parms(){
  var parms = Array();
  // Which object the object selector is going to search
  parms['type'] = 'user';
  // The properties the object selector will get from the query
  parms['parms'] = ['oid', 'name_processed', 'o_company', 'o_email_address'];
  // The captions for each column(property)
  parms['captions'] = ['OID', 'Name', 'Company', 'Email'];
  // 0 or 1 indicates which column should show in the result or not
  parms['shows'] = [0, 1, 1, 1];
  // The search controls that will be added to the query
  parms['cntls'] = [ ['status', '2', 'O' ], ['o_email_address', '13', ''] ];
  // The message displayed on top of the search result.
  parms['msg'] = 'Click name to add email address.';
  // The 'name' of the input field. This is only used for note_email_to, not a parameter for the object selector
  parms['select_name'] = document.getElementById( 'NOTE_EMAIL_TO' ).name;
  // The table id to add the selected user. This is only used for note_email_to, not a parameter for the object selector
  parms['tb_id'] = 'email_to_table';
  return parms;
}
/// The callback function when a row is clicked.
/**
  @param idx: The index of the row that clicked. Currently this is the only parameter object selector will pass to the callback function.
*/
function note_email_callback( idx ){
  changes_made( 1 );
  oid = object_selector_results[idx]['oid'];
  tb_id = object_selector_parms['tb_id'];
  in_name = object_selector_parms['select_name'];
  name = object_selector_results[idx]['name_processed'];
  email = object_selector_results[idx]['o_email_address'];
  if( note_selected_email_to[oid] ){
    remove_email_to( oid, tb_id, in_name + "_" + oid );
  }else{
    add_email_to_table( tb_id, in_name, oid, name, email );
  }
  return true;
}
/// The function that check whether a row is selected
/**
  @param idx: The index of the row that need to be checked.
  @return true/false indicates the row[idx] is selected or not
*/
function note_email_select_func( idx ){
  if( note_selected_email_to[object_selector_results[idx]['oid']] ){
    return true;
  }else{
    return false;
  }
}

/************************
 Functions and vars for display the email_to in notes
 ************************/
/// an array of selected contacts
var note_selected_email_to = Array();
/// Add a selected contact from a select box to the list of email_to
/*
  @param select_elm: The <select> element ( not a id of the element )
  @param tb_id: The table id that will display the selected contact
*/
function add_email_to_select( select_elm, tb_id ){
  var idx = select_elm.selectedIndex;
  var in_name = select_elm.name;
  var value = select_elm.options[idx].value;
  // empty selected
  if( value == 'HTTP-NON_ENTRY' ){
    return;
  }
  // select to email to all staff
  else if( value == 'NOTE_EMAIL_TO_ALL_STAFF' ){
    for( var i = 2; i < select_elm.length; i++ ){
      var txt = select_elm.options[i].text;
      var email = txt.substring( txt.lastIndexOf( '(' ) + 1, txt.lastIndexOf( ')' ) );
      var name = txt.substring( 0, txt.lastIndexOf( '(' ) - 1 );
      value = select_elm.options[i].value;
      if( value.indexOf( "#" ) == -1 ){
        add_email_to_table( tb_id, in_name, value, name, email );
      }
    }
  }else{
    var txt = select_elm.options[idx].text;
    var email = txt.substring( txt.lastIndexOf( '(' ) + 1, txt.lastIndexOf( ')' ) );
    var name = txt.substring( 0, txt.lastIndexOf( '(' ) - 1 );
    add_email_to_table( tb_id, in_name, value, name, email );
  }
  select_elm.selectedIndex = 0;
}
/// Add exists email to the table, used when save faild. See add_email_to_table()
function add_email_to_exists( tb_id, value, name, email ){
  in_name = document.getElementById( 'NOTE_EMAIL_TO' ).name;
  add_email_to_table( tb_id, in_name, value, name, email );
}
/// Add a contact/staff to the selected list of email_to
/**
  @param tb_id: The table id that will be inseart html code
  @param in_name:  The input field name , e.g. VNOTE30928email[]
  @param value: The value for the input, should be the oid of the contact
  @param name: The name of the contact
  @param email: The email of the contact
*/
function add_email_to_table( tb_id, in_name, value, name, email ){
  if( note_selected_email_to[value] ){
    return; // don't do anything if the contact is already added
  }
  note_selected_email_title(1);
  note_selected_email_to[value] = Array();
  note_selected_email_to[value]['name'] = name;
  note_selected_email_to[value]['email'] = email;

  tb = document.getElementById( tb_id );
  tr_id = in_name + '_' + value;
  new_tr = document.createElement( 'tr' );
  new_tr.setAttribute( 'id', tr_id );

  td1 = document.createElement( 'td' );
  td2 = document.createElement( 'td' );
  td1.setAttribute( 'width', '90%' );
  td2.setAttribute( 'width', '10%' );
  td2.setAttribute( 'align', 'right' );
  td1.innerHTML = '<strong>' + name + '</strong><br />' + email;
  td2.innerHTML = '<input type="button" value="Remove" onclick="remove_email_to( \''+ value + '\', \'' + tb_id + '\', \'' + tr_id + '\')" /><input type="hidden" value="' + value + '" name="' + in_name + '" />';
  new_tr.appendChild( td1 );
  new_tr.appendChild( td2 );
  if( tb ){
    tb.appendChild( new_tr );
  }
}
/// Remove a selected contact from the selected list
/**
  @param value: The oid of the contact
  @param tb_id: The table id that holds all the selected contacts
  @param tr_id: The <tr> id that holds this contact
*/
function remove_email_to( value, tb_id, tr_id ){
  note_selected_email_to[value] = 0;
  remove_element( tb_id, tr_id );
  note_selected_email_title();
}
/// Show/Hide the 'Selected Email to' title
function note_selected_email_title(show){
  if( !show ){
    show = 0;
    for( c in note_selected_email_to ){
      if( note_selected_email_to[c] ){
        show = 1;
        break;
      }
    }
  }
  if( show ){
    document.getElementById( 'NOTE_EMAIL_TO_SELECT_TITLE' ).style.visibility = 'visible';
  }else{
    document.getElementById( 'NOTE_EMAIL_TO_SELECT_TITLE' ).style.visibility = 'hidden';
  }
}
/*** Note email to function ends *******/


/// Counter of the upload fields
var upload_field_num = 0;
/// Add a file input field to upload note attachments
/**
  @param target_id: The element id that will be inseart the file input field
  @param name_id: The input field id that can get the 'name' attribute from
*/
function add_upload_field( target_id, name_id ){
  target = document.getElementById( target_id );
  input_field = document.getElementById( name_id );
  name = input_field.getAttribute( 'name' );
  size = input_field.getAttribute( 'size' );
  div_name = name_id + upload_field_num;
  var new_div = document.createElement( 'div' );
  new_div.setAttribute( 'id', div_name );
  new_div.setAttribute( 'style', 'white-space: nowrap' );
  new_div.innerHTML = '<input type="file" size="'+size+'" name="'+name+'" />&nbsp;';
  new_div.innerHTML += '<input type="button" value="Remove" onclick="remove_element(\'' +target_id +'\', \'' +div_name+ '\')" />';
  if( target ){
    target.appendChild(new_div);
    upload_field_num++;
  }
}

/// Changes a input fields value, mostly will be used by the hidden field tracks user's changes
function change_input_value( id, value ){
  if( id ){
    document.getElementById( id ).value = value;
    changes_made( true );
  }
}

/// save prompt. Use only one variable to remember if anything has been changed
var changes_on_page = false;
/// Indicates whether there are changes made or not
function changes_made( bool ){
  changes_on_page = false;
  if( bool ){
    changes_on_page = true;
  }
}
/// Popup the save prompt
function save_prompt(){
  if( changes_on_page ){
    form_submitted = false;
    return "You have unsaved changes, are you sure to leave current page?";
  }
}


/******************************
 Functions for member selector that used for select category/topic etc
 ******************************/
/// The member selector object, make a new member selector, need to pass the 3 arrays, and 3 divs' id.
/**
  @param myname: The name of the member selector object.
  @param input_name: The 'name' of the input field
  @param groups: An array of all the categories/topics, will be used to construct the first/second box
  @param s_groups: An array of selected categories/topics, will be used to construct the third box
  @param cats_id: The id that will used to construct the table.
  @param dir_id: The id of the element that shows the direction image *NOT USED NOW*
  @param select_div: The id of the element that shows all the selected categories

  The last two parameter is some fix for the unreasonable way of prevent save user/notes on the contact's notes page.
 */
function c_selector( myname, input_name, groups, s_groups, group_id, cats_id, dir_id, select_id, track_id, track_value ){
  this.group_div = document.getElementById( group_id );
  this.cats_div = document.getElementById( cats_id );
  this.dir_div = document.getElementById( dir_id );
  this.select_div = document.getElementById( select_id );
  this.groups = groups;
  this.s_groups = Array();
  this.myname = myname;
  this.input_name = input_name;
  this.track_id = track_id;
  this.track_value = track_value;

  /// Switch the direction image *NOT USED NOW*
  this.sh_img = function sh_img( to_left ){
    if( to_left ){
      img = 'i_to_left';
      alt = 'move to left';
    }else{
      img = 'i_to_right';
      alt = 'move to right';
    }
    this.dir_div.innerHTML = "<img src='imgs/" + img + ".gif' alt='" + alt + "' />";
  }

  /// display/refresh the first box ( category gorups )
  /**
    @param gn: The group name that is selected in the first box
  */
  this.show_grp = function show_grp( gn ){
    html = "";
    for( var g_name in this.groups ){
      gn_out = g_name.replace( /'/g, "\\'" );
      gn_out = gn_out.replace( /"/g, '&nbsp;' );
      if( gn == g_name ){
        cn = "class='now' ";
      }else{
        cn = '';
      }
      html += "<a " +cn+ " onclick=\"" + this.myname + ".switch_cat( '" + gn_out + "')\">" + g_name + "</a>";
    }
    this.group_div.innerHTML = html;
  }

  /// display/refresh the second box ( categorys )
  /**
    @param gn: The group name that is selected in the first box
  */
  this.switch_cat = function switch_cat( gn ){
    var html = "";
    gn_out = gn.replace( /'/g, "\\'" );
    gn_out = gn_out.replace( /"/g, '&nbsp;' );
    for( var cid in this.groups[gn] ){
      var name = this.groups[gn][cid].replace( gn + ">", "" );
      name = name.replace( gn + " >", "" );
      html += "<a class='click' onclick=\"" + myname + ".add_select( '" + gn_out + "', '"+ cid + "', 1 )\">" + name + "</a>";
    }
    if( html == "" ){
      html = '<a><i>Empty</i></a>';
    }
    this.show_grp( gn );
    this.cats_div.innerHTML = html;
  }

  /// remove a selected category
  /**
    @param gn: The group name of the category
    @param cid: The category oid
    @param change: Whether call made_change() or not
  */
  this.del_select = function del_select( gn, cid, changed ){
    this.select_div.removeChild( document.getElementById( this.myname + cid ) );
    if( typeof( this.groups[gn] ) != "object" ){
      this.groups[gn] = Array();
    }
    this.groups[gn][cid] = this.s_groups[gn][cid];
    delete this.s_groups[gn][cid];
    this.show_all( gn );
    if( changed ){
      this.made_change();
    }
  }

  /// refresh the third box( selected elements )
  this.show_select = function show_select(){
    html = "";
    for( var gn in this.s_groups ){
      gn_out = gn.replace( /'/g, "\\'" );
      gn_out = gn_out.replace( /"/g, '&nbsp;' );
      for( var cid in this.s_groups[gn] ){
        html += "<div id='"+ this.myname + cid + "'><a class='click' onclick=\"" + this.myname + ".del_select( '" + gn_out + "', '"+ cid + "', 1 )\">"
                + this.s_groups[gn][cid] + "</a><input type='hidden' name='" + this.input_name + "' value='" + cid + "'></div>";
      }
    }
    if( html == "" ){
      html = '<a><b><i>Empty</i></b></a><input type="hidden" name="'+ this.input_name + '" value="HTTP-NON_ENTRY">';
    }
    this.select_div.innerHTML = html;
  }

  /// add a category to selected categories
  /**
    @param gn: The group name of the category
    @param cid: The category oid
    @param change: Whether call made_change() or not
  */
  this.add_select = function add_select( gn, cid, changed ){
    gn_out = gn.replace( /'/g, "\\'" );
    gn_out = gn_out.replace( /"/g, '&nbsp;' );
    if( typeof( this.s_groups[gn] ) != "object" ){
      this.s_groups[gn] = Array();
    }
    this.s_groups[gn][cid] = this.groups[gn][cid];
    delete this.groups[gn][cid];
    this.show_all( gn );
    if( changed ){
      this.made_change();
    }
  }

  /// Tells the form that there are changes done, switch on the save prompt
  this.made_change = function made_change(){
    changes_made(1);
    change_input_value( this.track_id, this.track_value );
  }

  /// do a refresh of all three boxes
  this.show_all = function show_all( gn ){
    this.show_grp( gn );
    this.switch_cat( gn );
    this.show_select();
  }

  // Fill in the third box if there are an s_gruops passed in
  for( var g_name in s_groups ){
    for( var cid in s_groups[g_name] ){
      this.add_select( g_name, cid );
    }
  }

  //this.show_all( 'Misc' ); // don't display the default group at the beginning.
  this.show_all( '' );
}


/// show/hide a section
/**
  instead directly modify the element style, I prefer to do it by change CSS
  So this function is based on element that has a xxxx-hidden style
  @param id: The element id that need to be show/hide
  @param img: The image element that show/hide the element when use clicks
*/
function sh_sec( id, img ){
  ele = document.getElementById( id );
  c_name = ele.className;
  if( c_name.lastIndexOf( "-hidden" ) != '-1' ){
    ele.className = c_name.replace( "-hidden", "" );
    img.src = 'imgs/i_hide.gif';
  }else{
    ele.className = c_name + "-hidden";
    img.src = 'imgs/i_show.gif';
  }
}


/// show/hide a tab that constructed by hTabHolder
/**
  @param show: the id of the tab that will active
  @param all_ids: an array of all tab ids, all will be hide except the one = show
  @param callback: a function that will run on the current active id, like switch the background of the tab *NOT USED ANYMORE*
*/
function sh_tab( show_id, all_ids, callback ){
  for( i = 0; i < all_ids.length; i++ ){
    if( all_ids[i] != show_id ){
      document.getElementById( all_ids[i] + "cap" ).className = "hide";
      document.getElementById( all_ids[i] ).className = "tab-hide";
    }
  }
  document.getElementById( show_id + "cap" ).className = "show";
  document.getElementById( show_id ).className = "tab-show";
  if( callback ){
    callback( show_id );
  }
}
/// A callback of sh_tab, switch the background of the tab in note *NOT USED ANYMORE*
function note_bg( tab_id ){
  elem = document.getElementById( 'test_switch_bg' );
  switch( tab_id ){
    case 'NOTE_INPUT_tab1':
      elem.style.backgroundImage = "url( '/imgs/bg_note_attach.png' )";
      break;
    case 'NOTE_INPUT_tab2':
      elem.style.backgroundImage = "url( '/imgs/bg_note_mail.png' )";
      break;
    default:
      elem.style.backgroundImage = "url( '/imgs/bg_note_pen.png' )";
      break;
  }
}


/// Pop up a browser window
/**
  @param content: The html code goes in the <body>
  @param width: Width of the window
  @param height: Height of the window
*/
function new_window( content, width, height ){
  if( !width ){
    width = '700px';
  }
  if( !isNaN( width ) ){
    width += 'px';
  }
  if( !height ){
    height = "";
  }
  if( !isNaN( height ) ){
    height += "px";
  }
  var winname = window.open('', "_blank", 'toolbar=0,location=0,directories=0,status=yes,menubar=yes,scrollbars=1,resizable=yes,width=' + width + ',height=' + height);
  winname.document.open('text/html', 'replace');
  winname.opener = null;
  winname.document.write( "<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01//EN' 'http://www.w3.org/TR/HTML4/strict.dtd'>" );
  winname.document.write( "<html><head></head><body>" );
  winname.document.write( content );
  winname.document.write( "</body></html>" );
  winname.document.close();
}


/// Function to pop up a browser window to show the print preview
/**
  @param element_id: The id of the element that holds the html that goes in the pop up window
*/
function show_print_preview( element_id ){
  content = "<style>";
  if( document.styleSheets[0].cssText ){
    content += document.styleSheets[0].cssText;
  }else{
    // firefox dosen't have document.styleSheets[0].cssText, need to find a way to do it...
    // Should be fine now, all the html has inline styles
  }
  content += "</style>";
  content += "<div style='margin: 20px auto;'>";
  content += document.getElementById( element_id ).innerHTML;
  content += "</div>";
  content += "<script type='text/javascript'>window.print();</script>";
  new_window( content );
}


/// function groups for the note preview feature
var note_preview = {
  on: 0, // preview is on/off
  is_print: 0, // whether show in a popup div or popup window for printing
  templates: new Array(), // note template array
  settings: new Array(), // note template settings array
  replace_codes: new Array(), // note template replace codes array
  replace_regex: new Array(), // note template replacement regular expressions
  popup_div: 0, // *NOT USED*
  cover_div: 0, // *NOT USED*
  note_div: 0, // *NOT USED*
  ie_fix_added: 0, // whether the speical hack to IE has been added or not
  signature: "", // The html code for contact's signature

  /// Return the current selected template name
  get_name: function(){
    if( document.getElementById( 'NOTE_PREVIEW_TEMPLATE' ) ){
      return document.getElementById( 'NOTE_PREVIEW_TEMPLATE' ).options[document.getElementById( 'NOTE_PREVIEW_TEMPLATE' ).selectedIndex].value;
    }else{
      return "HTTP-EMPTY";
    }
  },

  /// Check whether there is an error when loading the template
  load_error: function( txt ){
    r = txt.toLowerCase().indexOf( "there was an error", 0 );
    return (r == 0);
  },

  /// the ajax call back function for show()
  show_callback: function( html ){
    if( !html || note_preview.load_error( html ) ){
      html = "Invalid template";
    }
    note_preview.show( note_preview.is_print, html );
  },

  /// the ajax call back function for fill()
  fill_callback: function( ini ){
    if( !ini || note_preview.load_error( ini ) ){
      ini = 1; // fill with something anyway, so next time don't need to get again.
    }
    if( ini ){
      note_preview.fill( ini );
    }
  },

  /// read a ini setting for the template and call replace_input to fill input fields
  fill: function( ini ){
    tmpl_name = this.get_name();

    if( ini ){
      this.settings[tmpl_name] = ini;
    }

    if( tmpl_name != "HTTP-EMPTY" && !this.settings[tmpl_name] ){
      ajaxCaller.getPlainText( "/note_templates/" + tmpl_name + ".ini?" + new Date().getTime(), this.fill_callback );
      return; // wait the fill_callback to call this function again.
    }

    this.replace_input( this.settings[tmpl_name] );
  },

  /// show the preview, or the pop up window depends on is_print
  show: function( is_print, content ){
    this.is_print = is_print;
    popup_div = document.getElementById( 'note_preview_div' );
    cover_div = document.getElementById( 'POPUP_COVER' );
    note_div = document.getElementById( 'NOTE_PREVIEW_HOLDER' );

    // set the default template to the templates array
    if( !this.templates["HTTP-EMPTY"] ){
      this.templates["HTTP-EMPTY"] = note_div.innerHTML;
    }
    if( !this.signature && document.getElementById( 'NOTE_PREVIEW_SIGNATURE' ) ){
      this.signature = document.getElementById( 'NOTE_PREVIEW_SIGNATURE' ).innerHTML;
    }

    if( !this.on ){
      // get the selected template name
      tmpl_name = this.get_name();
      // set the template if there is a content passed by ajax_callback
      if( content ){
        this.templates[tmpl_name] = content;
      }
      // get the template using ajaxCaller if it is not already stored
      if( tmpl_name != "HTTP-EMPTY" && !this.templates[tmpl_name] ){
        ajaxCaller.getPlainText( "/note_templates/" + tmpl_name + ".html?" + new Date().getTime(), this.show_callback );
        return; // wait the show_callback to call this function again.
      }

      // set the innerHTML of popup_div
      note_div.innerHTML = this.templates[tmpl_name];
      // process the replace of values
      this.replace_html();

      // show a popup div or popup window depends on whether is_print is set
      if( !this.is_print ){
        popup_div.style.top = 10 + document.documentElement.scrollTop + "px";
        cover_div.style.height = ( (document.body.scrollHeight < document.documentElement.scrollHeight) ?
                                    document.documentElement.scrollHeight : document.body.scrollHeight) + "px";
        cover_div.style.width = ( (document.body.scrollWidth < document.documentElement.scrollWidth) ?
                                   document.documentElement.scrollWidth : document.body.scrollWidth) + "px";
        popup_div.style.zIndex = '10';
        popup_div.style.visibility = "visible";
        cover_div.style.visibility = "visible";
        this.ie_fix();
        this.on = 1;
      }else{
        show_print_preview( 'NOTE_PREVIEW_HOLDER' );
      }
    }else{
      popup_div.style.visibility = "hidden";
      cover_div.style.visibility = "hidden";
      this.ie_fix();
      this.on = 0;
    }
    this.is_print = 0;
  },

  /// pop up a window to show the preview content
  print: function(){
    if( this.on ){
      show_print_preview( 'NOTE_PREVIEW_HOLDER' );
    }else{
      this.show(1);
    }
  },

  /// fill in the input fields that set in template ini file
  replace_input: function( ini ){
    if( !ini || ini == 1 ){
      return;
    }
    ini = ini.replace( /\r/g, "" );
    ini = ini.split( "\n" );
    for( line in ini ){
      line = ini[line];
      if( line ){
        key = line.substring( 0, line.indexOf("=") );
        value = this.replace_code( line.substring( line.indexOf("=") + 1 ) );
        switch( key ){
          case 'subject':
            document.getElementById( 'NOTE_SUBJECT' ).value = value;
            break;
          case 'content':
            value = value.replace( /\\r\\n/g, "\r\n" );
            if( document.getElementById( 'NEW_NOTE_CONTENT' ) ){
              document.getElementById( 'NEW_NOTE_CONTENT' ).value = value;
            }else{
              document.getElementById( 'NOTE_CONTENT' ).value = value;
            }
            break;
          case 'notetype':
            elem = document.getElementById( 'NOTE_TYPE' );
            for( i = 0; i< elem.options.length; i++ ){
              if( elem.options[i].value == value ){
                elem.selectedIndex = i;
              }
            }
            break;
          case 'reference':
            document.getElementById( 'NOTE_REF' ).value = value;
            break;
          case 'units':
            document.getElementById( 'TIMER_FIELD' ).value = value;
            break;
          case 'resolved':
            if( value != "0" ){
              document.getElementById( 'NOTE_RESOLVED' ).checked = true;
            }else{
              document.getElementById( 'NOTE_RESOLVED' ).checked = false;
            }
            break;
          case 'scheduled':
            document.getElementById( 'scheduled_date' ).value = value;
            break;
          case 'priority':
            document.getElementById( 'NOTE_PRIORITY_' + value ).checked = true;
            break;
          default:
            // nothing to do here yet
            alert( "'" + key + "' is not defined in the javascript yet..." );
            break;
        }
      }
    }
  },

  /// replace all the macro codes in txt
  replace_code: function(txt){
    for( code in this.replace_codes ){
      txt = txt.replace( this.replace_regex[code], this.replace_codes[code] );
    }
    return txt;
  },

  /// fill in all the values from input field into the preview container
  replace_html: function(){
    // set all the fiedls
    if( document.getElementById( 'NOTE_PREVIEW_SUBJECT' ) ){
      subject = document.getElementById( 'NOTE_SUBJECT' );
      document.getElementById( 'NOTE_PREVIEW_SUBJECT' ).innerHTML = subject.value;
    }

    if( document.getElementById( 'NOTE_PREVIEW_CONTENT' ) ){
      if( document.getElementById( 'NEW_NOTE_CONTENT' ) ){
        content = document.getElementById( 'NEW_NOTE_CONTENT' );
      }else{
        content = document.getElementById( 'NOTE_CONTENT' );
      }
      content = content.value;
      content = content.replace( /\n/g, "<br />" );
      document.getElementById( 'NOTE_PREVIEW_CONTENT' ).innerHTML = content;
    }

    if( document.getElementById( 'NOTE_PREVIEW_REF' ) && document.getElementById( 'NOTE_REF' ) ){
      document.getElementById( 'NOTE_PREVIEW_REF' ).innerHTML = "<i>" + document.getElementById( 'NOTE_REF' ).value + "</i>";
    }

    if( document.getElementById( 'NOTE_PREVIEW_RESOLVED' ) && document.getElementById( 'NOTE_RESOLVED' ) ){
      resolved = document.getElementById( 'NOTE_RESOLVED' ).checked ? "yes": "no";
      document.getElementById( 'NOTE_PREVIEW_RESOLVED' ).innerHTML = "<i>" + resolved + "</i>";
    }

    if( document.getElementById( 'NOTE_PREVIEW_TYPE' ) && document.getElementById( 'NOTE_TYPE' ) ){
      note_type = document.getElementById( 'NOTE_TYPE' ).options[document.getElementById( 'NOTE_TYPE' ).selectedIndex].text;
      document.getElementById( 'NOTE_PREVIEW_TYPE' ).innerHTML = "<i>" + note_type + "</i>";
    }

    if( document.getElementById( 'NOTE_PREVIEW_SCHEDULED' ) && document.getElementById( 'scheduled_date' ) ){
      document.getElementById( 'NOTE_PREVIEW_SCHEDULED' ).innerHTML = "<i>" + document.getElementById( 'scheduled_date' ).value + "</i>";
    }

    if( document.getElementById( 'NOTE_PREVIEW_SIGNATURE' ) ){
      document.getElementById( 'NOTE_PREVIEW_SIGNATURE' ).innerHTML = this.signature;
    }

    // set the email to field
    email_to = document.getElementById( 'NOTE_PREVIEW_EMAILTO' );
    email_to.innerHTML = "<div style='font-weight: bold ; margin-bottom: 10px;'>This note will be emailed to:</div>";
    has_email_to = 0;
    for( c in note_selected_email_to ){
      if( note_selected_email_to[c] ){
        has_email_to = 1;
        email_to.innerHTML += "<strong>" + note_selected_email_to[c]['name'] + "</strong>: " + note_selected_email_to[c]['email'] + "<br />";
      }
    }
    if( !has_email_to ){
      email_to.innerHTML += 'No Contact is selected';
    }
  },

  /// fix for IE that select box always on top of everything
  ie_fix: function(){
    if( !document.styleSheets[0].addRule ){
      return;
    }
    if( !this.on && !this.is_print && !this.ie_fix_added ){
      document.styleSheets[0].addRule( 'select', 'visibility:hidden;', 0 );
      this.ie_fix_added = 1;
    }else if( this.ie_fix_added ){
      document.styleSheets[0].removeRule(0);
      this.ie_fix_added = 0;
    }
  }
};


/// Change the lock icon for note private field
function set_note_private( on ){
  img_on = document.getElementById( 'NOTE_PRIVATE_on' );
  img_off = document.getElementById( 'NOTE_PRIVATE_off' );
  p_field = document.getElementById( 'NOTE_PRIVATE' );
  if( on ){
    img_off.className = "hidden";
    img_on.className = "icon_click show";
    p_field.value = "1";
  }else{
    img_on.className = "hidden";
    img_off.className = "icon_click show";
    p_field.value = "0";
  }
}


/// Strip out <script> tag in a the FCKEditor body
function FCKStripScript( html ){
  html = html.replace( /<script[^>]*?>(.|\s)*?<\/script>/gi, "" );
  html = html.replace( /<script[^>]*?>/gi, "" );
  html = html.replace( /<\/script>/gi, "" );

  return html;
}


/// Object for the image uploader
var image_uploader = {
  name: "", // image input field name
  value: "", // image input field value
  div_id: "", // the div id that holds the image preview
  width: "", // expecting width of the image
  height: "", // expecting height of the image

  /// Show a pop up div for image uploader
  /**
    @param n: The image input field name
    @param v: The image input field value
    @param id: The div id that holds the image preview
    @param w: The expected width of the image
    @param h: The expected height of the image
  */
  pop_up: function( n, v, id, w, h ){
    html = '<iframe width="100%" height="500px" frameborder="0" scrolling="no" src="/main/javascript/image_uploader/image_uploader.php"></iframe>';
    this.name = n || "";
    this.value = v || "";
    this.div_id = id || "";
    this.width = w || 0;
    this.height = h || 0;
    pop_up_div( html, 0, 50 );
  },

  /// Display the seleted image into the this.div_id element
  /**
    @param path: The full url to the image
  */
  set: function( path ){
    if( !this.name || !this.div_id ){
      this.pop_up();
      return;
    }
    if( path ){
      html_str = "<img src='" + path + "' alt='' />\r\n";
      html_str += "<input name='" + this.name + "' value='" + path + "' type='hidden' />\r\n";
    }else{
      html_str = "<strong>No Image</strong>";
      html_str += "<input name='" + this.name + "' value='' type='hidden' />\r\n";
    }
    document.getElementById( this.div_id ).innerHTML = html_str;
    this.pop_up();
  },
  
  /// return true if the image uplader has the div and field name set
  ready: function(){
    return ( this.name && this.div_id );
  }
};


/// Create a new <tr> and insert into a table, cols should be an array of the content of each column, cols_width should be the array of the width of each column
/**
  @param tb_elem: The table element that holds the <tr>
  @param cols: array of columns that in the new <tr>, each column holds the html inside the column
  @param col_widths: array of width for each of the column
  @param tr_class: the style name for the new <tr>
  @param tr_id: the id for the new <tr>
  @param col_classes: array of style names for each of the column
  @param col_ids: array of ids for each of the column 
*/
function insert_tr( tb_elem, cols, col_widths, tr_class, tr_id, col_classes, col_ids ){
  if( !tb_elem || !is_array( cols ) ){
    return;
  }
  if( !col_widths || !is_array( col_widths ) ){
    col_widths = new Array();
  }
  if( !col_classes || !is_array( col_classes ) ){
    col_classes = new Array();
  }
  if( !col_ids || !is_array( col_ids ) ){
    col_ids = new Array();
  }

  tr = document.createElement( 'tr' );
  if( tr_id ){
    tr.setAttribute( 'id', tr_id );
  }
  if( tr_class ){
    tr.className = tr_class;
  }
  for( var i = 0; i < cols.length; i++ ){
    td = document.createElement( 'td' );
    if( col_widths[i] ){
      td.setAttribute( 'width', col_widths[i] );
    }
    if( col_ids[i] ){
      td.setAttribute( 'id', col_ids[i] );
    }
    if( col_classes[i] ){
      td.className = col_classes[i];
    }
    td.innerHTML = cols[i];
    tr.appendChild( td );
  }
  tb_elem.appendChild( tr );
}


/// Fixes for the IE select box width bug
var ie_select_width = {

  /// Find out whether elem is onFocus
  focus: function( elem ){
    if( !is_ie() ){
      return;
    }
    if( elem.isOnFocus ){
      elem.isOnFocus = 0;
      elem.isOnBlur = 1;
      this.fix( elem );
    }else{
      elem.isOnFocus = 1;
      elem.isOnBlur = 0;
    }
  },

  /// Apply the width style changes if elem need to be fixed.
  fix: function( elem ){
    if( !is_ie() || !this.need_fix( elem ) ){
      return;
    }
    if( elem.style.width != 'auto' ){
      elem.style.old_width = elem.style.width;
      elem.style.width = 'auto';
      elem.fix_added = 1;
    }else{
      if( !elem.isOnFocus ){
        elem.style.width = elem.style.old_width;
        elem.fix_added = 0;
      }
    }
  },

  /// Find whether elem options is longer than its container.
  need_fix: function( elem ){
    if( elem.fix_added ){
      return 1;
    }
    var len = 0;
    var txt_len = 0;
    var elem_len = elem.clientWidth;
    for( var i = 0; i < elem.length; i++ ){
      txt_len = elem.options[i].text.length;
      if( txt_len > len ){
        len = txt_len;
      }
    }
    if( len * 6 > elem_len ){
      return 1;
    }else{
      return 0;
    }
  }
};


/// For the collection member editing screen, changing the select box into a text box if the selected value is HTTP-EMPTY
function clct_new_name( elem, def ){
  if( elem.options[elem.selectedIndex].value != "HTTP-EMPTY" ){
    return;
  }
  var input = document.createElement( 'input' );
  input.setAttribute( "name", elem.name );
  input.setAttribute( "type", "text" );
  input.setAttribute( "value", def );
  input.style.width = elem.style.width;
  input.className = "input_bd";
  elem.parentNode.replaceChild( input, elem );
}


/// For the advanced export. Only can show one such interface on the screen at the moment. Can turn this into a js object in order to show more than one.
var adv_export = {

  /// The multi-dim array of properties. Actually the one from json_encode is an object not an array...
  data: new Array(),
  /// The selected properties array
  selected: new Array(),
  /// The sections box ID
  id_sec: "USER_ADV_EXPORT_SEC",
  /// The property box ID
  id_pty: "USER_ADV_EXPORT_PTY",
  /// The selected box ID
  id_slc: "USER_ADV_EXPORT_SLC",
  /// the element for the sections
  elem_sec: 0,
  /// the element for the property
  elem_pty: 0,
  /// the element for the selection
  elem_slc: 0,
  /// current section
  cur_sec: "",

  /// show the selections
  show: function(){
    this.elem_sec = document.getElementById( this.id_sec );
    this.elem_pty = document.getElementById( this.id_pty );
    this.elem_slc = document.getElementById( this.id_slc );
    if( !this.elem_sec || !this.elem_pty || !this.elem_slc ){
      alert( "Faild to initialize the export interface." );
      return;
    }

    // select the first one by default
    for( var sec_name in this.data ){
      this.switch_sec( sec_name );
      break;
    }
  },

  /// re-populate the elements in section box
  /**
    @param sec_name: Current selected section
  */
  show_sec: function( sec_name ){
    if( !sec_name ){
      sec_name = "";
    }
    // populate the sections box
    var html = "";
    for( var name in this.data ){
      if( name == sec_name ){
        html += '<a class="now" onclick="adv_export.switch_sec( \'' + name.replace( /'/g, "\\'" ) + '\' )">' + name + '</a>';
      }else{
        html += '<a onclick="adv_export.switch_sec( \'' + name.replace( /'/g, "\\'" ) + '\' )">' + name + '</a>';
      }
    }
    this.elem_sec.innerHTML = html;
  },

  /// re-populate the elements in the property box
  /**
    @param sec_name: Current selected section
  */
  show_pty: function( sec_name ){
    if( !sec_name ){
      sec_name = this.cur_sec;
    }
    
    var data = this.data[sec_name]
    var html = "";
    for( var pty in data ){
      if( this.slc_pos( pty ) != -1 ){
        html += '<a><strong>' + data[pty] + '</strong></a>';
      }else{
        name = data[pty].replace( /'/g, "\\'" );
        html += '<a onclick="adv_export.select( \'' + pty + '\', \'' + name + '\')">' + data[pty] + '</a>';
      }
    }
    this.elem_pty.innerHTML = html;
  },

  /// re-populate the elements in selected box
  /* 
    original only show the up/down icon when mouse over the row, but found the icon is flashing since the onmouseover/out is on/off.
    will need to re-write the html content in this box if want to do the mouseover thing
  */
  show_slc: function(){
    var html = "";
    for( var i = 0; i < this.selected.length; i++ ){
      name = this.selected[i]['name'];
      name_safe = name.replace( /'/g, "\\'" );
      pty = this.selected[i]['pty'];
      id = this.id_slc + '_' + pty;
      //html += '<div id="' + id + '" onmouseover="adv_export.sh_ud(\'' + pty + '\', 1)" onmouseout="adv_export.sh_ud(\'' + pty + '\', 0)">';
      html += '<div id="' + id + '" >';
      //html += '<div id="' + id + '_ud" style="float:right;padding-right:5px;">&nbsp;</div>';
      html += '<div id="' + id + '_ud" style="float:right;padding-right:5px;">' + this.ud_link( pty ) + '</div>';
      html += '<a onclick="adv_export.del( \'' + pty + '\', \'' + name + '\' )">' + name + '</a>';
      html += '<input type="hidden" value="' + name.replace( /"/g, '\\"' ) + '" name="Aproperties[' + pty + ']" />';
      html += '</div>';
    }
    if( html == "" ){
      html = "&nbsp;";
    }
    this.elem_slc.innerHTML = html;
  },

  /// select a property then refresh the lists
  /**
    @param pty: The selected property
    @param name: The name of the property
  */
  select: function( pty, name ){
    this.add_slc( pty, name );

    // need to re-populate the properties
    this.show_pty();
    this.show_slc();
  },

  /// remove a property from selected list
  /**
    @param pty: The property that need to be removed
    @param name: The property name
  */
  del: function( pty, name ){
    /* need to re-draw the slc box for the up/down arrows
    remove_element( this.id_slc, this.id_slc + '_' + pty );
    if( !this.elem_slc.innerHTML ){
      this.elem_slc.innerHTML = "&nbsp;";
    }
    */
    this.del_slc( pty );
    this.show_pty();
    this.show_slc();
  },

  /// add a property to selected list.
  /**
    @param pty: The property
    @param name: Name of the property
  */
  add_slc: function( pty, name ){
    var idx = this.selected.length;
    this.selected[idx] = new Array();
    this.selected[idx]['pty'] = pty;
    this.selected[idx]['name'] = name;
  },

  /// remove a selected property from the selected list
  /**
    @param pty: The property
  */
  del_slc: function( pty ){
    // re-construct the whole selected array in order to have a valid js array, and easy to re-order
    var tmp = new Array();
    var j = 0;
    for( var i = 0; i < this.selected.length; i++ ){
      if( this.selected[i]['pty'] != pty ){
        tmp[j] = new Array();
        tmp[j]['pty'] = this.selected[i]['pty'];
        tmp[j]['name'] = this.selected[i]['name'];
        j++;
      }
    }
    this.selected = tmp;
  },

  /// Return the move up/down image link
  /**
    @param pty: The property of the image link regards to
  */
  ud_link: function( pty ){
    var html = "";
    var up = '<img class="click" src="/imgs/i_arrow_up.png" onclick="adv_export.move_up( \'' + pty + '\' )" />';
    var down = '<img class="click" src="/imgs/i_arrow_down.png" onclick="adv_export.move_down( \'' + pty + '\' )" />';

    if( this.selected.length < 2 ){
      return "";
    }
    switch( this.slc_pos( pty ) ){
      case 0:
        html = down;
        break;

      case ( this.selected.length - 1 ):
        html = up;
        break;

      default:
        html = up + " " + down;
        break;
    }
    return html;
  },

  /// show/hide the up down link
  /**
    @param pty: The property of the image link regards to
    @param on: on/off
  */
  sh_ud: function( pty, on ){
    var elem = document.getElementById( this.id_slc + '_' + pty + '_ud' );
    if( !elem ){
      return;
    }
    if( on ){
      elem.innerHTML = this.ud_link( pty );
    }else{
      elem.innerHTML = "&nbsp;";
    }
  },

  /// move a selected property up
  move_up: function( pty ){
    var pos = this.slc_pos( pty );
    if( pos == -1 || pos == 0){
      return;
    }

    var tmp = this.selected[pos-1];
    this.selected[pos-1] = this.selected[pos];
    this.selected[pos] = tmp;
    this.show_slc();
  },

  /// move a selected property down
  move_down: function( pty ){
    var pos = this.slc_pos( pty );
    if( pos == -1 || pos == this.selected.length - 1 ){
      return;
    }

    var tmp = this.selected[pos+1];
    this.selected[pos+1] = this.selected[pos];
    this.selected[pos] = tmp;
    this.show_slc();
  },

  /// find the position of the property in the selected array
  slc_pos: function( pty ){
    for( var i = 0; i < this.selected.length; i++ ){
      if( this.selected[i]['pty'] == pty ){
        return i;
      }
    }
    return -1;
  },

  /// switch a section.
  switch_sec: function( sec_name ){
    // re-populate the sections
    this.show_sec( sec_name );
    this.cur_sec = sec_name;
    // re-populate the properties
    this.show_pty( sec_name );
  }

};


/// Setup the calendar, added-in for the custom-control date fields, which failed to init since directly writing script to innerHTML is not working under Chrome/IE
function add_calendar_event( input_id, img_id ){
  if( Calendar && input_id && img_id ){
    Calendar.setup({inputField : input_id, ifFormat : '%d/%m/%Y %I:%M %p', showsTime : true, button : img_id, step : 1 })
  }
}

