
//  JavaScript: VolumeCalculator.js
//  Provides class object volumeCalculator
//  Version: 2.0
//  Date: 12-04-2010
//  (c) copyright 2010 ekerner at ekerner.com.au

// begin class volumeCalculator ...
var volumeCalculator = {
  // form/input fields container ...
  inputItems           : new Array(),
  interfaceColspan     : 5,
  interfaceWidthPixels : 750,
  revision             : '2.0',

  // adds or sets a volumeCalculator.inputItems ...
  setInputItem : function(category, item, volume)
  {
    if (! volumeCalculator.inputItems[category]      ) volumeCalculator.inputItems[category]       = new Array();
    if (! volumeCalculator.inputItems[category][item]) volumeCalculator.inputItems[category][item] = new Array();
    volumeCalculator.inputItems[category][item]['volume'] = volume;
  },

  // sets private volumeCalculator.interfaceColspan ...
  setInterfaceColspan : function(colspan)
  {
    volumeCalculator.interfaceColspan = colspan;
  },

  // sets private volumeCalculator.interfaceWidthPixels ...
  setInterfaceWidthPixels : function(widthPixels)
  {
    volumeCalculator.interfaceWidthPixels = widthPixels;
  },

  // accessor for volumeCalculator.inputItems.category.item.volume ...
  getInputItemVolume : function(itemName)
  {
    for (categoryName in volumeCalculator.inputItems)
      if (volumeCalculator.inputItems[categoryName] && volumeCalculator.inputItems[categoryName][itemName])
        return volumeCalculator.inputItems[categoryName][itemName]['volume'];
    return 0;
  },

  // accessor for form.item.value ...
  getInputItemValue : function(itemName)
  {
    return document.volumeCalculatorForm[itemName].value;
  },

  // accessor for form.item ...
  getInputItemElem : function(itemName)
  {
    return document.volumeCalculatorForm.elements[itemName];
  },

  // returns array of item names in category, or all items if category = '' ...
  getInputItemNames : function(category)
  {
    var itemNames = new Array();
    var categoryNames;
    if (category == '')
      categoryNames = volumeCalculator.getInputCategoryNames();
    else
      categoryNames = new Array(category);
    for (var categoryNamesIndex in categoryNames)
      for (var itemName in volumeCalculator.inputItems[categoryNames[categoryNamesIndex]])
        itemNames.push(itemName);
    return itemNames;
  },

  // returns array of category names/headings  ...
  getInputCategoryNames : function()
  {
    var categoryNames = new Array();
    for (var categoryName in volumeCalculator.inputItems)
      categoryNames.push(categoryName);    
    return categoryNames;
  },

  getInventoryList : function()
  {
    var inventory = Array();
    for (var categoryName in volumeCalculator.inputItems) {
      var itemNames = volumeCalculator.getInputItemNames(categoryName);
      for (var itemNamesIndex in itemNames) {
        var quantity = volumeCalculator.getInputItemValue(itemNames[itemNamesIndex]);
        if (quantity.match(/^\d+$/) && quantity > 0)
          inventory.push(quantity + ' x ' + itemNames[itemNamesIndex].replace(/^_/, '').replace(/_+/g, ' '));
      }
    }
    return inventory.join('\n');
  },

  // build the inventory list and populates the inventoryListTextarea ...
  setInventoryList : function(totalCubicMeters, totalCubicFeet)
  {
    var printFrame    = window.frames['printFrame'];
    var inventoryList = volumeCalculator.getInventoryList();
    var customer      = printFrame.document.getElementById('customerName').value;
    if (customer == '') customer = 'Un-named';
    printFrame.document.getElementById('inventoryListTd').innerHTML = inventoryList.replace(/\n/g, '<br>\n');
    document.getElementById('copyTextarea').value = 
      'Inventory List for ' + customer + '\n\n' + inventoryList + '\n\n' +
      'Total Cubic Metres = ' + totalCubicMeters + '\n' +
      'Total Cubic Feet   = ' + totalCubicFeet + '\n\n';
  },

  // call calculateVolume callback per key stroke ...
  setKeypressHandler : function(keypressHandler)
  {
    if (!keypressHandler) keypressHandler = 'volumeCalculator.calculateVolume';
    eval('document.onkeyup = function(e){' + keypressHandler + '();};');
  },

  // draw form in passed targetDivId and hook up calculate button to callbackHandler
  drawUserInterface : function(targetDivId, callbackHandler)
  {
    if (!callbackHandler) callbackHandler = 'volumeCalculator.calculateVolume';
    var volumeCalculatorDiv    = document.getElementById(targetDivId);
    var tableRowIndex          = 0;
    var expandCollapseDivIndex = 0;
    var innerHTML  = '<form name=volumeCalculatorForm method=post action="" onsubmit="' + callbackHandler + '(); return false;">\n' +
                     '<table style="border:1px solid #0062A6" cellpadding=2px cellspacing=2px width="' + volumeCalculator.interfaceWidthPixels + 'px">\n' +
                     '<tr><td class=tableHeader>Furniture Removal Volume Calculator ' + volumeCalculator.revision + '<br>\n<div style="text-align:right;width:100%;font-size:11px">by ' +
                     '<a href="http://www.ekerner.com.au/" onclick="window.open(\'http://www.ekerner.com.au/\'); return false;" title="Developed by eKerner">eKerner.com.au</a><br>\n' +
                     '<i style="font-sise:8px; font-weight:normal;">IT Consultancy in Canberra ACT</i></div>\n' +
                     '</td></tr>\n' + 
                     volumeCalculator.buildResultsTableRow(callbackHandler, tableRowIndex) +
                     volumeCalculator.buildMenuTableRow(tableRowIndex++) +
                     volumeCalculator.buildInventoryList();
    for (var categoryName in volumeCalculator.inputItems)
    {
      innerHTML += '<tr>\n<td class=categoryHeader>\n<a name="' + categoryName + 'Anchor"></a>\n' +
                   '<div style=float:left>' + categoryName.replace(/_/g, ' ').toUpperCase() + '</div>\n' +
                   '<div style=float:none align=right>\n' +
                   '<input type=button name="expandCollapseBtn' + expandCollapseDivIndex +
                   '" title="Expand Category" value="+" onclick="volumeCalculator.expandCollapseVolumeCalculatorDiv(' + 
                    expandCollapseDivIndex + ', this)" class=button>\n</div>\n</td>\n</tr>\n' +
                   '<tr>\n<td>\n<div id="expandCollapseDiv' + (expandCollapseDivIndex++) + '" style="position:relative;display:none">' +
                   '<table style="width:100%" cellpadding=2px cellspacing=2px>\n<tr>\n';
      var itemNames = volumeCalculator.getInputItemNames(categoryName);
      for (var itemNamesIndex = 0; itemNamesIndex < itemNames.length;)
      {
        for (var cellIndex = 0; cellIndex < volumeCalculator.interfaceColspan; cellIndex++)
          if (itemNamesIndex < itemNames.length)
          {
            innerHTML += '<td class=listBody>\n<table width="100%"><tr><td>' + itemNames[itemNamesIndex].replace(/_/g, ' ') + 
                         ':</td>\n<td style="text-align:right"><input type=text name="' + itemNames[itemNamesIndex] + 
                         '" value="" size=2></td></tr></table>\n</td>\n';
            itemNamesIndex++;
          }
          else
            innerHTML += '<td class=listBody>&nbsp;</td>\n';
          if (itemNamesIndex < itemNames.length)
            innerHTML += '</tr><tr>\n';
      }
      innerHTML += '</tr>\n</table>\n</div>\n</td>\n</tr>\n';
    }
    innerHTML += volumeCalculator.buildMenuTableRow(tableRowIndex) + volumeCalculator.buildResultsTableRow(callbackHandler, tableRowIndex); + '</table>\n</form>\n';
    volumeCalculatorDiv.innerHTML = innerHTML;
    volumeCalculator.loadPrintFrame(callbackHandler, ++tableRowIndex);
    eval(callbackHandler + '();');
  },

  // build and load the Inventory List page ...
  loadPrintFrame : function(callbackHandler, tableRowIndex)
  {
    var printFrame = window.frames['printFrame'];
    printFrame.document.open();
    printFrame.document.write('<html><body>' + volumeCalculator.buildPrintFrameInnerHTML(callbackHandler, tableRowIndex) + '</body></html>');
    printFrame.document.close();
  },

  // builds the calculate button and results divs ...
  buildResultsTableRow : function(callbackHandler, tableRowIndex)
  {
    return '<tr>\n<td class=calculateBar>\n' +
           '<table style="width:100%;" border=0 cellpadding="2px" cellspacing=0>\n' +
           '<tr>\n<td rowspan=2><input type=submit title="Calculate Total and Generate Inventory List" value=Calculate onclick="' + callbackHandler + '(); return false;" class=button>\n' + 
           '<input type=button title="Reset Form" value="Reset" ' +
           'onclick="this.form.reset(); volumeCalculator.expandCollapseVolumeCalculatorDivs(\'-\'); if(document.getElementById(\'inventoryList\').style.display != \'none\'){volumeCalculator.showHideInventoryList();}; window.frames[\'printFrame\'].document.getElementById(\'customerName\').value = \'Un-named\';' + callbackHandler + '();" class=button>\n' +
           '</td>\n' +
           volumeCalculator.buildResultsTableDivs(tableRowIndex) +
           '</tr>\n</table>\n</td>\n</tr>\n';
  },

  // builds the calculate button and results divs ...
  buildResultsTableDivs : function(tableRowIndex)
  {
    return '<td style="text-align:right; font-weight:bold; font-size:14px">Total Cubic Metres = </td>\n' +
           '<td><div id=cubicMetersResultDiv' + tableRowIndex + ' style="width:100%; text-align:right; color:#F08000; font-weight:bold; font-size:14px">0.00</div></td>\n' +
           '</tr><tr>\n<td style="text-align:right; font-weight:bold; font-size:14px">Total Cubic Feet&nbsp;&nbsp; = </td>\n' +
           '<td><div id=cubicFeetResultDiv' + tableRowIndex + ' style="width:100%; text-align:right; color:#F08000; font-weight:bold; font-size:14px">0.00</div></td>\n';
  },

  // builds the collapse/expand all buttons table row ...
  buildMenuTableRow : function(tableRowIndex)
  {
    return '<tr>\n<td class=calculateBar style="text-align:right">\n'+
           '<input type=button title="Expand All Categories" value="Show All" onclick="volumeCalculator.expandCollapseVolumeCalculatorDivs(\'+\')" class=button>\n' +
           '<input type=button title="Collapse All Categories" value="Hide All" onclick="volumeCalculator.expandCollapseVolumeCalculatorDivs(\'-\')" class=button>\n' +
           '<input id=inventoryListButton' + tableRowIndex + ' type=button title="Expand Inventory List" value="Show Inventory"' +
           ' onclick="volumeCalculator.showHideInventoryList();" class=button>\n' +
           '</td>\n</tr>\n';
  },

  // builds the hideable inventory list textarea ...
  buildInventoryList : function()
  {
    return '<tr id=inventoryList style="display:none;">\n<td>\n' +
           '<iframe id=printFrame name=printFrame style="width:100%; height:100px;" scrolling=yes></iframe>\n' +
           '<div id=copyTextareaDiv style="display:none;">\n' +
           '<textarea id=copyTextarea name=copyTextarea style="width:100%; height:100px;"></textarea>\n</div>\n' +
           '<br>\n<div style="width:100%;text-align:right;">\n' +
           '<input type=button title="Copy Inventory List to Clipboard" value="Copy" onclick="volumeCalculator.copyInventoryList();" class=button>\n' +
           '<input type=button title="Print Inventory List" value="Print" onclick="volumeCalculator.printInventoryList(); return false;" class=button>\n' +
           '<input type=button title="Hide Inventory List" value="Hide Inventory" onclick="volumeCalculator.showHideInventoryList();" class=button>\n' +
           '</div>\n</td>\n</tr>\n';
  },
  
  buildPrintFrameInnerHTML : function(callbackHandler, tableRowIndex)
  {
    return '<table style="width:100%; border:0" cellpadding="2px" cellspacing="0px">\n' +
           '<tr><td colspan=2 style="background:#EBEBE3; color:#0062A6;">\n' +
           '<span style="font-size:15px; font-weight:600">Inventory List</span>\n' +
           ' for <input type=text value="Un-named" id="customerName" onchange="window.parent.' + callbackHandler + '(); return false;"></td></tr>\n' +
           '<tr><td id=inventoryListTd colspan=2></td></tr>\n<tr>\n' +
           volumeCalculator.buildResultsTableDivs(tableRowIndex) +
           '</tr>\n</table>\n';
  },

  // tally input volumes - must be called by user from callbackHandler ...
  calculateVolume : function()
  {
    var totalCubicMeters = 0;
    var totalCubicFeet   = 0;
    var itemNames = volumeCalculator.getInputItemNames('');
    for (var itemNamesIndex in itemNames)
    {
       var itemValue = volumeCalculator.getInputItemValue(itemNames[itemNamesIndex]);
       if (! itemValue)
         itemValue = 0;
       else if (! itemValue.match(/^\d+$/))
       {
         volumeCalculator.reportCalculationResult('error', 'error');
         alert('Invalid Qty Value: "' + itemValue + '" for Item: "' + itemNames[itemNamesIndex] + '"!');
         volumeCalculator.getInputItemElem(itemNames[itemNamesIndex]).focus();
         return 0;
       }
       totalCubicMeters += parseFloat(itemValue * volumeCalculator.getInputItemVolume(itemNames[itemNamesIndex]));
    }
    totalCubicMeters = volumeCalculator.roundUpToDecimalPlaces(totalCubicMeters, 2);
    totalCubicFeet   = volumeCalculator.roundUpToDecimalPlaces(volumeCalculator.cubicMetersToCubicFeet(totalCubicMeters), 2)
    volumeCalculator.reportCalculationResult(
      totalCubicMeters,
      totalCubicFeet
    );
    volumeCalculator.setInventoryList(totalCubicMeters, totalCubicFeet);
    return totalCubicMeters;
  },

  // sets the results divs as select ...
  reportCalculationResult : function(cubicMeters, cubicFeet)
  {
    document.getElementById('cubicMetersResultDiv0').innerHTML = cubicMeters;
    document.getElementById('cubicFeetResultDiv0'  ).innerHTML = cubicFeet;
    document.getElementById('cubicMetersResultDiv1').innerHTML = cubicMeters;
    document.getElementById('cubicFeetResultDiv1'  ).innerHTML = cubicFeet;
    window.frames['printFrame'].document.getElementById('cubicMetersResultDiv2').innerHTML = cubicMeters;
    window.frames['printFrame'].document.getElementById('cubicFeetResultDiv2'  ).innerHTML = cubicFeet;
  },

  // conversion ...
  cubicFeetToCubicMeters : function(cubicFeet)
  {
    return (cubicFeet * 0.0283168466);
  },

  // conversion ...
  cubicMetersToCubicFeet : function(cubicMeters)
  {
    return (cubicMeters / 0.0283168466);
  },

  // round up to 2 decimal places ...
  roundUpToDecimalPlaces : function(floatingPointNumber, decimalPlaces)
  {
    var roundedUpValue = '' + (Math.ceil(floatingPointNumber * 100) / 100);
    var decimalIndex   = roundedUpValue.indexOf('.');
    var floatLength    = 0;
    if (decimalIndex == -1)
      roundedUpValue += decimalPlaces > 0 ? '.' : ''
    else
        floatLength = roundedUpValue.length - decimalIndex - 1;
    for (var i = 0; i < (decimalPlaces - floatLength); i++)
      roundedUpValue += '0';
    return roundedUpValue;
  },
  
  showHideInventoryList : function()
  {
    var inventory = document.getElementById('inventoryList');
    var button0   = document.getElementById('inventoryListButton0');
    var button1   = document.getElementById('inventoryListButton1');
    if (inventory.style.display == 'none') {
      inventory.style.display = '';
      button0.value = 'Hide Inventory';
      button1.value = 'Hide Inventory';
      button0.title = 'Collapse Inventory List';
      button1.title = 'Collapse Inventory List';
      button0.focus();
    } else {
      document.getElementById('copyTextareaDiv').style.display = 'none';
      inventory.style.display = 'none';
      button0.value = 'Show Inventory';
      button1.value = 'Show Inventory';
      button0.title = 'Expand Inventory List';
      button1.title = 'Expand Inventory List';
    }
  },

  // focus and print the inventory textarea ...
  printInventoryList : function()
  {
    var printFrame = window.frames['printFrame'];
    var inventoryListTd = printFrame.document.getElementById('inventoryListTd');
    if (inventoryListTd.innerHTML != '') {
      printFrame.focus();
      printFrame.print();
    } else {
      alert('Nothing to print.\nPlease select some values first.');
    }
  },

  // focus and print the inventory textarea ...
  copyInventoryList : function() {
    var copyTextarea    = document.getElementById('copyTextarea');
    if (window.frames['printFrame'].document.getElementById('inventoryListTd').innerHTML != '') {
      if(window.clipboardData)
        window.clipboardData.setData('text', copyTextarea.value);
      else if (copyTextarea.createTextRange) {
        var range = copyTextarea.createTextRange();
        if (range) range.execCommand('Copy');
      }
      else {
        alert('I cannot access your clipboard.\nPlease copy manually.');
        document.getElementById('copyTextareaDiv').style.display = '';
        document.getElementById('copyTextarea').select();
      }
    } else {
      alert('Nothing to copy.\nPlease select some values first.');
    }
  },

  // global func used by interface ...
  setExpandCollapseDivAndButton : function(divElem, btnElem, expand)
  {
    if (expand)
    {
      divElem.style.display = 'block';
      btnElem.value         = '-';
      btnElem.title         = 'Collapse Category';
    }
    else
    {
      divElem.style.display = 'none';
      btnElem.value         = '+';
      btnElem.title         = 'Expand Category';
    }
  },

  // global func used by interface ...
  expandCollapseVolumeCalculatorDiv : function(divNo, btnElem)
  {
    var divElem = document.getElementById('expandCollapseDiv' + divNo);
    volumeCalculator.setExpandCollapseDivAndButton(divElem, btnElem, divElem.style.display == 'none');
  },

  // global func used by interface ...
  expandCollapseVolumeCalculatorDivs : function(expandCollapse)
  {
    for (var expandCollapseDivIndex = 0; ; expandCollapseDivIndex++)
    {
      var divElem = document.getElementById('expandCollapseDiv' + expandCollapseDivIndex);
      var btnElem = document.volumeCalculatorForm['expandCollapseBtn' + expandCollapseDivIndex];
      if (! divElem) break;
      volumeCalculator.setExpandCollapseDivAndButton(divElem, btnElem, expandCollapse == '+');
    }
  }

};
// ... end class volumeCalculator



