/****************************************************
*
* FL Shop Engine v1.2 (c) 2004-2007, Image-Line
*
*****************************************************/

// soft implementations of possibly unsupported API
if (!Array.prototype.push) {
  Array.prototype.push = function() {
    for (var i = 0; i < arguments.length; i++) {
      this[this.length] = arguments[i];
    }
    return this.length;
  };
}

/****************************************************
*
* Preloader
*
*****************************************************/

fileList = new Array(); // of Image
fileCount = 0;
FaqImage = 1;

function onImageLoad() {
  fileCount++;
}

function loadFile(filePath) {
  if (pageType != "shop")
    return;

  fileList[fileList.length] = new Image();
  fileList[fileList.length -1 ].src = filePath;
  fileList[fileList.length -1 ].onload = onImageLoad;
  fileCount--;
}

loadFile('media/_cb_bundle.gif');
loadFile('media/_cb_no.gif');
loadFile('media/_cb_no_ro.gif');
loadFile('media/_cb_yes.gif');
loadFile('media/_cb_yes_ro.gif');
loadFile('media/_cb_dis.gif');
loadFile('media/_panel_bg.gif');
loadFile('media/_panel_bottom.gif');
loadFile('media/_panel_bottom_ie50.gif');
loadFile('media/_panel_titlebar.gif');
loadFile('media/_reduction_empty.gif');
loadFile('media/_reduction_filled.gif');
loadFile('media/_btnset2.gif');

/****************************************************
* global vars
*****************************************************/
if (!'shopPromoType' in window)
  shopPromoType = 0; // no promo

affiliateDiscount = 90 // 10% by default

/****************************************************
* THE FORMULA: gets several parameters about the
* product order and returns data about the resulting
* costs & reduction
*****************************************************/

function orderSum(a, d) {
  // sums all elements (discounted with the given percentage = d) and returns the result
  var sum = 0;
  for (var i in a)
    sum += Math.ceil(a[i] * d / 100);
  return sum;
}

function orderSumPromo(a, d) {
  // sums all elements (discounted with the given percentage = d) and returns the result
  var sum = 0;
  for (var i in a)
    sum += a[i];
  return Math.ceil(sum * d / 100);
}

function orderFormula(totalCount, discountedCount, nonDiscountedCount, discountedOrderCost, nonDiscountedOrderCost) {
  // ** FUNCTION PARAMETERS**
  // totalCount = number of ALL products, DISCOUNTED, purchased or not purchased, that are *VISIBLE* to the customer
  // discountedCount = number of ORDERED+PURCHASED products that are marked as DISCOUNTED (add discount percent and get price reduction)
  // nonDiscountedCount = number of ORDERED+PURCHASED products that are marked as NON-DISCOUNTED (third party products etc.)
  // discountedOrderCost = array of prices (for each product), which summed give the COST of all ORDERED DISCOUNTED products (sum of their prices)
  // nonDiscountedOrderCost = array of prices (for each product), which summed give the COST of all ORDERED NON-DISCOUNTED products (sum of their prices)
  //
  // ** RESULT OBJECT **
  // result.reduction = percentage of reduction applied to the DISCOUNTED products ORDERED by the customer
  // result.finalCost = final cost the customer pays (YOU PAY:)

  var result = new Object();
  if (pageType == "shop") {
    // for pages different that shop the discounts and precalced, sum straight
    if (shopPromoType == 0) {
//      if ((discountedCount + nonDiscountedCount) == 1) {
        // one product is ordered+owned only (no discount)
        result.finalCost = orderSumPromo(nonDiscountedOrderCost.concat(discountedOrderCost), 100);
        result.reduction = 0;
//      } else {
//        // two or more products are ordered+owned (discount activated)
//        result.reduction = Math.min(50, Math.floor(50 * discountedCount / totalCount));
//        result.finalCost = orderSum(discountedOrderCost, 100 - result.reduction) + orderSum(nonDiscountedOrderCost, 100);
//      }
    } else {
      result.finalCost = orderSumPromo(nonDiscountedOrderCost.concat(discountedOrderCost), affiliateDiscount);
      result.reduction = 10;
    }
  } else {
    result.finalCost = orderSumPromo(nonDiscountedOrderCost.concat(discountedOrderCost), 100);
    result.reduction =- 1;
  }

  return result;
}

/****************************************************
* Bundle Price related functions
*****************************************************/

function getBundleProductPrice(prod, bundle) {
  if (!(bundle in prod.bundles)) return null; // product not included in bundle
  if (prod.bundles[bundle] != null) return prod.bundles[bundle]; // hard-coded price
  return Math.floor(prod.price * (100 - dbase.bundles[bundle].discount) / 100); // calculate discount
}

// assigns balanced 10% off prices for bundle products for the affiliate sys
function balanceBundleAffiliate(bundle) {
  var totalSum = Math.ceil(dbase.bundles[bundle].price * .90);
  // step 1, set 10% off prices for all bundled products
  var refrSum = 0;
  var tweak = []; // native (adjust price to fit (sum)
  var tweakNot = []; //3rd party (avoid adjust)
  var products = dbase.bundles[bundle].products;
  for (var i in products) {
    var p = dbase.index[products[i]];
    if (p.related && !p.purchased) {
      p.affiliatePrice[bundle] = Math.ceil(.9 * getBundleProductPrice(p, bundle));
      refrSum += p.affiliatePrice[bundle];
      if (p.thirdParty)
        tweakNot[tweakNot.length] = p;
      else
        tweak[tweak.length] = p;
    }
  }
  // step 2, adjust
  if (tweak.length == 0)
    // no native, go with 3rd party
    tweak = tweakNot;
  var i = 0;
  while (refrSum > totalSum) {
    tweak[i].affiliatePrice[bundle]--;
    refrSum--;
    i++;
    if (i == tweak.length)
      i = 0;
  }

  // debug below:
  refrSum = 0;
  for (var i in products) {
    var p = dbase.index[products[i]];
    if (p.related && !p.purchased) {
      refrSum += p.affiliatePrice[bundle];
    }
  }
  //alert('totalSum: ' + totalSum + '  :  ' + 'refrSum: ' + refrSum);
}

/****************************************************
* Processed after the page rendering is completed
*****************************************************/

var loadChecks = 5; // timeout for loading the checbox images

function onDocumentLoad() {
  // init processing here
  // wait until the checkbox images are cached:
//  if (fileCount < 5 && loadChecks > 0 && pageType == "shop") {
//    setTimeout(onDocumentLoad, 500);
//    loadChecks--;
//    return;
//  }
  if (pageType == "shop") {
    // hide loading sign
    document.getElementById('productlist-body').removeChild(document.getElementById('loadingProducts'));
  }
  if (pageType == "shop" || pageType == "checkOut")  {
    // in incompat/equiv groups, scan each group, and if bundle item is found, add bundle product
    for (var bundle in dbase.bundles) {
      for (var i in dbase.tempIG) {
        var foundBundleItem = false;
        for (var j in dbase.tempIG[i]) {
          var code = dbase.tempIG[i][j];
          if (dbase.index[code].bundles && (bundle in dbase.index[code].bundles)) {
            foundBundleItem = true;
          }
        }
        if (foundBundleItem) dbase.tempIG[i].push(bundle);
      }
      for (var i in dbase.tempEG) {
        var foundBundleItem = false;
        for (var j in dbase.tempEG[i]) {
          var code = dbase.tempEG[i][j];
          if (dbase.index[code].bundles && (bundle in dbase.index[code].bundles)) {
            foundBundleItem = true;
            break;
          }
        }
        if (foundBundleItem) dbase.tempEG[i].push(bundle);
      }
    }

    // parse incompatibility & equivalent groups
    for (var i in dbase.index) { // pass through each product
      for (var j in dbase.tempIG) { // scan each separate group
        for (var k in dbase.tempIG[j]) { // scan each code in the group
          if (dbase.tempIG[j][k] == i) { // the current product is currently scanned code in the group
            for (var l in dbase.tempIG[j]) { // scan the whole group again
              if (dbase.tempIG[j][l] != i) { // interested in every product in that group but the current product
                var existing = false;
                for (var m in dbase.index[i].relIncompatible) { // and not already listed in products info
                  if (dbase.index[i].relIncompatible[m] == dbase.tempIG[j][l]) {
                    existing = true;
                    break;
                  }
                }
                // add it
                if (!existing)
                  dbase.index[i].relIncompatible[dbase.index[i].relIncompatible.length] = dbase.tempIG[j][l];
              }
            }
          }
        }
      }
    }
    for (var i in dbase.index) {
      for (var j in dbase.tempEG) {
        for (var k in dbase.tempEG[j]) {
          if (dbase.tempEG[j][k] == i) { // product in group
            for (var l in dbase.tempEG[j]) {
              if (dbase.tempEG[j][l] != i) {
                var existing = false;
                for (var m in dbase.index[i].relEquivalent) {
                  if (dbase.index[i].relEquivalent[m] == dbase.tempEG[j][l]) {
                    existing = true;
                    break;
                  }
                }
                if (!existing)
                  dbase.index[i].relEquivalent[dbase.index[i].relEquivalent.length] = dbase.tempEG[j][l];
              }
            }
          }
        }
      }
    }
    delete dbase.tempIG;
    delete dbase.tempEG;

    // compute bundle prices
    for (var bundle in dbase.bundles) {
      var pr = 0, fpr = 0, apr = 0, ipr;
      for (var i = 0; i < dbase.bundles[bundle].products.length; i++) {
        var p = dbase.bundles[bundle].products[i];
        switch (pageType) {
          case 'shop':
            if (dbase.index[p].related) {
              fpr += (ipr = getBundleProductPrice(dbase.index[p], bundle));
              if (!dbase.index[p].purchased) {
                apr += dbase.index[p].price;
                pr += ipr;
              }
            }
            break;
          case 'checkOut':
            if (!dbase.index[bundle].ordered) break;
            if (dbase.index[p].related && !dbase.index[p].purchased) {
              apr += dbase.index[p].price = getBundleProductPrice(dbase.index[p], bundle);
            }
            break;
        }
      }
      dbase.index[bundle].price = (dbase.bundles[bundle].price += pr);
      dbase.bundles[bundle].actualPrice = apr;
      if (dbase.bundles[bundle].actualPrice == 0) {
        dbase.index[bundle].purchased = true;
        // for already-owned bundles, show full price for info purposes
        dbase.index[bundle].price = fpr;
      }
    }
  }

  if (pageType == "shop")  {
    if (shopPromoType == 0) {
      //document.getElementById('discountSection').style.display = "block";
    } else {
      document.getElementById('promoTenSection').style.display = "block";
    }

    setStep(1);       // form step
    setPayment();     // set message for payment (in step 3)
    onSerialChange(); // check for products with serial
  }
  if (pageType == "shop" || pageType == "checkOut")  {
    renderPanels();   // triggers panels to render themselves in the placeholders created during creation
    onOrderChange();  // update order details;
  }
  if (pageType == "checkOut") {
    // write totals
    var cont = '';
    cont += '<div class="label">Total Order Cost:</div>';
    cont += '<div class="cost">US$ <strong>' + orderInfo.cost + '</strong>&nbsp;&nbsp;&nbsp;&nbsp;</div>';
    document.getElementById('totals').innerHTML = cont;
  }
  // opera fixes
  var isOpera = navigator.userAgent.indexOf('Opera') >= 0;
  if (isOpera) {
    function onWinChange() {
      var newGuideHeight = document.body.offsetHeight; // helper element with height 100%
      if (guideHeight != newGuideHeight) {
        var setHeight = newGuideHeight - 124;
        document.getElementById("console-body").style.height = setHeight + "px";
        document.getElementById("productlist-body").style.height = setHeight + "px";
        guideHeight = guide.offsetHeight;
      }
    }
    var guideHeight = 0;
    setInterval(onWinChange, 200);
    onWinChange();
  }
}

/****************************************************
* Common Tools
*****************************************************/

var productCount = 0; // init global counter for *ordered* products
var isOpera = navigator.userAgent.indexOf('Opera') >= 0;

function cancelBubble(evt) {
  // cross browser event bubbling prevention
  // (use: call handler with param. 'event', name it 'evt' in the handler def.; in handler "return cancelBubble(evt)";
  if (!evt && window.event)
    evt = window.event;
  if (evt != null) {
    if (typeof evt.stopPropagation == 'function')
      evt.stopPropagation();
    else
      evt.cancelBubble = true;
  }
  return false;
}

// pre-calc animation mappers
var easeIn = [];
var easeOut20 = [];
var easeOut50 = [];
for (var i = 0; i <= 100; i += 10) {
  easeIn[i] = (Math.pow(50, 1 + i / 100) - 50) / 2450;
  easeOut20[i] = 1 - (Math.pow(20, 2 - i / 100) - 20) / 380;
  easeOut50[i] = 1 - (Math.pow(50, 2 - i / 100) - 50) / 2450;
}
//function easeIn(value, expn) { return (Math.pow(expn, 1 + value) - expn) / (expn * (expn - 1)); } 
//function easeOut(value, expn) { return 1 - (Math.pow(expn, 2 - value) - expn) / (expn * (expn - 1)); } 

function changeContent(id, shtml) { var e = document.getElementById(id); if (e) e.innerHTML = shtml; }
function formatCurrency(i) { return '$' + i; }

/****************************************************
* INFO.JS Parsers
*****************************************************/

dbase = new Object();

dbase.tempIG = new Array(); // temporary storage for arrays of incompatible products (indexed below)
dbase.tempEG = new Array(); // temporary storage for arrays of equivalent products (indexed below)

dbase.categories = new Array();
dbase.categories[0] = {
  // internal
  name: "[INTERNAL]",
  products: new Array()
}

dbase.bundles = new Object();

// indexed by ID
dbase.index = new Object();

dbase.defaults = new Object();

function setCategory(name) {
  dbase.categories[dbase.categories.length] = {
    name: name,
    products: new Array()
  }
}

function addProduct(obj) {
  if (obj.id == 0) {
    dbase.defaults = obj;
  } else {
    if (window.shopState[obj.id]) {
      obj.ordered = true;
    } else if (obj.ordered) {
      window.shopState[obj.id] = true;
    }
    for (var i in dbase.defaults)
      if (typeof(obj[i]) == 'undefined')
        obj[i] = dbase.defaults[i];
    obj.relIncompatible = [];
    obj.relEquivalent = [];
    obj.affiliatePrice = {};
    // TEMP WORKAROUND (avoid referencing object structure)
    var x = dbase.categories.length - 1;
    dbase.categories[x].products[dbase.categories[x].products.length] = obj;
    dbase.index[obj.id] = obj;
    if (obj.bundles) {
      for (var bundle in obj.bundles) {
        dbase.bundles[bundle].products.push(obj.id);
      }
    }
  }
}

function addIncompatibleGroup(group) {
  dbase.tempIG[dbase.tempIG.length] = group;
}

function addEquivalentGroup(group) {
  dbase.tempEG[dbase.tempEG.length] = group;
}

function addBundle(bundle, base, discount) {
  dbase.bundles[bundle] = { price: base, actualPrice: 0, discount: discount, products: [] };
}

/****************************************************
* Form Step Switches
*****************************************************/
usesSerial = false;
function onSerialChange() {
  // check if a product needs a serial and update interface and flasg accordignly
  usesSerial = false;
  for (var i in panelA)
    if (panelA[i].isChecked && panelA[i].info.serial)
      usesSerial = true;
  if (!usesSerial) {
    if (currentStep ==4 )
      setStep(5);
  }
  showProgress();
}

var currentStep = 1;
var maxSteps = 5;

function nextStep() {
  if (currentStep == 3 && !usesSerial) {
    if (noValidate) {
      setStep(5);
    } else if (formValidate(currentStep)) {
      setStep(5);
    }
  } else if (currentStep < maxSteps) {
    if (noValidate) {
      setStep(currentStep + 1);
    } else if (formValidate(currentStep)) {
      setStep(currentStep + 1);
    }
  }
}

function prevStep() {
  if (currentStep == 5 && !usesSerial)
    setStep(3);
  else if (currentStep > 1)
    setStep(currentStep - 1);
}

function showProgress() {
  if (!isIE50)
    document.getElementById('wb-progressbar-inner').style.width = Math.round(96 * (currentStep - 1) / (maxSteps - (usesSerial? 1: 2))) + "px";
}

function setStep(step) {
  currentStep = step;
  for (var i = 1; i <= maxSteps; i++)
    document.getElementById('form-step' + i).style.display = (i == step)? 'block': 'none';
  if (step == 1)
    previousButton.setEnabled(false);
  else
    previousButton.setEnabled(true);
  if (step == maxSteps) {
    nextButton.setEnabled(false);
    updateOrderButton();
  } else {
    nextButton.setEnabled(true);
    updateOrderButton();
  }
  showProgress();
}

// Payment options:
var payA = new Array(); // all payment options

function addPaymentMethod(meth) {
  payA[payA.length] = meth;
}

function setPaymentMethods(tags) {
  var tagsA = tags.split('|');
  var payCombo = document.getElementById('ec_PaymentMethod');
  for (var i = 0; i < payA.length; i++) {
    for (var j = 0; j < tagsA.length; j++) {
      if (payA[i].val.toLowerCase() == tagsA[j].toLowerCase()) {
        payCombo.options[payCombo.options.length] = new Option(payA[i].title, payA[i].val);
      }
    }
  }
  payCombo.selectedIndex = 0;
}

function setPayment() {
  var pm = document.getElementById("ec_PaymentMethod");
  var id,
      index = pm.selectedIndex >= 0? pm.selectedIndex: 0; // save Opera from itself
      val   = pm.options[index].value;
      verb  = pm.options[index].text;
  for (var i = 0; i < payA.length; i++) {
    if (val.toLowerCase() == payA[i].val.toLowerCase()) {
      id = i;
      break;
    }
  }

  if ((val == 'VCASH') && (pageType == "shop")) {
    renderVCashNotes();
  } else {
    if (id == 0)
      document.getElementById('paystring').innerHTML = '<img style="float:left;margin:0px 6px 6px 0px;" src="media/_pm_' + val.toLowerCase() + '.gif">Please select the <strong>Payment Method</strong> you want to use in the listbox above before you can proceed to the checkout.<br><br>The <img src="media/_ssl.gif" style="vertical-align:middle" alt=""> <strong>Order</strong> button will enable when you do.';
    else
      document.getElementById('paystring').innerHTML = '<img style="float:left;margin:0px 6px 6px 0px;" src="media/_pm_' + val.toLowerCase() + '.gif">You have selected <strong>' + verb + '</strong>. ' + payA[(id == "undefined")? 0: id].info;
  }

  var ccInfo = payA[id].creditCards;
  var ccIcons = '';
  if (ccInfo.length > 1) {
    for (var i = 0; i < ccInfo.length; i++) {
      ccIcons += "<img src='media/_cc_" + ccInfo[i] + ".gif'>";
    }
  }
  document.getElementById('cc-types').innerHTML = ccIcons;
  updateOrderButton();
}

function insertPaymentPics(val) {
  for (var i = 0; i < payA.length; i++) {
    if (val.toLowerCase() == payA[i].val.toLowerCase()) {
      var id = i;
      break;
    }
  }
  var c = '';
  // c += '<div id="payPic_checkout"><img style="float:left;margin:0px 6px 6px 0px;" src="media/_pm_' + val.toLowerCase() + '.gif"></div>';
  var ccInfo = payA[id].creditCards;
  c += '<div id="payPicCC_checkout">';
  for (var i = 0; i < ccInfo.length; i++) {
    c += "<img src='media/_cc_" + ccInfo[i] + ".gif'>";
  }
  c += '</div>';
  document.write(c);
}

vCashAmount = 0;
vCashOk = true;

function setVCash(vc) {
  vCashAmount = vc;
}

function renderVCashNotes() {
  var pm = document.getElementById("ec_PaymentMethod");
  var val = pm.options[pm.selectedIndex >= 0? pm.selectedIndex: 0].value;
  vCashOk = true; // don't disable order button by default
  if (val == "VCASH") {
    // find index of VCASH payA
    for (var i = 0; i < payA.length; i++) {
      if (payA[i].val == 'VCASH') break;
    }
    var cont = '';
    cont += '<img style="float:left;margin:0px 6px 6px 0px;" src="media/_pm_vcash.gif">You have selected <strong>IL Virtual Cash</strong>. ' + payA[i].info;
    cont += '<br /><br />';
    cont += 'Your current IL Virtual Cash balance: <b>' + vCashAmount + ' USD</b>';
    cont += '<br /><br />';
    if (vCashAmount - orderInfo.finalCost < 0) {
      cont += 'IL Virtual Cash balance after your order: <strong style="color:red">' + (vCashAmount - orderInfo.finalCost) + ' USD</strong>';
      cont += '<br /><br /><div style="clear:both">&nbsp;</div><b>Your current IL Virtual Cash balance can not cover the cost of your order. Please select one of the other payment options.';
      vCashOk = false; // disable order button
    } else {
      cont += 'IL Virtual Cash balance after your order: <strong>' + (vCashAmount - orderInfo.finalCost) + ' USD</strong>';
    }
    document.getElementById('paystring').innerHTML = cont;
  }
}

/****************************************************
* Form Validation and Submit Routines
*****************************************************/

// regexp validation
re_blank     = /[.]*/;
re_name      = /^[a-zA-Z_\s'-]+\s+[a-zA-Z_\s'-]+$/;
re_company   = /^[-\w\s']+$/;
re_email     = /^[\w]([\w\.-]*[\w]){0,1}@[\w][\w\.-]*[\w]\.[a-zA-Z][a-zA-Z\.]*[a-zA-Z]$/;
re_telephone = /^[-\+\(\)\.\s^0-9]+$/;
re_zip       = /^[0-9]+$/;
re_serial    = /[.]*/;///[0-9]{5}-[0-9]{5}-[0-9]{5}-[0-9]{5}/;

valFields = new Array(); // index = form step (2D arr)
valFields[0] = new Array();
valFields[1] = new Array();
valFields[2] = new Array();
valFields[3] = new Array();
valFields[4] = new Array();
valFields[5] = new Array();

function addField(step, id, title, required, re) {
  if (typeof(valFields[step]) == "undefined")
    valFields[step] = new Array();
  valFields[step][valFields[step].length] = {
    id: id,
    title: title,
    required: required,
    re: re
  };
}

function valid_pass() {

	if(document.formfalse.ec_CustomerEmail.value != document.getElementById('email_confirm_box').value){
			alert('"Email" and "Confirm Email" do not match. The email address you entered in the \'Direct Customer Information\' section of this checkout does not match the email address you entered in this \'Confirm Email adress\' field. Please correct so that both match. ');
			return false;
		}

	if(document.getElementById('password')) {
		var pass = document.getElementById('password').value;
		var pass2 = document.getElementById('password2').value;

		if(pass.length < 4) {
			alert('The password must contain more than three characters.');
			document.getElementById('password').focus();
			return false;
		}

		if(pass != pass2) {
			alert('The "Password" and "Confirm password" field do not match. Please retype them and make sure that both match to the password of your choice. ');
			document.getElementById('password2').focus();
			return false;
		}

		document.forms.checkOutForm.password.value = pass;
		document.forms.checkOutForm.password2.value = pass2;
	}

	document.getElementById("checkOutForm").submit();
}

function formValidate(step, noalert) {	

  var i, j, k, failure = false;
  for (i in valFields[step]) {
    j = valFields[step][i];
    k = document.getElementById(j.id);
    k.value = trimSpaces(k.value);
    if (k.value == '') {
      if (j.required) {
        if (!noalert)
          alert('"' + j.title + '" is a required entry. Please fill it and click "Next" to continue.');
        failure = true;
      }
    } else if (!j.re.test(k.value)) {
      if (!noalert)
        alert('"' + j.title + '" has invalid format. Please check again if you entered it properly and click "Next" to continue.');
      failure = true;
    }
    if (failure) break;
  }
  
  return !failure;
}

function trimSpaces(str) {
  return str.replace(/^\s+|\s+$/g, '');
}

function formSubmit(newURL) {
  // limit affiliate discounted orders to min $90
  if (shopPromoType && (orderInfo.finalCost < 90)) {
    alert('Your order needs to be $90 or more to take avdantage of this special discount offer. ' +
          'Please correct your order or re-enter the shop without using an affiliate link.');
    return;
  }
  // vcash check
  var pm = document.getElementById("ec_PaymentMethod");
  var val = pm.options[pm.selectedIndex >= 0? pm.selectedIndex: 0].value;
  if (val == "VCASH" && vCashAmount < orderInfo.finalCost) {
    alert ('The total order cost (' + orderInfo.finalCost + ' USD) is more than your currently available IL Virtual Cash amount (' + vCashAmount + ' USD). ' +
           'Please adjust your order or select one of the other payment options');
    return;
  }

  var i;
  var form = document.getElementById("order");
  var relatedproducts = [], products = [], names = [], prices = [], total = 0;
  var bundles = [];
  for (i in panelA) {
    relatedproducts[relatedproducts.length] = panelA[i].info.id;
    if (panelA[i].isChecked) {
      if (panelA[i].info.id in dbase.bundles) {
        bundles.push(panelA[i].info.id);
        prices[prices.length] = 0; // null the bundle price (or itll be charged twice)
      } else {
        prices[prices.length]   = panelA[i].info.price;
      }
      names[names.length]       = panelA[i].info.name;
      products[products.length] = panelA[i].info.id;
    }
  }
  // special case: are bundles added? if so add all bundled products in the bunch
  if (bundles.length) {
    for (var bundle in bundles) {
      bundle = bundles[bundle];
      if (shopPromoType) {
        // assign promo prices on all bundled products
        balanceBundleAffiliate(bundle);
      }
      for (var i in dbase.bundles[bundle].products) {
        id = dbase.bundles[bundle].products[i];
        if (dbase.index[id].related && !dbase.index[id].purchased) {
          for (var j = 0; j < products.length; j++) {
            if (products[j] == id) {
              id = null;
              break;
            }
          }
          if (id) {
            products[products.length] = id;
            names[names.length]       = dbase.index[id].name;
            prices[prices.length]     = dbase.index[id].price;
          }
        }
      }
    }
  }
  // end special case

  var cont = '';
  cont += '<input type="hidden" name="order[relatedproducts]" value="' + relatedproducts.join('|') + '">';
  cont += '<input type="hidden" name="order[products]" value="' + products.join('|') + '">';
  cont += '<input type="hidden" name="order[names]" value="' + names.join('|') + '">';
  cont += '<input type="hidden" name="order[prices]" value="' + prices.join('|') + '">';
  cont += '<input type="hidden" name="order[total]" value="' + orderInfo.cost + '">';
  document.getElementById("hidden-fields").innerHTML = cont;

  if (typeof(newURL) != 'undefined')
    form.action = newURL;
  form.method = submitMethod;
  form.submit();
}

/****************************************************
* Containers
*****************************************************/

function openContainer(title) {
  var cont = '';
  cont += '<h2>' + title + '</h2>';
  document.write(cont);
}

function closeContainer() {};

/****************************************************
* Initialization
*****************************************************/

// store checkbox images
(cb_yes_ro = new Image()).src = 'media/_cb_yes_ro.gif';
(cb_yes    = new Image()).src = 'media/_cb_yes.gif';
(cb_no_ro  = new Image()).src = 'media/_cb_no_ro.gif';
(cb_no     = new Image()).src = 'media/_cb_no.gif';

/****************************************************
* FMButton - creates a multistate image button
*****************************************************/

function insert_FMButton(id, class_id, width, height, texture, offsetX, offsetY) {
  this[id] = new FMButton(id, class_id, width, height, texture, offsetX, offsetY);
}
function FMButton(id, class_id, width, height, texture, offsetX, offsetY) {
  // generate the button code
  var cont = '';
  cont += '<div id="' + id + '" ';
  if (class_id != '')
    cont += 'class="' + class_id + '" ';
  cont += 'style="font-size:0px;width:' + width + 'px; height:' + height + 'px;' +
          'background-image:url(media/' + texture + ');' +
          'background-position:-' + offsetX + 'px -' + offsetY + 'px" ';
  cont += 'onmouseover="' + id + '.onEvent(event,\'rollover\')" ';
  cont += 'onmouseout="' + id + '.onEvent(event,\'rollout\')" ';
  cont += 'onmousedown="' + id + '.onEvent(event,\'down\')" ';
  cont += 'onmouseup="' + id + '.onEvent(event,\'up\')" ';
  cont += 'onclick="' + id + '.onEvent(event,\'click\')" ';
  if (isIE)
    cont += 'ondblclick="' + id + '.onEvent(event,\'click\')" ';
  cont += '></div>';
  document.write(cont);

  this.id = id; this.width = width; this.height = height; this.texture = texture; this.offsetX = offsetX; this.offsetY = offsetY;

  this.state = "normal"; // normal, rollover, down
  this.enabled = true;
}

FMButton.prototype.onEvent = function(evt, evt_type) {
  switch (evt_type) {
    case "rollout": case "up":
                     this.state = "normal"; break;
    case "click":    this.state = "rollover"; if (this.enabled) this.onClick(); break;
    case "rollover": this.state = "rollover"; break;
    case "down":     this.state = "down"; break;
  }
  this.paint();
  return cancelBubble(evt);
}

FMButton.prototype.paint = function(flag) {
  var skinStyle = document.getElementById(this.id).style;
  if (this.enabled) {
    switch (this.state) {
      case "down":
        document.getElementById(this.id).style.backgroundPosition = '-' + this.offsetX + 'px -' + parseInt(this.offsetY + this.height * 2) + 'px';
        break;
      case "rollover":
        document.getElementById(this.id).style.backgroundPosition = '-' + this.offsetX + 'px -' + parseInt(this.offsetY + this.height * 1) + 'px';
        break;
      default:
        document.getElementById(this.id).style.backgroundPosition = '-' + this.offsetX + 'px -' + parseInt(this.offsetY) + 'px';
    }
  } else {
    document.getElementById(this.id).style.backgroundPosition = '-' + this.offsetX + 'px -' + parseInt(this.offsetY + this.height * 3) + 'px';
  }
}

/* PUBLIC */

FMButton.prototype.setEnabled = function(flag) {
  this.enabled = flag;
  this.paint();
}

FMButton.prototype.onClick = function() {
  // override with custom handler
}

/****************************************************
* insertProducts()/modifyProduct() implementation
*****************************************************/

function insertProducts() {
  dbase.notices = [];
  var i, j, k, l;
  updateShopState();
  for (i = 1; i < dbase.categories.length; i++) {
    // skip 0, its internal
    k = 0;
    l = 0;
    for (j in dbase.categories[i].products) // count visible and ordered items
      if (dbase.categories[i].products[j].related) {
        k++;
        if (dbase.categories[i].products[j].ordered) l++;
      }
    if (k) {
      if (pageType != "checkOut" || l)
        openContainer(dbase.categories[i].name);
      for (j = 0; j < dbase.categories[i].products.length; j++) {
        k = dbase.categories[i].products[j];
        // render only visible items, if checkout - only ordered
        if (k.related && ((pageType != "checkOut") || k.ordered)) {
          this['panel' + k.id] = new ProductPanel(k.id);
          // count/order notices in checkout page
          if ((pageType == "checkOut") && (typeof k.notice != 'undefined')) {
            for (var m = 0; m < k.notice.length; m++) {
              dbase.notices[dbase.notices.length] = k.notice[m];
            }
          }
        }
      }
      if ((pageType=="checkOut" && l) || pageType != "checkOut" || $l) closeContainer();
    }
  }
  dbase.notices.sort(function(a, b) { return ((a.priority > b.priority) ? -1 : ((a.priority < b.priority) ? 1 : 0)); });
}

function modifyProduct(id, obj) {
  var o = dbase.index[id];
  for (var i in obj) {
    if (i != 'id' && i != 'plugin' && i != 'type')
      o[i] = obj[i];
    if ((i == 'price') && (id in dbase.bundles))
      dbase.bundles[id].price = obj.price;
  }
};

function insertNotices() {
  if (dbase.notices.length > 0) {
    for (var i = 0; i < dbase.notices.length; i++) {
      var dontshow = false;
      for (var j = 0; j < dbase.notices[i].dontshow.length; j++) {
        var product = dbase.index[dbase.notices[i].dontshow[j]];
        if (product.purchased || product.ordered) {
          dontshow = true;
          break;
        }
      }
      if (dontshow) break;
      document.write('<div class="checkout-notice"><div class="icon-space">&nbsp;</div><h1>' + dbase.notices[i].title + '</h1>' +
                     '<p>' + dbase.notices[i].message + '</p></div>');
    }
  }
  document.write('<div class="checkout-notice"><div class="icon-space">&nbsp;</div><h1>Platform dependencies</h1>' +
                 '<p>Please note that not all products work on all platforms ( <img align="absmiddle" src="media/gxp.gif" />=Windows, ' +
                 '<img align="absmiddle" src="media/gmac.gif" />=Mac).</p></div>');
}

/****************************************************
* ProductPanel - Panel Manager Class
*****************************************************/

// collects references of all existing panels
var panelA = new Array();

function renderPanels() {
  // renders all panels in the list
  for (var i in panelA)
    panelA[i].render();
}

// titles of checkbox
cbt_enabled  = 'Click here to add/remove this product from your order';
cbt_disabled = 'You already own this product';

// title for rest of titlebar
ttb_text     = 'Click here to show/hide additional product information';

function ProductPanel(ID) {
  // panel indentificators
  this.pID = ID;
  this.info = dbase.index[ID];
  this.info.panel = this;
  this.index = panelA.length;

  // push refr for enumerating
  panelA[panelA.length] = this; // IE 5.0 doesnt have Array.push()

  document.write('<div id="ph' + ID + '"></div>'); // here content gets rendered on load
  this.placeHolder = document.getElementById('ph' + ID);

  // state flags
  this.isExpanded = this.isChecked = this.info.ordered;
  this.titleRO = false;
  this.checkBoxRO = false;

  this.c = 0; // counter in transitions
}

function formatCash(amt) { return 'US' + (amt < 0? '-': '') + '$<strong>' + Math.abs(amt) + '</strong>'; }

function varpriceChanged(event, input, panel) {
  if (/^[0-9]*$/.test(input.value)) {
    input.validated = input.value;
  } else {
    input.value = (input.validated == null)? input.defaultValue: input.validated;
  }
  panel.info.price = input.value.length? parseInt(input.value, 10): 0;
  if (input.value != input.defaultValue) {
    panel.isChecked = false;
    panel.toggleCheckBox(event);
  }
}

ProductPanel.prototype.render = function () {
  if (pageType == "checkOut" && (!this.info.ordered))
    return; // in checkout pages, only ordered products render

  var ID = this.pID;

  // generate the panel code

  // detemine the checkbox startup image
  var cb_img = this.info.ordered? 'yes': 'no';
  var cbt_bundle = null;
  var bundle_name = null;
  this.info.bundled = false;
  if (this.info.purchased) {
    cb_img = 'dis';
  } else if (this.info.bundles) {
    for (bundle in this.info.bundles) {
      if (dbase.index[bundle].ordered) {
        this.info.bundled = true;
        cb_img = 'bundle';
        cbt_bundle = 'This product is already included with your bundle purchase. To purchase it separately, uncheck &quot;' +
                     (bundle_name = dbase.index[bundle].name) +
                     '&quot; from your order.';
        break;
      }
    }
  }
  var cont = '';
  cont += '<div title="' + ttb_text + '" class="panel-header" id="' + ID + '_header" ' +
          'onmouseover="panelA[' + this.index + '].tbRO(event, true)" onmouseout="panelA[' + this.index + '].tbRO(event, false)" ' +
          'onclick="panelA[' + this.index + '].toggleContent(event)">';
  if (pageType == 'shop') {
    var price;
    if (this.info.varprice) {
      price = 'US$<input class="varprice" type="text" name="amount[' + this.info.id + ']" value="' + this.info.price + '" onclick="this.focus(); cancelBubble(event);" onkeyup="varpriceChanged(event, this, panelA[' + this.index + '])">';
    } else {
      price = formatCash(this.info.price);
    }
    cont += '<div class="panel-headerprice">' + price + '&nbsp;&nbsp;&nbsp;&nbsp;</div>' + // new abs pos price;
            '<a title="' + (this.info.purchased? cbt_disabled: (cbt_bundle? cbt_bundle: cbt_enabled)) + '" ' +
            'class="panel-checkbox" href="javascript:void(0);" ' +
            'onmouseover="this.className = \'panel-checkbox-ro\'; panelA[' + this.index + '].cbRO(event, true)" ' +
            'onmouseout="this.className = \'panel-checkbox\'; panelA[' + this.index + '].cbRO(event, false)" ' +
            'onclick="panelA[' + this.index + '].toggleCheckBox(event)">' +
            '<img src="media/_cb_' + cb_img + '.gif" id="' + ID + '_cbGlyph" />' +
            '<span' + (this.info.purchased? ' style="color:#DDDDDD"': '') + ' class="title' + (isIE50? ('-ie'): ('')) + '">' +
            this.info.name +

            ((this.info.pc || this.info.mac)?
              ' (' +
              (this.info.pc? '<img src="media/xp.gif" />': '') +
              (this.info.mac? '<img src="media/mac.gif" />': '') +
              ')':
              '') +

    //      this.info.reduction == 'none')? '*': '' +
            '</span>' +
            '</a>';
  } else {
    var newPrice = Math.ceil(this.info.price * (100 - orderInfo.reduction) / 100); // discounted price
    cont += '<div class="panel-headerprice">' + formatCash((this.info.reduction == 'none')? this.info.price: newPrice) +
            '&nbsp;&nbsp;&nbsp;&nbsp;</div>' + // new abs pos price;
            '<a title="' + cbt_enabled + '" class="panel-checkbox" href="javascript:void(0);" ' +
            'onmouseover="panelA[' + this.index + '].tbRO(event, true)" onmouseout="panelA[' + this.index + '].tbRO(event, false)" ' +
            'onclick="panelA[' + this.index + '].toggleContent(event)">' +
            '<span class="title-checkout">' + this.info.name +

            ((this.info.pc || this.info.mac)?
              ' (' +
              (this.info.pc? '<img src="media/xp.gif" />': '') +
              (this.info.mac? '<img src="media/mac.gif" />': '') +
              ')':
              '') +

            ((this.info.reduction == 'none' || orderInfo.reduction == 0)? '': (' <strong>[-' + orderInfo.reduction + '%]</strong>')) + '</span>' +
            '</a>';
  }
  cont += '</div>';
  if (!isIE50) { // body + wrapper
    cont += '<div class="panel-body-wrapper" id="' + ID + '_bodywrapper">' +
            '<div class="panel-body" id="' + ID + '_body">';
  } else {
    // alternative body for IE50
    cont += '<table cellspacing="0" cellpadding="5" border="0" style="margin:0px 0px 0px 8px;"><tr>' +
            '<td class="panel-bodyie" background="media/_panel_bg.gif"' +
            'width="318" valign="top" id="' + ID + '_body"><div align="left">';
  }
  // content start
  if (ID in dbase.bundles) {
    var normal_total = 0;
    var reduction_total = 0;
    var bundle_total = 0;
    var bundle_info = '<table id="bundled_products" width="100%"><tr><th style="text-align:left">Product</th><th style="text-align:right">Normal</th><th style="text-align:right">You pay</th></tr>';
    for (var i = 0; i < dbase.bundles[ID].products.length; i++) {
      var p = dbase.index[dbase.bundles[ID].products[i]];
      var price_decoration = "normal";
      if (p.related) {
        bundle_info += '<tr><td style="text-align:left">' + p.name + '</td><td style="text-align:right">$' + p.price + '</td>';
        normal_total += p.price;
        var bundled_price = getBundleProductPrice(p, ID);
        if (p.purchased) {
          price_decoration = "line-through";
        }
        else {
          bundle_total += bundled_price;
        }
        reduction_total += p.price - bundled_price;

        bundle_info += '<td style="text-align:right;text-decoration:' + price_decoration + '">$' + bundled_price + '</td></tr>';
      }
    }
    bundle_info += '<td style="border-top: thin dashed;text-align:left">Total</td><td style="border-top: thin dashed;text-align:right">$' + normal_total + '</td><td style="border-top: thin dashed;text-align:right">$' + bundle_total + '</td></tr>';
    bundle_info += '</table>';

    this.info.description += bundle_info;
  }

  cont += 
  // '<div class="details">' +
  // '<p><strong>Standalone/Plugin: </strong>' + (this.info.plugin? 'Plugin': 'Standalone') + '</p>' +
  // '<p><strong>Type: </strong>' + this.info.type + '</p>' +
  // '<p><strong>LifeTime Free Updates: </strong>' + (this.info.ltupdates? 'Yes': 'No') + '</p>' +
  // '</div>' +
            '<div class="details">' +
            '<div>' + this.info.description + '</div>';
  if (this.info.url != 'none')
    cont += '<p style="padding-top:10px;font-size:10px;"><a target="_new" href=' + this.info.url + '>More Info...</a></p>';
  if ((pageType == 'shop' || pageType == 'checkOut') && (this.info.purchased)) {
    cont += '<p style="text-align:center;padding-top:10px;font-weight:bold;font-size:14px;">YOU ALREADY OWN IT</p>';
  }
  cont += '</div>';

//  // type message : part of bundle / you already own
//  if (this.info.purchased) {
//    cont += '<div style="clear:both; border-top:1px solid black; font-size: 13px; font-weight:bold; text-align:middle; padding-bottom:8px;">' +
//            'You already own this product.' +
//            '</div>';
//  } else
  if (bundle_name && !this.info.purchased) {
    cont += '<div style="clear:both; border-top:1px solid black; font-size: 13px; font-weight:bold; text-align:center; padding-bottom:8px;">' +
            'Part of ' + bundle_name + '.' +
            '</div>';
  }
  // ------------

  // content end
  if (!isIE50)
    cont += '</div></div>'; // closing of body+wrapper
  else {
    cont += '</div></td></tr></table>' +
            '<img style="margin-left:8px" src="media/_panel_bottom_ie50.gif">'; // alternative bottom cap for IE50;
  }
  cont += '<div class="panel-spacer"></div>';
  this.placeHolder.innerHTML = cont;

  // element references
  if (!isIE50)
    this.wrapper = document.getElementById(ID + '_bodywrapper');
  this.body = document.getElementById(ID+'_body');
  this.titlebar = document.getElementById(ID + '_header');
  this.cbGlyph = document.getElementById(ID + '_cbGlyph');

  // fetch height and hide (unless should be expanded)
  if (!isIE50) {
    this.bodyHeight = this.body.offsetHeight;
    if (this.isExpanded)
      this.wrapper.style.height = this.bodyHeight + 'px';
    else
      this.wrapper.style.height = "2px";
  } else {
    if (this.isExpanded)
      this.body.style.display = "block";
    else
      this.body.style.display = "none";
  }
}

ProductPanel.prototype.toggleCheckBox = function (evt) {
  if (this.info.bundles) {
    for (var bundle in this.info.bundles) {
      if (dbase.index[bundle].ordered) {
        alert('This product is already included with your ' + dbase.index[bundle].name + ' purchase. To purchase it separately, uncheck ' + dbase.index[bundle].name + ' from your order.');
        return cancelBubble(evt);
      }
    }
  }
  if (!this.info.purchased) {
    if (!this.isChecked && !this.isExpanded) {
      this.toggleContent();
    }
    this.isChecked = !this.isChecked;
    this.info.ordered = !this.info.ordered;
    this.cbDrawState();
    onOrderChange(this);
    onSerialChange();
    if (this.info.ordered && !this.info.bundled) {
      window.shopState[this.info.id] = true;
    } else if (this.info.id in window.shopState) {
      window.shopState[this.info.id] = false;
    }
    updateShopState();
  }
  return cancelBubble(evt);
}

ProductPanel.prototype.toggleContent = function (evt) {
  // toggles expanded/shrinked states
  this.c = this.isExpanded? 100: 0;
  this.isExpanded = !this.isExpanded; // change mode (theres transition but we ignore that)

  this.tbDrawState();

  if (!isIE50) {
    // calculate it now, since in safari the earlier calculation gives funny results
    this.bodyHeight = this.body.offsetHeight;
    clearInterval(this.trnsTimerID);
    var parent = this;
    this.trnsTimerID = setInterval(function() { parent.onTrnsTimer() }, 20);
  } else {
    this.body.style.display = this.isExpanded? 'block': 'none';
  }

  return cancelBubble(evt);
}

ProductPanel.prototype.onTrnsTimer = function () {
  // a single step in the transition process

  // frameskip enabled now:
  if (!(this.c || (this.c == 100)) && this.transiting) {
    return;
  }

  this.transiting = true;
  this.wrapper.style.height = (2 + this.bodyHeight * (this.isExpanded? easeOut50[this.c += 10]: easeIn[this.c -= 10])) + 'px';

  if (this.isExpanded? (this.c >= 100): (this.c <= 0))
    clearInterval(this.trnsTimerID);

  this.transiting = false;
}

ProductPanel.prototype.tbRO = function (evt, isRO) {
  if (!this.info.bundles) {
    this.titleRO = isRO;
    this.tbDrawState();
  }
  return cancelBubble(evt);
};

ProductPanel.prototype.cbRO = function (evt, isRO) {
  if (!this.info.bundles) {
    this.checkBoxRO = isRO;
    this.cbDrawState();
  }
  return cancelBubble(evt);
};

ProductPanel.prototype.tbDrawState = function () {
  // update the titlebar bg graphics acording to state flags
  this.titlebar.style.backgroundPosition = this.isExpanded? (this.titleRO? '0px -48px': '0px 0px'): (this.titleRO? '0px -72px': '0px -24px');
}

ProductPanel.prototype.cbDrawState = function () {
  // update the checkbox graphics acording to state flags
  if (!this.info.purchased)
    this.cbGlyph.src = (this.isChecked? (this.checkBoxRO? cb_yes_ro.src: cb_yes.src): (this.checkBoxRO? cb_no_ro.src: cb_no.src));
}

/****************************************************
* calculates/renders the reduction and order info
*****************************************************/

orderInfo = {};
orderInfo.updateTimer = null;
orderInfo.updateCounter = 0;
orderInfo.cost = 0; // without reduction
orderInfo.finalCost = 0; // with reduction
orderInfo.reduction = 0; // percent
// transition related (temporary)
orderInfo.cBarPos = 0;
orderInfo.tBarPos = 0;
orderInfo.cCost = 0;
orderInfo.tCost = 0;
orderInfo.cFinalCost = 0;
orderInfo.tFinalCost = 0;
orderInfo.cReduction = 0;
orderInfo.tReduction = 0;

function onOrderChange(sender) { // event handler for checkbox check/uncheck
  // check bundle compat
  if (sender && (sender.info.id in dbase.bundles)) {
    for (var i = 0; i < panelA.length; i++) {
      if (panelA[i].info.bundles && (sender.info.id in panelA[i].info.bundles)) {
        panelA[i].isChecked = false;
        panelA[i].info.ordered = false;
        panelA[i].render();
      }
    }
  }

  // check if sender is incompatible with any other ordered products
  if (sender && sender.info.ordered) {
    var relIncompatible = sender.info.relIncompatible;
    var riChecked = [];
    var riCheckedNames = '';
    for (var i in relIncompatible) {
      if (dbase.index[relIncompatible[i]].ordered && relIncompatible[i] != sender.info.id) {
        riChecked[riChecked.length] = dbase.index[relIncompatible[i]];
        riCheckedNames += ', "' + dbase.index[relIncompatible[i]].name + '"';
      }
    }
    if (riChecked.length > 0) {
      riCheckedNames = riCheckedNames.substr(2); // strip starting ", "
      if (confirm('The products "' + sender.info.name + '" and ' + (riChecked.length == 1? '': 'the following products:') + riCheckedNames + ' can not be purchased together. Click "Ok" to confirm purchase of "' + sender.info.name + '" and deselect the incompatible product' + (riChecked.length == 1? '': 's') + ', or "Cancel" to ' + (riChecked.length == 1? 'keep ' + riCheckedNames: 'cancel your current action'))) {
        // have to match codes to uncheck against the panels
        for (var i = 0; i < panelA.length; i++) {
          for (var j = 0; j < riChecked.length; j++) {
            if (panelA[i].info.id == riChecked[j].id) {
              panelA[i].toggleCheckBox();
            }
          }
        }
      } else {
        sender.toggleCheckBox();
      }
    }
  }

  // check if sender is equivalent with any other ordered products
  if (sender && sender.info.ordered) {
    var relEquivalent = sender.info.relEquivalent;
    var riChecked = [];
    var riCheckedNames = '';
    for (var i in relEquivalent) {
      if (dbase.index[relEquivalent[i]].ordered && relEquivalent[i] != sender.info.id) {
        riChecked[riChecked.length] = dbase.index[relEquivalent[i]];
        riCheckedNames += ', "' + dbase.index[relEquivalent[i]].name + '"';
      }
    }
    if (riChecked.length > 0) {
      riCheckedNames = riCheckedNames.substr(2); // strip starting ", "
      if (dbase.bundles[sender.info.id]) {
        var msg = 'You have already ordered: ' + riCheckedNames + ', which is a part of "' + sender.info.name + '". You need to order only one of those products: click "Ok" to select "' + sender.info.name + '" and deselect the other product' + (riChecked.length == 1? '': 's') + ', or "Cancel" to ' + (riChecked.length == 1? "keep " + riCheckedNames: 'cancel your current action');
      } else {
        var msg = 'You have already ordered: ' + riCheckedNames + ', which entitles you to a free license for "' + sender.info.name + '". You need to order only one of those products: click "Ok" to select "' + sender.info.name + '" and deselect the other product' + (riChecked.length == 1? '': 's') + ', or "Cancel" to ' + (riChecked.length == 1? 'keep ' + riCheckedNames: 'cancel your current action');
      }
      if (confirm(msg)) {
        // have to match codes to uncheck against the panels
        for (var i = 0; i < panelA.length; i++) {
          for (var j = 0; j < riChecked.length; j++) {
            if (panelA[i].info.id == riChecked[j].id) {
              panelA[i].toggleCheckBox();
            }
          }
        }
      } else {
        sender.toggleCheckBox();
      }
    }
  }

  var oi = orderInfo;
  var i;
//  var rb = document.getElementById('reduction-filled');

  // set current values for all animated objects
  oi.cFinalCost = oi.finalCost;
  oi.cCost = oi.cost;
  if (pageType == "shop") {
    //oi.cBarPos = rb.offsetWidth;
    oi.cReductionText = Math.floor(oi.cost - oi.finalCost);
  }

  // calc reduction & order cost
  oi.cost = 0;
  oi.reduction = 0;

  // count number of products to spread on
  var totalCount = 0;
  for (i in dbase.index) {
    if (dbase.index[i].related && dbase.index[i].reduction == "default") totalCount++;
    if (dbase.index[i].serial && currentStep == 5) { // check if serial product was added and no serial entered
      if (!formValidate(4, true)) {
        setStep(4);
      }
    }
  }

  var dCount = 0, dOrderCount = 0; // discounted count all / ordered only
  var nCount = 0, nOrderCount = 0; // non-discounted count all / ordered only
  var dPrice = new Array(); // discounted price array
  var nPrice = new Array(); // non-discounted price array

  for (i = 0; i < panelA.length; i++) {
    if (panelA[i].isChecked || panelA[i].info.purchased) { // if the page is checkout visible=ordered
      if (panelA[i].isChecked) oi.cost += panelA[i].info.price;
      if (panelA[i].info.reduction != 'none') {
        dCount++;
        if (panelA[i].isChecked) {
          dPrice[dPrice.length] = panelA[i].info.price;
          dOrderCount++;
        }
      } else {
        nCount++;
        if (panelA[i].isChecked) {
          nPrice[nPrice.length] = panelA[i].info.price;
          nOrderCount++;
        }
      }
    }
  }

  var orderTotals = orderFormula(totalCount, dCount, nCount, dPrice, nPrice); // replaces commented code below
  oi.finalCost = orderTotals.finalCost;
  oi.reduction = orderTotals.reduction;

  // global var with all products ordered & update orderButton
  productCount = dOrderCount + nOrderCount;
  if (pageType == "shop")
    updateOrderButton();

  if (oi.reduction != oi.reduction)
    oi.reduction = 0; // fix NaN bug (div by zero)

  // set target values for all animated objects
  oi.tFinalCost = oi.finalCost;
  oi.tCost = oi.cost;
  oi.tBarPos = 9 + Math.ceil(7.16 * oi.reduction);
  oi.tReductionText = oi.tCost - oi.tFinalCost;
  if ((pageType == "shop") && (oi.cCost != oi.tCost)) {
    // the shop order details exist only on the shop page
    // also only update the cost when currently displayed is different
    if (!isIE50) {
      clearInterval(oi.updateTimer);
      oi.updateCounter = 0;
      oi.updateTimer = setInterval(orderAnimate, 20);
    } else {
      // update in a single step for IE50
      document.getElementById('reduction-filled').style.width = oi.tBarPos + 'px';
      // write order details
      var mc = 1; // mapped counter
      changeContent('odTotalCost', formatCurrency(map(mc, oi.cCost, oi.tCost)));
      changeContent('odReduction', formatCurrency(map(mc, oi.cReductionText, oi.tReductionText)));
      changeContent('odFinalCost', formatCurrency(map(mc, oi.cFinalCost, oi.tFinalCost)));
    }
  }

  if (pageType == "shop")
    // updates order notes if VCASH payment is selected
    renderVCashNotes();
}

function updateOrderButton() {
  // disable order button if the form step is not last or no products are ordered
  var payCombo = document.getElementById('ec_PaymentMethod');
  orderButton.setEnabled((pageType == 'checkOut')? vCashOk: (productCount && (currentStep == 5) && (payCombo.selectedIndex > 0) && vCashOk));
}

function map(i, c, t) {
  return Math.round(i * t + (1 - i) * c);
}

function orderAnimate() {
  var oi = orderInfo;
  var mc = easeOut20[oi.updateCounter += 10]; // mapped counter

//  var rb = document.getElementById('reduction-filled');
//  rb.style.width = map(mc, oi.cBarPos, oi.tBarPos) + 'px';

  // write order details
  changeContent('odTotalCost', formatCurrency(map(mc, oi.cCost, oi.tCost)));
  changeContent('odReduction', formatCurrency(map(mc, oi.cReductionText, oi.tReductionText)));
  changeContent('odFinalCost', formatCurrency(map(mc, oi.cFinalCost, oi.tFinalCost)));

  if (oi.updateCounter >= 100)
    clearInterval(oi.updateTimer);
}

// product panel toolbar functions
function toggleAll(flag) {
  for (var i = 0; i < panelA.length; i++)
    if (panelA[i].isExpanded != flag)
      panelA[i].toggleContent();
}

function decodeCookie(input) {
  var i, cookie;
  var cookies = input.split(/; */);
  var output = {};
  for (i = 0; i < cookies.length; i++) {
    cookie = cookies[i].split('=');
    output[cookie.shift()] = decodeURIComponent(cookie.join('='));
  }
  return output;
}

function updateShopState() {
  var cookie = [];
  for (var id in window.shopState) {
    if (window.shopState[id]) cookie.push(id);
  }
  document.cookie = 'shopState=' + encodeURIComponent(cookie.join(':'));
}

window.shopState = {};
var cookies = decodeCookie(document.cookie);
if ('shopState' in cookies) {
  // restore shop state
  var state = cookies.shopState.split(':');
  for (var i = 0; i < state.length; i++) {
    window.shopState[state[i]] = true;
  }
}
