Prv8 Shell
Server : Apache/2.2.22 (Unix) mod_ssl/2.2.22 OpenSSL/1.0.0-fips mod_auth_passthrough/2.1 mod_bwlimited/1.4
System : Linux server.jackjohnson.com 2.6.32-279.5.2.el6.x86_64 #1 SMP Fri Aug 24 01:07:11 UTC 2012 x86_64
User : jackjohn ( 502)
PHP Version : 5.3.17
Disable Function : NONE
Directory :  /home/jackjohn/www/style/javascript/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/jackjohn/www/style/javascript/DhtmlFieldLists.js
// ----------------------------------------------------------------------------
// Dynamic Lists - Javascript Library
// Copyright (C) 2003-2004, All Rights Reserved
// ----------------------------------------------------------------------------
// This program is protected by local and international copyright laws. Any
// use of this program is subject to the the terms of the license agreement
// included as part of this distribution archive. Any other uses are stictly
// prohibited without the written permission of the Vendor and all other
// rights are reserved.
// ----------------------------------------------------------------------------


/* ---------------------------------------------------------------------- *\
  Function    : dlist_init
  Description : Loads listdata, clears "rows" list and inserts one row for
                each hidden field in listdata (that doesn't have _erased
                set), then reset visibility and colors.
  Usage       : dlist_init(listname, createRowHTML_function)
\* ---------------------------------------------------------------------- */

function dlist_init(listname, createRowHTML) {

  // error checking
  var isErrors = dlist_errorChecking(listname, createRowHTML);
  if (isErrors) { return; }

  // set vars
  var listdata      = _listdata_loadAll(listname);
  var rowsContainer = document.getElementById(listname +':rows');

  // remove existing list rows
  removeChildNodes( rowsContainer );

  // add rowHTML to dynamic list
  _dlist_appendRowHTML(listname, listdata.rows, createRowHTML)

  _dlist_setNodeVisibility(listname);
  _dlist_setRowColors(listname);
}

/* ---------------------------------------------------------------------- *\
  Function    : dlist_errorChecking
  Description : Check for required fields and information before creating
                dynamic list
  Usage       : dlist_errorChecking(listname, createRowHTML_function)
\* ---------------------------------------------------------------------- */

function dlist_errorChecking(listname, createRowHTML) {

  // basic error checking
  if (!listname) { error("no listname specified!"); }
  if (typeof(createRowHTML) != 'function') { error("createRowHTML must be a function!"); };

  var errorMsg = '';

  // check elements exist
  var reqElementIdSuffixes = ['listdata','rows','max_rows_reached','empty_list','add_row','tmpHTML'];
  for (var n=0; n < reqElementIdSuffixes.length; n++) {
    var suffixName = reqElementIdSuffixes[n];
    var fullname   = listname +':'+ suffixName;
    var el = document.getElementById(fullname);
    if (!el) {
      errorMsg += "required element not defined: "+ fullname + "\n";
    }
  }

  // check config field defined
  var reqElementNameSuffixes = ['maximum_rows_allowed','highest_record_number','retain_erased_rows','background_classnames'];
  for (n=0; n < reqElementNameSuffixes.length; n++) {
    suffixName = reqElementNameSuffixes[n];
    fullname   = listname +':'+ suffixName;
    var matches = document.getElementsByName(fullname);
    if (matches.length == 0) {
      errorMsg += "required element not defined: "+ fullname + "\n";
    }
    if (matches.length > 1) {
      errorMsg += "Too many elements with same name: "+ fullname + "\n";
    }
  }

  //
  // Check createRowHTML output
  //

  var fields     = { '_listname': listname };
  var rowHTML    = createRowHTML( fields );
  var rowNodes   = __dlist_createRowNodes(listname, rowHTML);
  var firstTR    = rowNodes[0];

  // check form elements created by createRowHTML have onchange/onclick event handlers
  var formFields = getFormFieldNames( firstTR );
  for (var fieldName in formFields) {
    var fieldElement = formFields[ fieldName ];
    var needsOnClickHandler = 0;
    var needsOnChangeHandler = 0;

    // figure out which elements need which handlers
    if      (fieldElement.type == 'hidden') { continue; }
    else if (fieldElement.type == 'radio' ||
             fieldElement.type == 'checkbox') { needsOnClickHandler = 1; }
    else if (fieldElement.type == 'text' ||
             fieldElement.type == 'password' ||
             fieldElement.type == 'textarea' ||
             fieldElement.type == 'select' ||
             fieldElement.type == 'select-one') { needsOnChangeHandler = 1; }
    else { errorMsg += "Unrecognized element type '" +fieldElement.type+ "'"; }

    // check handler is defined
    var fieldNameParts         = fieldName.split(":");
    var fieldNameWithoutPrefix = fieldNameParts[ fieldNameParts.length - 1 ];
    if (needsOnClickHandler && fieldElement.onclick == undefined) {
      errorMsg += "The '" +fieldNameWithoutPrefix+ "' field in the createRowHTML function doesn't have a onclick handler defined!\n";
    }
    if (needsOnChangeHandler && fieldElement.onchange == undefined) {
      errorMsg += "The '" +fieldNameWithoutPrefix+ "' field in the createRowHTML function doesn't have a onchange handler defined!\n";
    }
  }

  // check row created by createRowHTML has id
  if (firstTR.id == '') {
    errorMsg += "The table row TR tag in the createRowHTML function doesn't have an id value!";
  }

  // display error message(s)
  if (errorMsg) {
    alert(errorMsg);
    return true;
  }
  else {
    return false;
  }

}

/* ---------------------------------------------------------------------- *\
  Function    : dlist_addNewRow
  Description : add a new row to the dynamic list.
                formFieldValues is optional
\* ---------------------------------------------------------------------- */

function dlist_addNewRow(listname, createRowHTML, formFieldValues) {
  var rowNode        = document.getElementById(listname +':add_row');

  // get form field values for row
  if (typeof(formFieldValues) == 'undefined') {
    formFieldValues = new Object();

    var formFieldNames = getFormFieldNames(rowNode);
    for (var longFieldname in formFieldNames) {
      var shortFieldname = longFieldname.replace(listname+':add:', '');

      if (longFieldname == '') {
        error("rowNode '" +listname+ ":add_row' contains a field with a blank name!\n\n" + rowNode.innerHTML);
      }

      formFieldValues[ shortFieldname ] = getFormFieldValueByName( longFieldname );
    }
  }

  var rowfields = _listdata_addRow(listname, formFieldValues);

  // add row HTML to dynamic list table
  _dlist_appendRowHTML(listname, [ rowfields ], createRowHTML)

  // reset visibility and colors
  _dlist_setNodeVisibility(listname);
  _dlist_setRowColors(listname);


}

/* ---------------------------------------------------------------------- *\
  Function    : dlist_removeRow
  Description : Remove a row from the list and mark it as _erased in the
                listdata.
\* ---------------------------------------------------------------------- */

function dlist_removeRow(listname, rownum) {

  _listdata_eraseRow(listname, rownum);

  // remove HTML Row from list
  var rowNode = document.getElementById(listname +':'+ rownum);
  rowNode.parentNode.removeChild( rowNode );

  _dlist_setNodeVisibility(listname);
  _dlist_setRowColors(listname);

}

/* ---------------------------------------------------------------------- *\
  Function    : dlist_moveRow
  Description : Move a specified row up or down in list, then reset
                visibility and colors. Note, when a row is moved off the
                top of the list it gets added to the bottom and all the
                other rows move up one (and vice-versa when it's moved off
                the bottom).
\* ---------------------------------------------------------------------- */

function dlist_moveRow(listname, rownum, direction) {
  // error checking
  if (direction != 'up' && direction != 'down') { error("direction must be 'up' or 'down'"); }

  // define vars
  var rowsContainer = document.getElementById(listname +':rows');

  // switch _order values in listdata (and save)
  _listdata_moveRow(listname, rownum, direction);

  // backup form elements state
  var elementStates = _backupFormElementStates(rowsContainer);

  //
  // switch row order in table
  //

  var thisRow = document.getElementById(listname +':'+ rownum);

  if (direction == 'up') {
    if (thisRow.previousSibling) {
      swapNode(thisRow.previousSibling, thisRow);
    }
    else { // move to bottom of list if no row above
      var parentNode = thisRow.parentNode;
      parentNode.removeChild(thisRow);
      parentNode.appendChild(thisRow)
    }
  }

  if (direction == 'down') {
    if (thisRow.nextSibling) { swapNode(thisRow.nextSibling, thisRow); }
    else { // move to top of list if no row below thisRow
      parentNode = thisRow.parentNode;
      parentNode.removeChild(thisRow);
      parentNode.insertBefore(thisRow, parentNode.firstChild);
    }
  }

  // restore form state
  _restoreFormElementStates(elementStates);

  _dlist_setNodeVisibility(listname);
  _dlist_setRowColors(listname);

  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : dlist_saveRowOnChange
  Description : update row after it's form elements have been modified.
  Usage       : call with onclick="" for radios and checkboxes
                and onchange="" for all other elements
\* ---------------------------------------------------------------------- */

function dlist_saveRowOnChange(listname, rownum) {
  var rowNode    = document.getElementById(listname +':'+ rownum);
  var formfields = getFormFieldNames(rowNode);
  var rowfields = new Object();

  // get form field values for row
  for (var longFieldname in formfields) {
    var shortFieldname = longFieldname.replace(listname+':'+rownum+':', '');
    rowfields[ shortFieldname ] = getFormFieldValueByName( longFieldname );
  }

  // save row data
  _listdata_saveRow(listname, rownum, rowfields);

}

/* ---------------------------------------------------------------------- *\
  Function    : dlist_debugger
  Description : display a textarea with the realtime contents of the
                datalist node.
  Usage       : <script> dlist_debugger('listname'); </script>
\* ---------------------------------------------------------------------- */

function dlist_debugger(listname) {

  var debugfield = document.getElementById(listname + ':debugger');
  var listdata = document.getElementById(listname +':listdata');
  if (!listdata) { error("Can't start debug viewer without: " +listname+ ':listdata'); }

  if (!debugfield) {
    document.write('<p><b>listdata Contents</b><br>');
    document.write('<textarea id="' +listname+ ':debugger" cols=120 rows=25 style="width: 100%"></textarea></p>');
    debugfield = document.getElementById(listname + ':debugger');

    // note: setInterval is picky in safari, but this works
    window.setInterval( 'dlist_debugger("'+listname+'")', 500); // refresh ourselves
  }

  var listdataHTML = listdata.innerHTML;
  listdataHTML = listdataHTML.replace(/>/g, ">\n");
  listdataHTML = listdataHTML.replace(/\n\s+/g, "\n");
  listdataHTML = listdataHTML.replace(/&amp;/g, "&");

  listdataHTML = listdataHTML.replace(/&/g, "&\n");

  debugfield.value = listdataHTML;
  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : _dlist_appendRowHTML
  Description : Append one or more rows to the HTML list.  Note, this does
                NOT add anything to listdata.  It just updates the HTML.
\* ---------------------------------------------------------------------- */

function _dlist_appendRowHTML(listname, rowDataArray, createRowHTML) {

  // create new rows
  var rowsHTML = '';
  for (n=0; n < rowDataArray.length; n++) {
    var rowFields = rowDataArray[n];
    if (parseInt(rowFields._erased)) { continue; }
    rowsHTML += createRowHTML( rowFields );
  }

  // create row nodes
  var rowNodes = __dlist_createRowNodes(listname, rowsHTML);

  // copy rows over to rowsContainer
  var rowsContainer = document.getElementById(listname +':rows');
  for (var n=0; n < rowNodes.length; n++) {
    var thisRow = rowNodes[n];
    rowsContainer.appendChild( thisRow );
  }

}

/* ---------------------------------------------------------------------- *\
  Function    : _dlist_setNodeVisibility
  Description : Toggle the visibility on the following elements as
                appropriate: max_records_reached, empty_list, and add_row.
\* ---------------------------------------------------------------------- */

function _dlist_setNodeVisibility(listname) {
  var listdata = _listdata_loadAll(listname);

  var visibleRowCount = _dlist_getVisibleRowCount(listdata);

  //
  // set display attribute
  //

  var VISIBLE = '';     // set to blank for default visibility setting
  var NONE    = 'none';

  // rows
  var listrowss = document.getElementById(listname +':rows');
  listrowss.style.display = VISIBLE;

  // empty list
  var empty_list = document.getElementById(listname +':empty_list');
  if (visibleRowCount == 0) { empty_list.style.display = VISIBLE; }
  else                      { empty_list.style.display = NONE; }

  // max records & add row
  var max_records = document.getElementById(listname +':max_rows_reached');
  var add_row     = document.getElementById(listname +':add_row');

  if (listdata.config.maximum_rows_allowed <= visibleRowCount) {
    max_records.style.display = VISIBLE;
    add_row.style.display = NONE;
  } else {
    max_records.style.display = NONE;
    add_row.style.display = VISIBLE;
  }

}

/* ---------------------------------------------------------------------- *\
  Function    : _dlist_getVisibleRowCount
  Description :
  Usage       : visibleRowCount = _dlist_getVisibleRowCount(listdata);
\* ---------------------------------------------------------------------- */

function _dlist_getVisibleRowCount(listdata) {

  // get not-erased row count
  var visibleRowCount = 0;
  for (var n=0; n < listdata.rows.length; n++) {
    var thisRow = listdata.rows[n];
    if (parseInt(thisRow._erased)) { continue; }
    visibleRowCount++;
  }

  return visibleRowCount;

}


/* ---------------------------------------------------------------------- *\
  Function    : _dlist_setRowColors
  Description : Reset the background colors on all the rows on the ":rows"
                tbody cell.
\* ---------------------------------------------------------------------- */

function _dlist_setRowColors(listname) {

  var listdata = _listdata_loadAll(listname);
  var rowsContainer = document.getElementById(listname +':rows');

  if (listdata.config.background_classnames.length == 0) { return; } // 0 colors
  if (!isArray(listdata.config.background_classnames)) {
    listdata.config.background_classnames = [ listdata.config.background_classnames ];
  }

  // Set row colors on dynamic list rows
  var parentNodeStack = [ rowsContainer ];
  var tableRows = new Array();
  var counter = 0;
  for (var x=0; x < parentNodeStack.length; x++) {
    var thisParent = parentNodeStack[x];
    var bgclass_idx, bgclass;

    // set background color for row's cells
    if (thisParent.nodeName == 'TR') {
      bgclass_idx = counter % listdata.config.background_classnames.length;
      bgclass     = listdata.config.background_classnames[ bgclass_idx ];
      counter++;
    }

    for (var y=0; y < thisParent.childNodes.length; y++) {
      var thisNode = thisParent.childNodes[y];
      if (thisNode.nodeName == 'TABLE') { parentNodeStack.push(thisNode); } // add to stack
      if (thisNode.nodeName == 'TBODY') { parentNodeStack.push(thisNode); } // add to stack
      if (thisNode.nodeName == 'TR')    { parentNodeStack.push(thisNode); } // add to stack

      // set class for all child TD's in TR row
      if (thisNode.nodeName == 'TD' && bgclass) {
        thisNode.className = bgclass;
      }
    }
  }

}

/* ---------------------------------------------------------------------- *\
  Function    : _dlist_redrawRow
  Description : regenerate a row with (presumably) updated listdata, then
                reset visibility and colors.
\* ---------------------------------------------------------------------- */

function _dlist_redrawRow(listname, rownum, createRowHTML) {
  var rowfields = _listdata_loadRow(listname, rownum);
  var oldRow    = document.getElementById(listname +':'+ rownum);


  // create new row
  var newRowHTML = createRowHTML(rowfields);
  var newRowNodes   = __dlist_createRowNodes(listname, newRowHTML);
  var newRow     = newRowNodes[0];

  // swap it with old
  swapNode(oldRow, newRow);

  _dlist_setNodeVisibility(listname);
  _dlist_setRowColors(listname);
}

/* ---------------------------------------------------------------------- *\
  Function    : __dlist_createRowNodes
  Description : Takes some rowHTML "<tr>...</tr><tr>...</tr>", inserts it
                into an invisible table, and returns an array of row nodes.
  Usage       : rowNodes = __dlist_createRowNodes(listname, rowsHTML);
\* ---------------------------------------------------------------------- */

function __dlist_createRowNodes(listname, rowsHTML) {

  // insert row HTML into temporary table to generate DOM elements
  var tmpTable = document.getElementById(listname + ':tmpHTML');
  tmpTable.innerHTML = '<table border=1 style="display: none">' + rowsHTML + '</table>';

  // Get a list of all rows in tmpTable (non-recursive version)
  var parentNodeStack = [ tmpTable ];
  var rowNodes = new Array();
  for (var x=0; x < parentNodeStack.length; x++) {
    var thisParent = parentNodeStack[x];

    for (var y=0; y < thisParent.childNodes.length; y++) {
      var thisNode = thisParent.childNodes[y];
      if (thisNode.nodeName == 'TABLE') { parentNodeStack.push(thisNode); } // add to stack
      if (thisNode.nodeName == 'TBODY') { parentNodeStack.push(thisNode); } // add to stack

      if (thisNode.nodeName == 'TR')    { rowNodes.push(thisNode); }
    }
  }

  // erase child nodes from DOM (so multiple IDs with the same name don't conflict)
  removeChildNodes(tmpTable);

  // return array of row nodes
  return rowNodes;
}


/* ---------------------------------------------------------------------- *\
  Function    : _listdata_loadAll
  Description : Return the listdata data structure. Structure:

  var listdata = {
    config: {
      maximum_rows_allowed: X,
      highest_record_number: X,
      retain_erased_rows: X,
      background_classnames: X
    }
    rows: [
      '... querystring encoded list of rows fields ...',
      '... querystring encoded list of rows fields ...',
      '... querystring encoded list of rows fields ...'
    ]
  };

\* ---------------------------------------------------------------------- */

function _listdata_loadAll(listname) {

  var listdataNode = document.getElementById(listname+':listdata');
  var listdata = {
    config: {},
    rows:   []
  };

  // load config data
  var formfields = getFormFieldNames(listdataNode);
  for (var fieldname in formfields) {
    var thisField     = formfields[fieldname];
    var fieldvalue    = thisField.value;
    var isConfigField = thisField.name && (thisField.name.lastIndexOf(listname+':') > -1);
    var configField   = fieldname.replace(listname+':', '');

    if (isConfigField) {
      var isCommaSeperatedList = (fieldvalue.lastIndexOf(',') > -1);
      if (isCommaSeperatedList) {
        var valuesCSV   = fieldvalue.replace(/\s*,\s*/g, ',');
        var valuesArray = valuesCSV.split(',');
        listdata.config[ configField ] = valuesArray;
      }
      else {
        listdata.config[ configField ] = fieldvalue;
      }
    }
  }

  // error checking
  if (isNaN(listdata.config.highest_record_number)) { error(listname+":highest_record_number must be a number not '" +listdata.config.highest_record_number+ "'"); }

  // load rows
  var rowsArray = document.getElementsByName(listname);
  for (var n=0; n < rowsArray.length; n++) {
    var thisRow   = rowsArray[n];
    var rowfields = query2obj(thisRow.value);
    rowfields['_listname'] = listname;
    listdata.rows.push( rowfields );
  }

  // sort array by "_order" field
  listdata.rows.sort( __listdata_sortRows );

  // return listdata object
  return listdata;
}


/* ---------------------------------------------------------------------- *\
  Function    : _listdata_saveAll
  Description : Re-generates all the hidden fields in the listdataNode to
                represent the current state of the listdata.
\* ---------------------------------------------------------------------- */

function _listdata_saveAll(listname, listdataOriginal) {

  // clone obj because we modify values below and referenced object is used in calling function
  var listdataCopy = cloneObject(listdataOriginal);

  var listdataNode = document.getElementById(listname + ":listdata");
  if (listdataNode == null) { error("Can't find: " +listname+ ':listdata'); }

  // Remove old contents of listdata
  removeChildNodes( listdataNode );

  // Append Config Fields
  for (var shortFieldname in listdataCopy.config) {
    var fieldvalue    = listdataCopy.config[ shortFieldname ];
    var longFieldname = listname +':'+ shortFieldname;
    var newTag        = __listdata_createHiddenElement(longFieldname, fieldvalue)

    listdataNode.appendChild(newTag)
  }

  // Append List Row Fields
  for (var n=0; n < listdataCopy.rows.length; n++) {
    delete listdataCopy.rows[n]['_listname']; // don't save dynamic field (set by _listdata_loadAll)
    var rowQueryString = obj2query( listdataCopy.rows[n] );
    newTag             = __listdata_createHiddenElement(listname, rowQueryString)

    listdataNode.appendChild(newTag)
  }

  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : _listdata_loadRow
\* ---------------------------------------------------------------------- */

function _listdata_loadRow(listname, rownum) {
  var listdata = _listdata_loadAll(listname);

  // find matching row
  var matchingRow;
  for (var n=0; n < listdata.rows.length; n++) {
    var thisRow = listdata.rows[n];
    if (thisRow._num == rownum) { matchingRow = thisRow; }
  }

  if (!matchingRow) { error("unable to find row " + rownum); }

  return matchingRow;

}

/* ---------------------------------------------------------------------- *\
  Function    : _listdata_saveRow
  Description : Updates field values of a specified row.  Note, fields
                that are not specified will maintain their original value
\* ---------------------------------------------------------------------- */

function _listdata_saveRow(listname, rownum, newValues) {
  var listdata = _listdata_loadAll(listname);

  // find matching row
  var matchingRow;
  for (var n=0; n < listdata.rows.length; n++) {
    var thisRow = listdata.rows[n];
    if (thisRow._num == rownum) { matchingRow = thisRow; }
  }
  if (!matchingRow) { error("unable to find row " + rownum); }

  // update row values
  for (var fieldname in newValues) {
    matchingRow[fieldname] = newValues[fieldname];
  }

  // save listdata
  _listdata_saveAll(listname, listdata);

  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : _listdata_addRow
  Description : Add a row to the listdata and a hash of it's field & values
\* ---------------------------------------------------------------------- */

function _listdata_addRow(listname, newRowFields) {

  var listdata = _listdata_loadAll(listname);

  // get new row number
  var highestRowNumber = listdata.config.highest_record_number;
  var newRowNumber = parseInt(highestRowNumber)+1;
  listdata.config.highest_record_number = newRowNumber;

  // get new order number
  var highestOrderNumber = 0;
  for (var n=0; n < listdata.rows.length; n++) {
    var thisRow = listdata.rows[n];
    if (thisRow._order > parseInt(highestOrderNumber)) { highestOrderNumber = thisRow._order; }
  }
  var newOrderNumber = Math.max( parseInt(highestOrderNumber)+1, newRowNumber);

  // create new row object
  var newRow = {
    _num:       newRowNumber,
    _order:     newOrderNumber,
    _erased:    0,
    _listname:  listname
  };
  for (var rowField in newRowFields) {
    newRow[ rowField ] = newRowFields[ rowField ];
  }

  // add to listdata
  listdata.rows.push(newRow);

  // save to HTML
  _listdata_saveAll(listname, listdata);

  // return row reference
  return newRow;

}

/* ---------------------------------------------------------------------- *\
  Function    : _listdata_eraseRow
  Description : Mark a row as erased by setting _erased flag
\* ---------------------------------------------------------------------- */

function _listdata_eraseRow(listname, rownum) {

  var listdataObj = _listdata_loadAll(listname);

  // flag erased rows
  var rowFound = 0;
  for (var n=0; n < listdataObj.rows.length; n++) {
    var thisRow = listdataObj.rows[n];
    if (thisRow._num == rownum) {
      thisRow._erased = 1;
      thisRow._order = 0;
      rowFound = 1;
    }
  }
  if (!rowFound) { error("_listdata_eraseRow: Couldn't find record #" + rownum); }

  // erase flagged rows
  var retain_erased_rows = parseInt(listdataObj.config.retain_erased_rows);
  if (!retain_erased_rows) {

    for (var idx=0; idx < listdataObj.rows.length; null) {
      thisRow = listdataObj.rows[idx];

      if (parseInt(thisRow._erased)) {
        listdataObj.rows.splice(idx, 1);  // remove row
        continue; // next element is now at same index
      }

      idx++;  // check next element
    }
  }

  // save to listdata HTML
  _listdata_saveAll(listname, listdataObj);

  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : _listdata_moveRow
  Description : move a row up or down, adjusting other row values as
                required.  The top and bottom of lists wrap/connect.
\* ---------------------------------------------------------------------- */

function _listdata_moveRow(listname, rownum, direction) {
  var listdata = _listdata_loadAll(listname);
  var rowArray = listdata.rows;

  // split rowArray into hidden and visible arrays
  var hiddenRows = [];
  var visibleRows = [];
  var sourceIndex;

  for (var n=0; n < rowArray.length; n++) {
    var thisRow  = rowArray[n];
    var isHidden = parseInt(thisRow._erased);

    if (isHidden) {
      hiddenRows.push( thisRow );
      thisRow._order = 0;
    }
    else {
      visibleRows.push( thisRow );
      if (thisRow._num == rownum) { sourceIndex = visibleRows.length - 1; }
    }
  }
  if (sourceIndex == null) { error("Unable to find row number " + rownum); }
  if (direction != 'down' && direction != 'up') { error("Invalid direction '"+direction+"', must be 'up' or 'down'"); }


  // define vars
  var sourceIsFirstRow = (sourceIndex == 0);
  var sourceIsLastRow  = (sourceIndex == (visibleRows.length-1));
  var sourceValue      = visibleRows[sourceIndex];

  // re-order rows
  if (direction == 'up' && !sourceIsFirstRow) {   // row above us
      var targetIndex = sourceIndex - 1;
      visibleRows[sourceIndex] = visibleRows[targetIndex];
      visibleRows[targetIndex] = sourceValue;
  }
  else if (direction == 'down' && !sourceIsLastRow) {   // row below us
      targetIndex = sourceIndex + 1;
      visibleRows[sourceIndex] = visibleRows[targetIndex];
      visibleRows[targetIndex] = sourceValue;
  }
  else if (direction == 'up' && sourceIsFirstRow) {   // none above us
    visibleRows.push(visibleRows.shift());                  // first becomes last
  }
  else if (direction == 'down' && sourceIsLastRow) {     // none below us
    visibleRows.unshift(visibleRows.pop());                  // last becomes first
  }

  // reset all order values
  for (var idx=0; idx < visibleRows.length; idx++) {
    thisRow = visibleRows[idx];
    thisRow._order = idx + 1;
  }

  // merge arrays into rowArray
  rowArray = hiddenRows.concat(visibleRows);

  // save to listdata HTML
  _listdata_saveAll(listname, listdata);

  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : __listdata_sortRows
  Description : sort rows by _order field

  Usage       : listdata.rows.sort( __listdata_sortRows );
\* ---------------------------------------------------------------------- */

function __listdata_sortRows(obj1, obj2) {

  var order1 = parseInt(obj1._order) || 0;
  var order2 = parseInt(obj2._order) || 0;

  if      (order1 < order2)  { return -1; }
  else if (order1 == order2) { return 0; }
  else if (order1 > order2)  { return 1; }

  return true;
}

/* ---------------------------------------------------------------------- *\
  Function    : __listdata_createHiddenElement
  Description : Create a new hidden input field element and return a
                reference to it.
\* ---------------------------------------------------------------------- */

function __listdata_createHiddenElement(elName, elValue) {

  var browserEngine = whatBrowserEngine();

  if (browserEngine == 'msie') {  // setAttribute('name') broken in IE6
    var newTag = document.createElement('<input type="hidden" name="' +elName+ '" id="' +elName+ '">');
    newTag.setAttribute('value', elValue);
  }
  else {
    newTag = document.createElement('input');
    newTag.setAttribute('type', 'hidden');
    newTag.setAttribute('name', elName);
    newTag.setAttribute('id', elName);
    newTag.setAttribute('value', elValue);
  }

  return newTag;
}



/* ---------------------------------------------------------------------- *\
  Function    : *formElementStates
  Description : Get or set the form element states (value, checked, and
                selected specifically) for all the form elements below
                the current node.

                This function is to workaround a common browser bug that
                resets form element states when a node is moved or manipulated.
                IE 6 resets checkboxes/radios to pageload values on move.

  Usage       : var elementStates = backupFormElementStates(parentNode);
                restoreFormElementStates(elementStates);
\* ---------------------------------------------------------------------- */

function _backupFormElementStates(parentNode) {

  // get element lists
  var inputs    = parentNode.getElementsByTagName('input');     // hidden, checkbox, radio, text, password
  var selects   = parentNode.getElementsByTagName('select');    // select-one, select-multi
  var textareas = parentNode.getElementsByTagName('textarea');  // textareas

  // merge element lists
  var elements  = new Array();
  for (var n=0; n < inputs.length; n++) { elements.push( inputs[n] ); }
  for (n=0; n < selects.length; n++) { elements.push( selects[n] ); }
  for (n=0; n < textareas.length; n++) { elements.push( textareas[n] ); }

  // create array to save element states in
  var elementStates = new Array();

  // save value for each element
  for (n=0; n < elements.length; n++) {
    var thisElement = elements[n];
    var backupState = new Object();
    var eType = thisElement.type;

    backupState.node  = thisElement;
    backupState.type  = thisElement.type;   // for debug use only

    // hidden, textarea, password, and text fields
    if (eType == 'hidden' || eType == 'textarea' || eType == 'password' || eType == 'text') {
      backupState.value = thisElement.value;
    }

    // checkboxes & radios
    else if (eType == 'checkbox' || eType == 'radio' ) {
      backupState.checked = thisElement.checked;
    }

    // selects
    else if (eType == 'select-one' || eType == 'select-multiple') {
      backupState.selectedIndexes = "";
      for (var idx=0; idx < thisElement.options.length; idx++) {
        var thisOption = thisElement.options[idx];
        if (thisOption.selected) {
          if (backupState.selectedIndexes.length != 0) {
            backupState.selectedIndexes += ",";
          }
          backupState.selectedIndexes += idx
        }
      }
    }

    // skip these elements
    else if (eType == 'button' || eType == 'submit' || eType == 'reset' || eType == 'image') { continue;  }

    // unknown elements
    else { error("Unknown element type: " + eType);  }

    // add this elements state to list
    elementStates.push( backupState );
  }

  return elementStates;

}

// ----------------------------------------------------------------------------

function _restoreFormElementStates(elementStates) {

  for (var n=0; n < elementStates.length; n++) {
    var thisElement = elementStates[n].node;
    var backupState = elementStates[n];
    var eType       = thisElement.type;

    // hidden, textarea, password, and text fields
    if (eType == 'hidden' || eType == 'textarea' || eType == 'password' || eType == 'text') {
      thisElement.value = backupState.value;
    }

    // checkboxes & radios
    else if (eType == 'checkbox' || eType == 'radio' ) {
      thisElement.checked = backupState.checked;
    }

    // selects
    else if (eType == 'select-one' || eType == 'select-multiple') {

      // create lookup table for selected Indexes
      var wasSelectedIndex = new Object();
      var indexArray = backupState.selectedIndexes.split(",");
      for (var i=0; i < indexArray.length; i++) {
        var indexNumber = indexArray[i];
        wasSelectedIndex[ indexNumber ] = true;
      }

      // mark options as selected or not
      for (var idx=0; idx < thisElement.options.length; idx++) {
        var thisOption = thisElement.options[idx];
        var selectedBoolValue = (typeof wasSelectedIndex[idx] == 'undefined') ? false : true;
        thisOption.selected = selectedBoolValue;
      }
    }

    // skip these elements
    else if (eType == 'button' || eType == 'submit' || eType == 'reset' || eType == 'image') { continue;  }

    // unknown elements
    else { error("Unknown element type: " + eType);  }
  }

  return true;
}

// ----------------------------------------------------------------------------

haha - 2025