  function setFactors (n) {
    // Set all factors to n
    if (n == "" || n == 0) {
	$("td[headers='FACTOR'] input").val("").hide()
	    .after("<span>&nbsp;</span>");
	$("td[headers='CHECKBOX'] input").attr('checked', false);
    } else {
	$("td[headers='FACTOR'] input").show().val(n)
	    .next("span").remove();
	$("td[headers='CHECKBOX'] input").attr('checked', true);
    }
    updateMutations();
  }

  function selectAllMembers () {
    $("td[headers='CHECKBOX'] input").attr('checked', true);
  }

  function selectNoMembers () {
    $("td[headers='CHECKBOX'] input").attr('checked', false);
  }
 
  function formatNumberForDisplay (number, decimals) {
    if (isNaN(number)) return "";

    if (decimals != null) {
      number = Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
    }

    var parts = number.toString().split(".");
    var integerPart = (parts[0]) ? parts[0] : "0";
    var decimalPart = (parts[1]) ? parts[1] : "";
    var reg = /(\d)(\d{3}$|\d{3}\.)/;

    while (reg.test(integerPart)) {
      integerPart = integerPart.replace(reg,'$1.$2');
    }

    if (decimals == null) {
      if (decimalPart == "") {
        var s = integerPart;
      } else {
        var s = integerPart + "," + decimalPart;
      }
    } else {
      while (decimalPart.length < decimals) {
        decimalPart = decimalPart + "0";
      }
      var s = integerPart + "," + decimalPart;
    }

    return s;
  }

  function formatNumberForEdit (number, decimals) {
    if (isNaN(number)) return "";

    if (decimals != null) {
      number = Math.round(number * Math.pow(10, decimals)) / Math.pow(10, decimals);
    }

    var s = number.toString().replace(".", ",")

    if (decimals != null) {
      if (! s.match(",")) s = s + ",";
      while (s.lastIndexOf(",") > s.length - decimals - 1) {
        s = s + "0";
      }
    }

    return s;
  }

  function parseFormattedNumber (s) {
    if (s == null) return null;

    var cleanNumber = s.replace(".", "").replace(",", ".");
    return parseFloat(cleanNumber);
  }

  function parseNumber (s) {
    if (s == null) return null;

    // Remove all non-relevant characters
    var cleanNumber = s.replace(/[^0-9.,]/g, "");
    var lastComma = cleanNumber.lastIndexOf(",");
    var lastPeriod = cleanNumber.lastIndexOf(".");
    var seperators = cleanNumber.match(/[.,]/g);
    if (seperators == null) {
      var numberOfSeperators = 0;
    } else {
      var numberOfSeperators = seperators.length;
    }

    if (lastComma > 0 && lastPeriod > 0) {
      // Both comma and period present. Assume last one is decimal seperator.
      if (lastComma > lastPeriod) {
        // Comma is decimal seperator.
        // Clear periods
        cleanNumber = cleanNumber.replace(/\./g, "");
        // Replace commas by periods.
        cleanNumber = cleanNumber.replace(/,/g, ".");
      } else {
        // Period is decimal seperator.
        // Clear commas
        cleanNumber = cleanNumber.replace(/,/g, "");
      }
    } else if (numberOfSeperators > 1) {
      // More than one comma or period occurences.
      // Might be thousand seperators.
      // Clear all periods and commas.
      cleanNumber = cleanNumber.replace(/[.,]/g, "");
    } else if (cleanNumber.match(/^[1-9]\d?[.,]\d{3}$/)) {
      // Only one thousand seperator.
      // Clear all periods and commas.
      cleanNumber = cleanNumber.replace(/[.,]/g, "");
    } else {
      // One comma or period, no thousand seperator.
      // Replace comma by period.
      cleanNumber = cleanNumber.replace(/[,]/g, ".");
    }

    return parseFloat(cleanNumber);
  }

  function sumFactors () {
    var curr_id;
    var currentValue;
    var sum = 0;

    $("td[headers='FACTOR'] > input").each(function () {
      currentValue = parseNumber($(this).val());
      if (! isNaN(currentValue))
        sum = sum + currentValue;
    });

    return sum;
  }

  function sumCredits () {
    var curr_id;
    var currentValue;
    var sum = 0;

    $("td[headers='AMOUNT'] > input").each(function () {
      currentValue = parseNumber($(this).val());
      if (! isNaN(currentValue))
        sum = sum + currentValue;
    });

    return sum;
  }

  function sumDebits () {
    var curr_id;
    var currentValue;
    var sum = 0;

    $("td[headers='DEBIT'] > input").each(function () {
      currentValue = parseNumber($(this).val());
      if (! isNaN(currentValue))
        sum = sum + currentValue;
    });

    return sum;
  }

  function sumDifferences () {
    var tr;
    var credit;
    var debit;
    var sum = 0;

    $("label:contains('USER_ID')").next("input").each(function () {
      tr = $(this).parent().parent();

      credit = parseNumber(tr.children("td[headers='AMOUNT']")
                           .children("input").val());
      if (isNaN(credit)) credit = 0;

      debit = parseNumber(tr.children("td[headers='DEBIT']")
                           .children("input").val());
      if (isNaN(debit)) debit = 0;

      sum = sum + Math.abs(credit - debit);
    });

    return sum;
  }

  function allMutationsZero () {
    var curr_id;
    var currentValue;
    var allZero = true;

    $("td[headers='MUTATION'] > span").each(function () {
      currentValue = parseNumber($(this).text());
      if (! isNaN(currentValue) && currentValue != 0)
        allZero = false;
    });

    return allZero;
  }

  function disableFields() {
    var factor_id;
    var credit_id;
    var factor_privilege_id;
    var amount_privilege_id;

    $("label:contains('AMOUNT_PRIVILEGE')").next("input").each(function () {
      amountPrivilege = ($(this).val() == 'Y');
      tr = $(this).parent().parent();

      if ($("input#P4_TRANSACTION_TYPE").val() == 'USEREXPENSE' ||
          $("input#P4_TRANSACTION_TYPE").val() == 'BANK' ||
          ! amountPrivilege) {
        tr.children("td[headers='AMOUNT']").children("input").hide()
          .after("<span>&nbsp;</span>");
      }

    });
  }

  function enableAllFields() {
    var factor_id;
    var credit_id;

    $("input#P4_AMOUNT")
      .add("td[headers='AMOUNT'] > input")
      .add("td[headers='DEBIT'] > input")
      .each(function () {
      $(this).val(formatNumberForEdit (parseNumber ($(this).val()),2));
    });

    $("td[headers='FACTOR'] > input")
      .each(function () {
      $(this).val(formatNumberForEdit (parseNumber ($(this).val())));
    });

  }

  function updateMutations () {
    if ($("input#P4_TRANSACTION_TYPE").val() == 'START') {
      updateStartBalances();
    } else {
      updateNormalMutations ()
    }
  }

  function updateStartBalances() { // TODO !!!
    var sumOfCredits = sumCredits();
    var sumOfDebits = sumDebits();
    var totalRemainder = sumOfCredits - sumOfDebits;
    var remainderFactor;
    var credit;
    var debit;
    var correction;
    var mutation;

    $("label:contains('USER_ID')").next("input").each(function () {
      tr = $(this).parent().parent();

      credit = parseNumber(tr.children("td[headers='AMOUNT']")
                           .children("input").val());
      if (isNaN(credit)) credit = 0;

      debit = parseNumber(tr.children("td[headers='DEBIT']")
                           .children("input").val());
      if (isNaN(debit)) debit = 0;

      remainderFactor = Math.abs(credit - debit) / sumDifferences();

      correction = - remainderFactor * totalRemainder;

      mutation = credit - debit + correction;

      tr.children("td[headers='REMAINDER']")
        .children("span")
        .html((correction != 0)
              ? (correction > 0 ? "+" : "") +
                formatNumberForDisplay (correction, 2)
              : "&nbsp;");

      tr.children("td[headers='REMAINDER']")
        .children("span")
        .css("color", correction < 0 ? "#C00000" : "#000000");

      tr.children("td[headers='MUTATION']")
        .children("span")
        .html((mutation != 0)
              ? (mutation > 0 ? "+" : "") +
                formatNumberForDisplay (mutation, 2)
              : "&nbsp;")
        .css("font-weight", "bold");

      tr.children("td[headers='MUTATION']")
        .children("span")
        .css("color", mutation < 0 ? "#C00000" : "#000000");

    });
  }

  function updateNormalMutations () {
    var sumOfFactors = sumFactors();
    var sumOfCredits = sumCredits();

    if ($("input#P4_TRANSACTION_TYPE").val() == 'BANK') {
      var amountSign = parseInt($("select#P4_AMOUNT_SIGN").val());
      if (isNaN(amountSign)) amountSign = 0;
    }

    if ($("input#P4_TRANSACTION_TYPE").val() == 'USEREXPENSE' ||
        $("input#P4_TRANSACTION_TYPE").val() == 'BANK') {
      var amount = parseNumber($("input#P4_AMOUNT").val());
      if (isNaN(amount)) amount = 0;
    }

    var userId;
    var tr;
    var factor;
    var debit;
    var credit;

    $("label:contains('USER_ID')").next("input").each(function () {
      userId = $(this).val();
      tr = $(this).parent().parent();

      factor = parseNumber(tr.children("td[headers='FACTOR']")
                           .children("input").val());
      if (isNaN(factor)) factor = 0;

      if (sumOfFactors == 0) {
        factor_pct = 0;
      } else {
        factor_pct = factor / sumOfFactors;
      }

      credit = parseNumber(tr.children("td[headers='AMOUNT']")
                           .children("input").val());
      if (isNaN(credit)) credit = 0;

      if ($("input#P4_TRANSACTION_TYPE").val() == 'USEREXPENSE') {
        if (userId == $("input#P4_USER_ID").val()) {
          credit = amount;
        }
        debit = factor_pct * amount;
      } else if ($("input#P4_TRANSACTION_TYPE").val() == 'EXPENSE') {
        debit = factor_pct * sumOfCredits;
      } else if ($("input#P4_TRANSACTION_TYPE").val() == 'BANK') {
        if (amountSign == -1) {
          credit = factor_pct * amount;
          debit = '';
        } else if (amountSign == 1) {
          credit = '';
          debit = factor_pct * amount;
        } else {
          credit = '';
          debit = '';
        }
      }

      mutation = credit - debit;

      tr.children("td[headers='FACTOR_PCT']")
        .children("span")
        .html(factor_pct > 0
              ? formatNumberForDisplay (factor_pct * 100, 1) + "%"
              : "&nbsp;");

      if ($("input#P4_TRANSACTION_TYPE").val() == 'USEREXPENSE' ||
          $("input#P4_TRANSACTION_TYPE").val() == 'BANK') {
        tr.children("td[headers='AMOUNT']")
          .children("span")
          .html(credit != 0
                ? formatNumberForDisplay (credit, 2)
                : "&nbsp;");
      }

      tr.children("td[headers='DEBIT']")
        .children("span")
        .html(debit != 0
              ? formatNumberForDisplay (debit, 2)
              : "&nbsp;");

      tr.children("td[headers='MUTATION']")
        .children("span")
        .html((credit != 0 || debit != 0)
              ? (mutation > 0 ? "+" : "") +
                formatNumberForDisplay (mutation, 2)
              : "&nbsp;");
      tr.children("td[headers='MUTATION']")
        .children("span")
        .css("color", mutation < 0 ? "#C00000" : "#000000");
    });
  }

function handleCheckbox(box) {
    if (box.attr("checked")) {
	box.parent("td").nextAll("td[headers='FACTOR']")
	    .children("input").show().val("1").next("span")
	    .remove();
    } else {
	box.parent("td").nextAll("td[headers='FACTOR']")
	    .children("input").val("").hide()
	    .after("<span>&nbsp;</span>");
    }
    updateMutations();
}

  function transactionDocumentReady () {
    disableFields();
    updateMutations();

    $(".date-picker").datepicker({
      yearRange: "-10:+1",
      changeMonth: true,
      changeYear: true,
      showOtherMonths: true,
      showOn: 'both',
      dateFormat: 'dd-mm-yy',
      buttonText: '',
      buttonImage: '/i/date.gif',
      buttonImageOnly: true,
      weekHeader: 'Wk',
      showWeeks: true,
      firstDay: 1,
      monthNamesShort: ['Januari','Februari','Maart','April','Mei','Juni',
 'Juli','Augustus','September','Oktober','November','December']
    });

    $("td[headers='CHECKBOX']").width(13);
    $("td[headers='USER_NAME']").width(143);
    $("td[headers='FACTOR']").width(64);
    $("td[headers='FACTOR_PCT']").width(42);
    $("td[headers='PLUS_SIGN']").width(9);
    $("td[headers='AMOUNT']").width(68);
    $("td[headers='MINUS_SIGN']").width(19);
    $("td[headers='DEBIT']").width(62);
    $("td[headers='REMAINDER_SIGN']").width(19);
    $("td[headers='REMAINDER']").width(62);
    $("td[headers='EQUALS_SIGN']").width(22);
    $("td[headers='MUTATION']").width(65);

    $("td[headers='MINUS_SIGN'], td[headers='REMAINDER_SIGN'], td[headers='EQUALS_SIGN']")
      .css({'padding': '0'});
    $("td[headers='PLUS_SIGN']").css({'padding-right': '0'});

    $("td[headers='USER_NAME']") //.add("th#USER_NAME")
      .add("td[headers='FACTOR_PCT']") //.add("th#FACTOR_PCT")
      .css({
      'border-right': '1px dotted #666666'
    });

    $("th").css('white-space', 'nowrap');

    $("th#FACTOR").attr('align', 'center');

    $("th#AMOUNT").attr('colSpan', 2).prev("th").remove();

    $("input#P4_AMOUNT").add("td[headers='AMOUNT'] input").add("td[headers='DEBIT'] input")
      .focus(function () {
        $(this).val(formatNumberForEdit (parseNumber($(this).val()), 2));
      }).blur(function () {
        $(this).val(formatNumberForDisplay (parseNumber($(this).val()), 2));
        updateMutations();
      }).keyup(function () {
        updateMutations();
      });

    $("td[headers='FACTOR'] input")
      .focus(function () {
	$(this).val(formatNumberForEdit (parseFormattedNumber($(this).val())));
      }).blur(function () {
	$(this).val(formatNumberForDisplay (parseNumber($(this).val())));
	if ($(this).val() == "0") $(this).val("");
	if ($(this).val() == "") {
	    $(this).parent("td").prevAll("td[headers='CHECKBOX']")
		.children("input").attr('checked', false);
	    $(this).hide().after("<span>&nbsp;</span>");
	}
	updateMutations();
      }).keyup(function () {
        updateMutations();
      });

    $("td[headers='CHECKBOX']")
	.css("line-height", "0")
	.css("padding-top", "4px")
	.css("padding-bottom", "5px")
	.css("vertical-align", "middle");

    $("td[headers='CHECKBOX'] input")
	.css("margin", "0")
	.css("padding", "0")
	.click(function() {
		handleCheckbox($(this));
	    });

    $("td[headers='USER_NAME']").click(function() {
	    box = $(this).prevAll("td[headers='CHECKBOX']").children("input");
	    box.attr('checked', ! box.attr('checked'));
	    handleCheckbox(box);
	});

//    $("input:text").blur();

  }

