<commit_before>///<reference path="jquery-1.6.2-vsdoc.js"/>

//Self-Executing Anonymous Function
(function (purchasing, $, undefined) {
    //Private Property
    var options = { invalidNumberClass: "invalid-number-warning", a: true, b: false };

    //Public Property
    purchasing.splitType = "None"; //Keep track of current split [None,Order,Line]

    //Public Method
    purchasing.options = function (o) {
        $.extend(options, o);
        console.log(options);
    };

    //
    purchasing.init = function () {
        $(".button").button();

        attachVendorEvents();
        attachAddressEvents();
        attachLineItemEvents();
        attachSplitOrderEvents();
        attachSplitLineEvents();
    };

    //Private method
    function attachVendorEvents() {
        $("#vendor-dialog").dialog({
            autoOpen: false,
            height: 500,
            width: 500,
            modal: true,
            buttons: {
                "Create Vendor": function () { createVendor(this); },
                "Cancel": function () { $(this).dialog("close"); }
            }
        });

        $("#add-vendor").click(function (e) {
            e.preventDefault();

            $("#vendor-dialog").dialog("open");
        });

        function createVendor(dialog) {
            var form = $("#vendor-form");

            if (form.validate().form() == false) {
                return; //don't create the vendor if the form is invalid
            }

            var vendorInfo = {
                name: form.find("#vendor-name").val(),
                address: form.find("#vendor-address").val(),
                city: form.find("#vendor-city").val(),
                state: form.find("#vendor-state").val(),
                zip: form.find(("#vendor-zip")).val(),
                countryCode: form.find("#vendor-country-code").val()
            };

            $.post(options.AddVendorUrl, vendorInfo, function (data) {
                var vendors = $("#vendors");
                //removing existing selected options
                vendors.find("option:selected").removeAttr("selected");

                //Get back the id & add into the vendor select
                var newAddressOption = $("<option>", { selected: 'selected', value: data.id }).html(vendorInfo.name);
                vendors.append(newAddressOption);
            });

            $(dialog).dialog("close");
        }
    }

    function attachAddressEvents() {
        $("#address-dialog").dialog({
            autoOpen: false,
            height: 500,
            width: 500,
            modal: true,
            buttons: {
                "Create Shipping Address": function () { createAddress(this); },
                "Cancel": function () { $(this).dialog("close"); }
            }
        });

        $("#add-address").click(function (e) {
            e.preventDefault();

            $("#address-dialog").dialog("open");
        });

        function createAddress(dialog) {
            var form = $("#address-form");

            var addressInfo = {
                name: form.find("#address-name").val(),
                building: form.find("#address-building").val(),
                room: form.find("#address-room").val(),
                address: form.find("#address-address").val(),
                city: form.find("#address-city").val(),
                state: form.find("#address-state").val(),
                zip: form.find(("#address-zip")).val(),
                phone: form.find(("#address-phone")).val()
            };

            $.post(options.AddAddressUrl, addressInfo, function (data) {
                var addresses = $("#addresses");
                //removing existing selected options
                addresses.find("option:selected").removeAttr("selected");

                //Get back the id & add into the select
                var newAddressOption = $("<option>", { selected: 'selected', value: data.id }).html(addressInfo.name);
                addresses.append(newAddressOption);
            });

            $(dialog).dialog("close");
        }
    }

    function attachLineItemEvents() {
        $("#add-line-item").click(function (e) {
            e.preventDefault();

            var newLineItem = $("#line-item-template").tmpl({}).prependTo("#line-items > tbody");

            newLineItem.find(".button").button();

            if (purchasing.splitType === "Line") {
                var lineItemSplitTemplate = $("#line-item-split-template");
                var newLineItemSplitTable = newLineItem.find(".sub-line-item-split-body");

                lineItemSplitTemplate.tmpl().appendTo(newLineItemSplitTable);
                lineItemSplitTemplate.tmpl().appendTo(newLineItemSplitTable);

                $(".line-item-splits").show();
            }
        });

        $(".toggle-line-item-details").live('click', function (e) {

            $(this).parents("tr").next().toggle();

            e.preventDefault();
        });

        $(".quantity, .price, #shipping, #tax", "#line-items").live("focus blur change keyup", function () {
            //First make sure the number is valid
            var el = $(this);

            purchasing.validateNumber(el);

            calculateSubTotal();
            calculateGrandTotal();
        });

        function calculateSubTotal() {
            var subTotal = 0;

            $(".line-item-row").each(function () {
                var row = $(this);
                var quantity = purchasing.cleanNumber(row.find(".quantity").val());
                var price = purchasing.cleanNumber(row.find(".price").val());

                var lineTotal = parseFloat(quantity) * parseFloat(price);

                if (!isNaN(lineTotal)) {
                    subTotal += lineTotal;
                    displayLineItemTotal(row, lineTotal);
                }
            });

            $("#subtotal").html("$" + subTotal.toFixed(2));
        }

        function calculateGrandTotal() {
            var subTotal = parseFloat(purchasing.cleanNumber($("#subtotal").html()));
            var shipping = parseFloat(purchasing.cleanNumber($("#shipping").val()));
            var tax = parseFloat(purchasing.cleanNumber($("#tax").val()));

            var grandTotal = (subTotal * (1 + tax / 100.00)) + shipping;

            if (!isNaN(grandTotal)) {
                displayGrandTotal(grandTotal);
            }
        }
    }

    function attachSplitOrderEvents() {
        $("#add-order-split").click(function (e) {
            e.preventDefault();

            $("#order-split-template").tmpl().prependTo("#order-splits");
        });

        $("#split-order").click(function (e) {
            e.preventDefault();

            if (confirm("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat")) {
                var splitTemplate = $("#order-split-template");
                splitTemplate.tmpl({}).appendTo("#order-splits");
                splitTemplate.tmpl({}).appendTo("#order-splits");
                splitTemplate.tmpl({}).appendTo("#order-splits");

                $("#order-split-total").html($("#grandtotal").html());

                $("#order-split-section").show();

                setSplitType("Order");
            }
        });

        $(".order-split-account-amount, .order-split-account-percent").live("focus blur change keyup", function (e) {
            e.preventDefault();
            var el = $(this);

            purchasing.validateNumber(el);

            if (el.hasClass(options.invalidNumberClass) == false) { //don't bother doing work on invalid numbers
                var total = purchasing.cleanNumber($("#order-split-total").html());
                var amount = 0, percent = 0;

                if (el.hasClass("order-split-account-amount")) { //update the percent
                    amount = purchasing.cleanNumber(el.val());

                    percent = (amount / total) * 100.0;

                    el.siblings(".order-split-account-percent").val(percent.toFixed(2));
                } else { //update the amount
                    percent = purchasing.cleanNumber(el.val());

                    amount = total * (percent / 100.0);

                    el.siblings(".order-split-account-amount").val(amount.toFixed(2));
                }

                calculateOrderAccountSplits();
            }
        });

        function calculateOrderAccountSplits() {
            var total = 0;

            $(".order-split-account-amount").each(function () {
                var amt = purchasing.cleanNumber(this.value);

                var lineTotal = parseFloat(amt);

                if (!isNaN(lineTotal)) {
                    total += lineTotal;
                }
            });

            var fixedTotal = total.toFixed(2);

            var accountTotal = $("#order-split-account-total");
            accountTotal.html("$" + fixedTotal);

            verifyAccountTotalEqualsGrandTotal(accountTotal);
        }
    }

    function attachSplitLineEvents() {
        $("#split-by-line").click(function (e) {
            e.preventDefault();

            if (confirm("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat")) {
                var lineItemSplitTemplate = $("#line-item-split-template");
                lineItemSplitTemplate.tmpl().appendTo(".sub-line-item-split-body");
                lineItemSplitTemplate.tmpl().appendTo(".sub-line-item-split-body");

                $(".line-item-splits").show();

                calculateSplitTotals();

                setSplitType("Line");
            }
        });

        $(".line-item-split-account-amount, .line-item-split-account-percent").live("focus blur change keyup", function (e) {
            e.preventDefault();
            var el = $(this);

            purchasing.validateNumber(el);

            if (el.hasClass(options.invalidNumberClass) == false) { //don't bother doing work on invalid numbers
                //find the total for this line
                var containingLineItemSplitTable = el.parentsUntil(".line-item-splits", ".sub-line-item-split");
                var total = purchasing.cleanNumber(containingLineItemSplitTable.find(".add-line-item-total").html());

                var amount = 0, percent = 0;

                if (el.hasClass("line-item-split-account-amount")) { //update the percent
                    amount = purchasing.cleanNumber(el.val());

                    percent = (amount / total) * 100.0;

                    el.parent().parent().find(".line-item-split-account-percent").val(percent.toFixed(2));
                } else { //update the amount
                    percent = purchasing.cleanNumber(el.val());

                    amount = total * (percent / 100.0);

                    el.parent().parent().find(".line-item-split-account-amount").val(amount.toFixed(2));
                }

                calculateLineItemAccountSplits();
            }
        });

        $(".add-line-item-split").live("click", function (e) {
            e.preventDefault();

            var containingFooter = $(this).parentsUntil("table.sub-line-item-split", "tfoot");
            var splitBody = containingFooter.prev();

            $("#line-item-split-template").tmpl().appendTo(splitBody);
        });

        function calculateLineItemAccountSplits() {
            $(".sub-line-item-split").each(function () {
                var currentLineItemSplitRow = $(this);
                var total = 0;

                currentLineItemSplitRow.find(".line-item-split-account-amount").each(function () {
                    var amt = purchasing.cleanNumber(this.value);

                    var lineTotal = parseFloat(amt);

                    if (!isNaN(lineTotal)) {
                        total += lineTotal;
                    }
                });
                console.log(total);

                var fixedTotal = total.toFixed(2);

                var accountTotal = currentLineItemSplitRow.find(".add-line-item-split-total");
                accountTotal.html("$" + fixedTotal);

                verifyAccountTotalEqualsLineItemTotal(currentLineItemSplitRow);
            });
        }

        function calculateSplitTotals() {
            $(".line-item-row").each(function () {
                var row = $(this);
                var quantity = purchasing.cleanNumber(row.find(".quantity").val());
                var price = purchasing.cleanNumber(row.find(".price").val());

                var lineTotal = parseFloat(quantity) * parseFloat(price);

                if (!isNaN(lineTotal)) { //place on sibling line total
                    displayLineItemTotal(row, lineTotal);
                }
            });
        }
    }

    function displayLineItemTotal(itemRow, total) {
        itemRow.next().next().find(".add-line-item-total").html("$" + total.toFixed(2));
    }

    function displayGrandTotal(total) {
        var formattedTotal = "$" + total.toFixed(2);
        $("#grandtotal").html(formattedTotal);

        if (purchasing.splitType === "Order") {
            $("#order-split-total").html(formattedTotal);
            verifyAccountTotalEqualsGrandTotal($("#order-split-account-total"));
        }
    }

    function verifyAccountTotalEqualsGrandTotal(accountTotal) {
        var grandTotal = $("#grandtotal");
        var difference = $("#order-split-account-difference");

        if (accountTotal.html() != grandTotal.html()) {
            accountTotal.addClass(options.invalidNumberClass);
        }
        else {
            accountTotal.removeClass(options.invalidNumberClass);
        }

        var totalDifference = purchasing.cleanNumber(grandTotal.html()) - purchasing.cleanNumber(accountTotal.html());

        if (totalDifference == 0) {
            difference.html("");
        } else {
            difference.html("($" + totalDifference.toFixed(2) + ")");
        }
    }

    function verifyAccountTotalEqualsLineItemTotal(lineItemSplitRow) {
        var splitTotal = lineItemSplitRow.find(".add-line-item-split-total");
        var lineItemTotal = lineItemSplitRow.find(".add-line-item-total");
        var lineItemDifference = lineItemSplitRow.find(".add-line-item-split-difference");

        if (splitTotal.html() != lineItemTotal.html()) {
            splitTotal.addClass(options.invalidNumberClass);
        }
        else {
            splitTotal.removeClass(options.invalidNumberClass);
        }

        var totalDifference = purchasing.cleanNumber(lineItemTotal.html()) - purchasing.cleanNumber(splitTotal.html());

        if (totalDifference == 0) {
            lineItemDifference.html("");
        }
        else {
            lineItemDifference.html("($" + totalDifference.toFixed(2) + ")");
        }
    }

    function setSplitType(split) {
        purchasing.splitType = split;

        if (split === "Order") {
            $("#order-account-section").addClass("ui-state-disabled"); //TODO: this is just fake disabled
            $(".line-item-splits").hide();
            $(".sub-line-item-split-body").empty(); //clear all line splits
        }
        else if (split === "Line") {
            $("#order-account-section").addClass("ui-state-disabled"); //TODO: this is just fake disabled
            $("#order-split-section").hide();
            $("#order-splits").empty();
        }
        else { //For moving back to "no split" (not implemented)
            $(".line-item-splits").hide(); //if any line splits are showing, hide them
            $("#order-split-section").hide();
            $("#order-account-section").removeClass("ui-state-disabled"); //TODO: this is just fake disabled
            $(".sub-line-item-split-body").empty(); //clear all line splits
            $("#order-splits").empty();
        }
    }

    purchasing.validateNumber = function (el) {
        //takes a jquery element & validates that it is a number
        var value = purchasing.cleanNumber(el.val());

        if (isNaN(value) && value != '') {
            el.addClass(options.invalidNumberClass); //TODO: return true/false and use that value instead of querying for class
        }
        else {
            el.removeClass(options.invalidNumberClass);
        }
    };

} (window.purchasing = window.purchasing || {}, jQuery));

//Adding a Public Property
purchasing.quantity = "12";

//Adding New Functionality to Purchasing
(function( purchasing, $, undefined ) {
    //Private Property
    var prop = "testing";

    //Public Method
    purchasing.cleanNumber = function(n) {
           // Assumes string input, removes all commas, dollar signs, percents and spaces
            var newValue = n.replace(",","");
            newValue = newValue.replace("$","");
            newValue = newValue.replace("%","");
            newValue = newValue.replace(/ /g,'');
            return newValue;
    };
} (window.purchasing = window.purchasing || {}, jQuery));
<commit_msg>added highlight to new line item being added<commit_after>///<reference path="jquery-1.6.2-vsdoc.js"/>

//Self-Executing Anonymous Function
(function (purchasing, $, undefined) {
    //Private Property
    var options = { invalidNumberClass: "invalid-number-warning", a: true, b: false };

    //Public Property
    purchasing.splitType = "None"; //Keep track of current split [None,Order,Line]

    //Public Method
    purchasing.options = function (o) {
        $.extend(options, o);
        console.log(options);
    };

    //
    purchasing.init = function () {
        $(".button").button();

        attachVendorEvents();
        attachAddressEvents();
        attachLineItemEvents();
        attachSplitOrderEvents();
        attachSplitLineEvents();
    };

    //Private method
    function attachVendorEvents() {
        $("#vendor-dialog").dialog({
            autoOpen: false,
            height: 500,
            width: 500,
            modal: true,
            buttons: {
                "Create Vendor": function () { createVendor(this); },
                "Cancel": function () { $(this).dialog("close"); }
            }
        });

        $("#add-vendor").click(function (e) {
            e.preventDefault();

            $("#vendor-dialog").dialog("open");
        });

        function createVendor(dialog) {
            var form = $("#vendor-form");

            if (form.validate().form() == false) {
                return; //don't create the vendor if the form is invalid
            }

            var vendorInfo = {
                name: form.find("#vendor-name").val(),
                address: form.find("#vendor-address").val(),
                city: form.find("#vendor-city").val(),
                state: form.find("#vendor-state").val(),
                zip: form.find(("#vendor-zip")).val(),
                countryCode: form.find("#vendor-country-code").val()
            };

            $.post(options.AddVendorUrl, vendorInfo, function (data) {
                var vendors = $("#vendors");
                //removing existing selected options
                vendors.find("option:selected").removeAttr("selected");

                //Get back the id & add into the vendor select
                var newAddressOption = $("<option>", { selected: 'selected', value: data.id }).html(vendorInfo.name);
                vendors.append(newAddressOption);
            });

            $(dialog).dialog("close");
        }
    }

    function attachAddressEvents() {
        $("#address-dialog").dialog({
            autoOpen: false,
            height: 500,
            width: 500,
            modal: true,
            buttons: {
                "Create Shipping Address": function () { createAddress(this); },
                "Cancel": function () { $(this).dialog("close"); }
            }
        });

        $("#add-address").click(function (e) {
            e.preventDefault();

            $("#address-dialog").dialog("open");
        });

        function createAddress(dialog) {
            var form = $("#address-form");

            var addressInfo = {
                name: form.find("#address-name").val(),
                building: form.find("#address-building").val(),
                room: form.find("#address-room").val(),
                address: form.find("#address-address").val(),
                city: form.find("#address-city").val(),
                state: form.find("#address-state").val(),
                zip: form.find(("#address-zip")).val(),
                phone: form.find(("#address-phone")).val()
            };

            $.post(options.AddAddressUrl, addressInfo, function (data) {
                var addresses = $("#addresses");
                //removing existing selected options
                addresses.find("option:selected").removeAttr("selected");

                //Get back the id & add into the select
                var newAddressOption = $("<option>", { selected: 'selected', value: data.id }).html(addressInfo.name);
                addresses.append(newAddressOption);
            });

            $(dialog).dialog("close");
        }
    }

    function attachLineItemEvents() {
        $("#add-line-item").click(function (e) {
            e.preventDefault();

            var newLineItem = $("#line-item-template").tmpl({}).prependTo("#line-items > tbody");
            newLineItem.find(".button").button();

            if (purchasing.splitType === "Line") {
                var lineItemSplitTemplate = $("#line-item-split-template");
                var newLineItemSplitTable = newLineItem.find(".sub-line-item-split-body");

                lineItemSplitTemplate.tmpl().appendTo(newLineItemSplitTable);
                lineItemSplitTemplate.tmpl().appendTo(newLineItemSplitTable);

                $(".line-item-splits").show();
            }
            newLineItem.effect('highlight', 5000);
        });

        $(".toggle-line-item-details").live('click', function (e) {

            $(this).parents("tr").next().toggle();

            e.preventDefault();
        });

        $(".quantity, .price, #shipping, #tax", "#line-items").live("focus blur change keyup", function () {
            //First make sure the number is valid
            var el = $(this);

            purchasing.validateNumber(el);

            calculateSubTotal();
            calculateGrandTotal();
        });

        function calculateSubTotal() {
            var subTotal = 0;

            $(".line-item-row").each(function () {
                var row = $(this);
                var quantity = purchasing.cleanNumber(row.find(".quantity").val());
                var price = purchasing.cleanNumber(row.find(".price").val());

                var lineTotal = parseFloat(quantity) * parseFloat(price);

                if (!isNaN(lineTotal)) {
                    subTotal += lineTotal;
                    displayLineItemTotal(row, lineTotal);
                }
            });

            $("#subtotal").html("$" + subTotal.toFixed(2));
        }

        function calculateGrandTotal() {
            var subTotal = parseFloat(purchasing.cleanNumber($("#subtotal").html()));
            var shipping = parseFloat(purchasing.cleanNumber($("#shipping").val()));
            var tax = parseFloat(purchasing.cleanNumber($("#tax").val()));

            var grandTotal = (subTotal * (1 + tax / 100.00)) + shipping;

            if (!isNaN(grandTotal)) {
                displayGrandTotal(grandTotal);
            }
        }
    }

    function attachSplitOrderEvents() {
        $("#add-order-split").click(function (e) {
            e.preventDefault();

            $("#order-split-template").tmpl().prependTo("#order-splits");
        });

        $("#split-order").click(function (e) {
            e.preventDefault();

            if (confirm("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat")) {
                var splitTemplate = $("#order-split-template");
                splitTemplate.tmpl({}).appendTo("#order-splits");
                splitTemplate.tmpl({}).appendTo("#order-splits");
                splitTemplate.tmpl({}).appendTo("#order-splits");

                $("#order-split-total").html($("#grandtotal").html());

                $("#order-split-section").show();

                setSplitType("Order");
            }
        });

        $(".order-split-account-amount, .order-split-account-percent").live("focus blur change keyup", function (e) {
            e.preventDefault();
            var el = $(this);

            purchasing.validateNumber(el);

            if (el.hasClass(options.invalidNumberClass) == false) { //don't bother doing work on invalid numbers
                var total = purchasing.cleanNumber($("#order-split-total").html());
                var amount = 0, percent = 0;

                if (el.hasClass("order-split-account-amount")) { //update the percent
                    amount = purchasing.cleanNumber(el.val());

                    percent = (amount / total) * 100.0;

                    el.siblings(".order-split-account-percent").val(percent.toFixed(2));
                } else { //update the amount
                    percent = purchasing.cleanNumber(el.val());

                    amount = total * (percent / 100.0);

                    el.siblings(".order-split-account-amount").val(amount.toFixed(2));
                }

                calculateOrderAccountSplits();
            }
        });

        function calculateOrderAccountSplits() {
            var total = 0;

            $(".order-split-account-amount").each(function () {
                var amt = purchasing.cleanNumber(this.value);

                var lineTotal = parseFloat(amt);

                if (!isNaN(lineTotal)) {
                    total += lineTotal;
                }
            });

            var fixedTotal = total.toFixed(2);

            var accountTotal = $("#order-split-account-total");
            accountTotal.html("$" + fixedTotal);

            verifyAccountTotalEqualsGrandTotal(accountTotal);
        }
    }

    function attachSplitLineEvents() {
        $("#split-by-line").click(function (e) {
            e.preventDefault();

            if (confirm("Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat")) {
                var lineItemSplitTemplate = $("#line-item-split-template");
                lineItemSplitTemplate.tmpl().appendTo(".sub-line-item-split-body");
                lineItemSplitTemplate.tmpl().appendTo(".sub-line-item-split-body");

                $(".line-item-splits").show();

                calculateSplitTotals();

                setSplitType("Line");
            }
        });

        $(".line-item-split-account-amount, .line-item-split-account-percent").live("focus blur change keyup", function (e) {
            e.preventDefault();
            var el = $(this);

            purchasing.validateNumber(el);

            if (el.hasClass(options.invalidNumberClass) == false) { //don't bother doing work on invalid numbers
                //find the total for this line
                var containingLineItemSplitTable = el.parentsUntil(".line-item-splits", ".sub-line-item-split");
                var total = purchasing.cleanNumber(containingLineItemSplitTable.find(".add-line-item-total").html());

                var amount = 0, percent = 0;

                if (el.hasClass("line-item-split-account-amount")) { //update the percent
                    amount = purchasing.cleanNumber(el.val());

                    percent = (amount / total) * 100.0;

                    el.parent().parent().find(".line-item-split-account-percent").val(percent.toFixed(2));
                } else { //update the amount
                    percent = purchasing.cleanNumber(el.val());

                    amount = total * (percent / 100.0);

                    el.parent().parent().find(".line-item-split-account-amount").val(amount.toFixed(2));
                }

                calculateLineItemAccountSplits();
            }
        });

        $(".add-line-item-split").live("click", function (e) {
            e.preventDefault();

            var containingFooter = $(this).parentsUntil("table.sub-line-item-split", "tfoot");
            var splitBody = containingFooter.prev();

            $("#line-item-split-template").tmpl().appendTo(splitBody);
        });

        function calculateLineItemAccountSplits() {
            $(".sub-line-item-split").each(function () {
                var currentLineItemSplitRow = $(this);
                var total = 0;

                currentLineItemSplitRow.find(".line-item-split-account-amount").each(function () {
                    var amt = purchasing.cleanNumber(this.value);

                    var lineTotal = parseFloat(amt);

                    if (!isNaN(lineTotal)) {
                        total += lineTotal;
                    }
                });
                console.log(total);

                var fixedTotal = total.toFixed(2);

                var accountTotal = currentLineItemSplitRow.find(".add-line-item-split-total");
                accountTotal.html("$" + fixedTotal);

                verifyAccountTotalEqualsLineItemTotal(currentLineItemSplitRow);
            });
        }

        function calculateSplitTotals() {
            $(".line-item-row").each(function () {
                var row = $(this);
                var quantity = purchasing.cleanNumber(row.find(".quantity").val());
                var price = purchasing.cleanNumber(row.find(".price").val());

                var lineTotal = parseFloat(quantity) * parseFloat(price);

                if (!isNaN(lineTotal)) { //place on sibling line total
                    displayLineItemTotal(row, lineTotal);
                }
            });
        }
    }

    function displayLineItemTotal(itemRow, total) {
        itemRow.next().next().find(".add-line-item-total").html("$" + total.toFixed(2));
    }

    function displayGrandTotal(total) {
        var formattedTotal = "$" + total.toFixed(2);
        $("#grandtotal").html(formattedTotal);

        if (purchasing.splitType === "Order") {
            $("#order-split-total").html(formattedTotal);
            verifyAccountTotalEqualsGrandTotal($("#order-split-account-total"));
        }
    }

    function verifyAccountTotalEqualsGrandTotal(accountTotal) {
        var grandTotal = $("#grandtotal");
        var difference = $("#order-split-account-difference");

        if (accountTotal.html() != grandTotal.html()) {
            accountTotal.addClass(options.invalidNumberClass);
        }
        else {
            accountTotal.removeClass(options.invalidNumberClass);
        }

        var totalDifference = purchasing.cleanNumber(grandTotal.html()) - purchasing.cleanNumber(accountTotal.html());

        if (totalDifference == 0) {
            difference.html("");
        } else {
            difference.html("($" + totalDifference.toFixed(2) + ")");
        }
    }

    function verifyAccountTotalEqualsLineItemTotal(lineItemSplitRow) {
        var splitTotal = lineItemSplitRow.find(".add-line-item-split-total");
        var lineItemTotal = lineItemSplitRow.find(".add-line-item-total");
        var lineItemDifference = lineItemSplitRow.find(".add-line-item-split-difference");

        if (splitTotal.html() != lineItemTotal.html()) {
            splitTotal.addClass(options.invalidNumberClass);
        }
        else {
            splitTotal.removeClass(options.invalidNumberClass);
        }

        var totalDifference = purchasing.cleanNumber(lineItemTotal.html()) - purchasing.cleanNumber(splitTotal.html());

        if (totalDifference == 0) {
            lineItemDifference.html("");
        }
        else {
            lineItemDifference.html("($" + totalDifference.toFixed(2) + ")");
        }
    }

    function setSplitType(split) {
        purchasing.splitType = split;

        if (split === "Order") {
            $("#order-account-section").addClass("ui-state-disabled"); //TODO: this is just fake disabled
            $(".line-item-splits").hide();
            $(".sub-line-item-split-body").empty(); //clear all line splits
        }
        else if (split === "Line") {
            $("#order-account-section").addClass("ui-state-disabled"); //TODO: this is just fake disabled
            $("#order-split-section").hide();
            $("#order-splits").empty();
        }
        else { //For moving back to "no split" (not implemented)
            $(".line-item-splits").hide(); //if any line splits are showing, hide them
            $("#order-split-section").hide();
            $("#order-account-section").removeClass("ui-state-disabled"); //TODO: this is just fake disabled
            $(".sub-line-item-split-body").empty(); //clear all line splits
            $("#order-splits").empty();
        }
    }

    purchasing.validateNumber = function (el) {
        //takes a jquery element & validates that it is a number
        var value = purchasing.cleanNumber(el.val());

        if (isNaN(value) && value != '') {
            el.addClass(options.invalidNumberClass); //TODO: return true/false and use that value instead of querying for class
        }
        else {
            el.removeClass(options.invalidNumberClass);
        }
    };

} (window.purchasing = window.purchasing || {}, jQuery));

//Adding a Public Property
purchasing.quantity = "12";

//Adding New Functionality to Purchasing
(function( purchasing, $, undefined ) {
    //Private Property
    var prop = "testing";

    //Public Method
    purchasing.cleanNumber = function(n) {
           // Assumes string input, removes all commas, dollar signs, percents and spaces
            var newValue = n.replace(",","");
            newValue = newValue.replace("$","");
            newValue = newValue.replace("%","");
            newValue = newValue.replace(/ /g,'');
            return newValue;
    };
} (window.purchasing = window.purchasing || {}, jQuery));
<|endoftext|>


<commit_before>/**
 * Grunt-Contrib-copy
 * @description Copy files and folders.
 * @docs https://github.com/gruntjs/grunt-contrib-copy
 */

var config = require("../Config"),
	createNodeMdulesTargets = function() {
		var nodeModulesFiles = [];

		for(var key in config.nodeModuleDists) {
			if (config.nodeModuleDists.hasOwnProperty(key)) {
				nodeModulesFiles.push({
					expand: true,
					cwd: "./node_modules/" + key,
					src: "**",
					dest: config.nodeModuleDists[key]
				})
			}
		}

		return nodeModulesFiles;
	};

module.exports = {
	imagesDir: {
		files: [{
			expand: true,
			cwd: config.Images.distDir + '/',
			src: "**",
			dest: config.Images.tempDir
		}]
	},
	nodeModules: {
		files: createNodeMdulesTargets()
	}
};
<commit_msg>[BUGFIX] Add a missing 'use strict;' statement<commit_after>/**
 * Grunt-Contrib-copy
 * @description Copy files and folders.
 * @docs https://github.com/gruntjs/grunt-contrib-copy
 */

var config = require("../Config"),
	createNodeMdulesTargets = function() {
		"use strict";

		var nodeModulesFiles = [];

		for(var key in config.nodeModuleDists) {
			if (config.nodeModuleDists.hasOwnProperty(key)) {
				nodeModulesFiles.push({
					expand: true,
					cwd: "./node_modules/" + key,
					src: "**",
					dest: config.nodeModuleDists[key]
				})
			}
		}

		return nodeModulesFiles;
	};

module.exports = {
	imagesDir: {
		files: [{
			expand: true,
			cwd: config.Images.distDir + '/',
			src: "**",
			dest: config.Images.tempDir
		}]
	},
	nodeModules: {
		files: createNodeMdulesTargets()
	}
};
<|endoftext|>


<commit_before>document.observe('trisano:dom:loaded', function() {
  Trisano.Ajax.hookLiveSearchFields();
  Trisano.Ajax.hookUpdateLinks();
  Trisano.Ajax.setupRepeaters();
});

Element.addMethods({
  associatedSpinner: function(element) {
    var spinnerId = element.identify() + "_spinner";
    return $(spinnerId) || spinnerId;
  },

  associatedSearchResults: function(element) {
    var searchResultsId = element.identify() + "_search_results";
    return $(searchResultsId) || searchResultsId;
  }

});

Trisano.Ajax = {

  liveSearch: function(field) {
    if (!Object.isElement(field.associatedSearchResults())) {
      var searchResults = new Element('div', { id: field.associatedSearchResults() });
      Element.extend(document.body).insert( { bottom: searchResults } );
      searchResults.addClassName('autocomplete');
    }
    if (!Object.isElement(field.associatedSpinner())) {
      field.insert({ after: Trisano.Ajax.spinnerImg(field) });
    }
    new Ajax.Autocompleter(field, field.associatedSearchResults(), field.getAttribute('data-url'), {
                             indicator: field.associatedSpinner(),
                             method: 'get',
                             afterUpdateElement: Trisano.Ajax.callbackUpdateRequest
                           });
  },

  callbackUpdateRequest: function(field, selection) {
    new Ajax.Request(field.getAttribute('data-update-url'), {
                       method: 'post',
                       parameters: { place_entity_id: selection.id },
                       onCreate: function() { field.associatedSpinner().show(); },
                       onFailure: function() {
                         field.associatedSpinner().hide();
                         Trisano.flashError(i18n.t('operation_failed')).fade({delay: 3.0});
                       }
                     });
  },

  hookLiveSearchFields: function(selector) {
    var css = selector || "input[data-live-search='true']";
    $$(css).each(function(searchable) {
      Trisano.Ajax.liveSearch(searchable);
    });
  },

  hookUpdateLinks: function(selector) {
    var css = selector || 'a[data-update-link=true]';
    $$(css).each(function(link) {
      if (!Object.isElement(link.associatedSpinner())) {
        link.insert({ after: Trisano.Ajax.spinnerImg(link) });
      }
      link.observe('click', function(event) {
        new Ajax.Request(link.href, { method: 'post',
                                      onCreate: function() { link.associatedSpinner().show(); },
                                      onFailure: function() {
                                        link.associatedSpinner().hide();
                                        Trisano.flashError(i18n.t('operation_failed')).fade({delay: 4.0});
                                      } });
        event.stop();
      });
    });
  },

  spinnerImg: function(node) {
    var spinner = Trisano.Ajax.spinnerImgNoID();
    spinner.setStyle({display: 'none'});
    spinner.id = node.associatedSpinner();
    return spinner;
  },

  spinnerImgNoID: function() {
    return new Element('img', {
                         src: Trisano.url('images/redbox_spinner.gif')
                       });
  },

  getHosFacRepeaterAjaxActions: function() {
    return $j("div#hospitalization_facilities").find("span.ajax-actions");
  },

  getPatientTeleRepeaterAjaxActions: function() {
    return $j("div#telephones").find("span.ajax-actions");
  },

  getRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getHosFacRepeaterAjaxActions();
    repeaters = repeaters.add(Trisano.Ajax.getPatientTeleRepeaterAjaxActions());
    return repeaters;
  },

  hideRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getRepeaterAjaxActions();
      repeaters.each(function(index) {
        this.remove();
      });
  },

  hidePatientTeleRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getPatientTeleRepeaterAjaxActions();
      repeaters.each(function(index) {
        this.remove();
      });
  },

  hideHosFacRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getHosFacRepeaterAjaxActions();
      repeaters.each(function(index) {
        this.remove();
      });
  },

  hideRepeaterAjaxActionsAfterAddingRepeater: function() {
    $j("a#add-patient-telephone").live("click", function() {
      Trisano.Ajax.hidePatientTeleRepeaterAjaxActions();
    });
    $j("a#add-hospitalization-facilities").live("click", function() {
      Trisano.Ajax.hideHosFacRepeaterAjaxActions();
    });
  },

  enableRepeaterAjaxActions: function() {
    Trisano.Ajax.enableHosFacRepeaterAjaxActions();
    Trisano.Ajax.enablePatientTeleRepeaterAjaxActions();
  },

  enablePatientTeleRepeaterAjaxActions: function() {
    $j("a.save-new-patient-telephone").live("click", Trisano.Ajax.savePatientTelephone);

    $j("a.discard-new-patient-telephone").live("click", function() {
      $j(this).closest("div.phone").remove();
      $j("a#add-patient-telephone").show();
      return false;
    });

    $j("a#add-patient-telephone").live("click", function() {
      this.hide();
    });

    if(Trisano.Ajax.getPatientTeleRepeaterAjaxActions().length != 0) {
      // A blank template is being shown
      $j("a#add-patient-telephone").hide();
    }
  },

  enableHosFacRepeaterAjaxActions: function() {
    $j("a.save-new-hospital-participation").live("click", Trisano.Ajax.saveHospitalization);

    $j("a.discard-new-hospital-participation").live("click", function() {
      $j(this).closest("div.hospital").remove();
      $j("a#add-hospitalization-facilities").show();
      return false;
    });

    $j("a#add-hospitalization-facilities").live("click", function() {
      this.hide();
    });

    if(Trisano.Ajax.getHosFacRepeaterAjaxActions().length != 0) {
      // A blank template is being shown
      $j("a#add-hospitalization-facilities").hide();
    }
  },

  setupRepeaters: function() {
    if($j("form[class^=new_][class$=_event]").length!=0) {
      // New mode
      Trisano.Ajax.hideRepeaterAjaxActions();
      Trisano.Ajax.hideRepeaterAjaxActionsAfterAddingRepeater();
    } else {
      // Edit mode
      Trisano.Ajax.enableRepeaterAjaxActions();
    }
  },

  saveRepeaters: function() {
    var deferreds = Trisano.Ajax.saveHospitalizations();
    deferreds = $j.merge(deferreds, Trisano.Ajax.savePatientTelephones());
    return deferreds;
  },

  savePatientTelephones: function() {
    var deferreds = [];
    $j("div.phone a.save-new-patient-telephone").closest("div.phone").each(function() {
      deferreds.push(Trisano.Ajax.postPatientTelephone($j(this)));
    });
    return deferreds;
  },

  saveHospitalizations: function() {
    var deferreds = [];
    $j("div.hospital a.save-new-hospital-participation").closest("div.hospital").each(function() {
      deferreds.push(Trisano.Ajax.postHospitalization($j(this)));
    });
    return deferreds;
  },

  savePatientTelephone: function() {
    var data_source = $j(this).closest("div.phone");
    var result = Trisano.Ajax.postPatientTelephone(data_source);
    if(result.length == 0){
      // No inputs were populated. We want to do this here so that this message is only shown
      // when an individual hospitalization is saved, not when Save & Continue is used.
      data_source.prepend("Please fill out at least one field.");
    };
    return false;
  },

  saveHospitalization: function() {
    var data_source = $j(this).closest("div.hospital");
    var result = Trisano.Ajax.postHospitalization($j(data_source));
    if(result.length == 0){
      // No inputs were populated. We want to do this here so that this message is only shown
      // when an individual hospitalization is saved, not when Save & Continue is used.
      data_source.prepend("Please fill out at least one field.");
    };
    return false;
  },

  fieldsPopulated: function(data_source) {
    var populated = false;
    $j(data_source).find(":input[type!=hidden]").each(function(idx, elem){
      if($j(elem).is("[type=radio]") || $j(elem).is("[type=checkbox]")){
        if($j(elem).is(":checked")){
          populated = true;
          return true;
        }
      } else {
        if($j(elem).val().length != 0){
          populated = true;
          return true;
        }
      }
    });
    return populated;
  },

  postRepeater: function(data_source, fields, url, target, new_repeater_id) {
    if(!Trisano.Ajax.fieldsPopulated(data_source)) {
      return [];
    }

    var data = fields.serialize();

    // Because form questions name attribute does not match it's ID attribute,
    // if invalid data is submitted, Rails's form builder doesn't know how to
    // insert the form data back into the correct form question.
    // Here we collect all the visible questions IDs and map to their values
    // so that if we hit an error, we can repopulate below.
    var id_data = {};

    // Must refind elements directly form data source
    data_source.find(":input[type!=hidden]").each(function(i,e) {
      // { element_id: element_value }
      id_data[$j(e).attr('id')] = $j(e).val();
    });

    return $j.ajax({
      url: url,
      data: data,
      dataType: 'html',
      type: "POST",
      beforeSend: function( xhr) {
        data_source.find("span.ajax-actions").replaceWith(Trisano.Ajax.spinnerImgNoID());
      },
      error: function(request, textStatus, error) {
        target.replaceWith(request.responseText);
        Trisano.Tabs.highlightTabsWithErrors();

        // Must manually repopulate the form fields data on an error
        $j.each(id_data, function(element_id,element_value) {
          // Strangly, you MUST operate on the DOM via $j, not $j(target) or $j(request.response)
          // otherwise changes won't be applied.
           var element = $j.find("#" + element_id);
           if(element){
              $j(element).val(element_value);

              // This line is required to make the testing system be able to see the value was
              // actually inserted. The Selenium RC version we're using cannot read the DOM
              // manipulation directly.
              $j(element).attr('value', element_value);
           }
        });
      },
      success: function(data, textStatus, request) {
        // Must be run before attempting to show a#add-hospitalization-facilities
        target.replaceWith(request.responseText);
        $j(new_repeater_id).show();
      }
    });
  },

  postPatientTelephone: function(data_source) {
    var patient_telephone_fields = data_source.find(":input");
    var url = Trisano.url("/human_events/" + $j("#id").attr('value') +"/patient_telephones");
    var target = data_source;
    var new_repeater_id = "a#add-patient-telephone";

    return Trisano.Ajax.postRepeater(data_source, patient_telephone_fields, url, target, new_repeater_id);
  },
  postHospitalization: function(data_source) {
    var hospitalization_fields = data_source.find(":input");
    // Immediately following the div.hospital the hospitalization_facilities id is
    // rendered. We must include this in the POST to avoid creating a new
    // HospitalizationFacility
    var facilities_hidden_fields = data_source.next(":input[type=hidden]");
    hospitalization_fields = hospitalization_fields.add(facilities_hidden_fields);

    var url = Trisano.url("/human_events/" + $j("#id").attr('value') +"/hospitalization_facilities");
    var target = data_source;
    var new_repeater_id = "a#add-hospitalization-facilities";

    return Trisano.Ajax.postRepeater(data_source, hospitalization_fields, url, target, new_repeater_id);
  }
};

// Prototype is lame. JQuery bits
$j('a.ajaxy.delete').livequery(function() {
  $j(this).click(function(evt) {
    evt.preventDefault();
    if ($j(evt.target).attr('data-confirm') != undefined && confirm($j(evt.target).attr('data-confirm'))) {
      $j($j(evt.target).attr('data-spinner')).show();
      $j.ajax({
        url: this.href,
        data: { _method: 'delete' },
        success: function() { $j($j(evt.target).attr('data-remove')).remove() },
        error: function() {
          $j($j(evt.target).attr('data-message-box')).text($j(evt.target).attr('data-error-message'))
        },
        complete: function() { $j($j(evt.target).attr('data-spinner')).hide() },
        type: "POST",
        dataType: "script"
      });
    }
  });
});
<commit_msg>Set radio/checkboxes on repeaters errors<commit_after>document.observe('trisano:dom:loaded', function() {
  Trisano.Ajax.hookLiveSearchFields();
  Trisano.Ajax.hookUpdateLinks();
  Trisano.Ajax.setupRepeaters();
});

Element.addMethods({
  associatedSpinner: function(element) {
    var spinnerId = element.identify() + "_spinner";
    return $(spinnerId) || spinnerId;
  },

  associatedSearchResults: function(element) {
    var searchResultsId = element.identify() + "_search_results";
    return $(searchResultsId) || searchResultsId;
  }

});

Trisano.Ajax = {

  liveSearch: function(field) {
    if (!Object.isElement(field.associatedSearchResults())) {
      var searchResults = new Element('div', { id: field.associatedSearchResults() });
      Element.extend(document.body).insert( { bottom: searchResults } );
      searchResults.addClassName('autocomplete');
    }
    if (!Object.isElement(field.associatedSpinner())) {
      field.insert({ after: Trisano.Ajax.spinnerImg(field) });
    }
    new Ajax.Autocompleter(field, field.associatedSearchResults(), field.getAttribute('data-url'), {
                             indicator: field.associatedSpinner(),
                             method: 'get',
                             afterUpdateElement: Trisano.Ajax.callbackUpdateRequest
                           });
  },

  callbackUpdateRequest: function(field, selection) {
    new Ajax.Request(field.getAttribute('data-update-url'), {
                       method: 'post',
                       parameters: { place_entity_id: selection.id },
                       onCreate: function() { field.associatedSpinner().show(); },
                       onFailure: function() {
                         field.associatedSpinner().hide();
                         Trisano.flashError(i18n.t('operation_failed')).fade({delay: 3.0});
                       }
                     });
  },

  hookLiveSearchFields: function(selector) {
    var css = selector || "input[data-live-search='true']";
    $$(css).each(function(searchable) {
      Trisano.Ajax.liveSearch(searchable);
    });
  },

  hookUpdateLinks: function(selector) {
    var css = selector || 'a[data-update-link=true]';
    $$(css).each(function(link) {
      if (!Object.isElement(link.associatedSpinner())) {
        link.insert({ after: Trisano.Ajax.spinnerImg(link) });
      }
      link.observe('click', function(event) {
        new Ajax.Request(link.href, { method: 'post',
                                      onCreate: function() { link.associatedSpinner().show(); },
                                      onFailure: function() {
                                        link.associatedSpinner().hide();
                                        Trisano.flashError(i18n.t('operation_failed')).fade({delay: 4.0});
                                      } });
        event.stop();
      });
    });
  },

  spinnerImg: function(node) {
    var spinner = Trisano.Ajax.spinnerImgNoID();
    spinner.setStyle({display: 'none'});
    spinner.id = node.associatedSpinner();
    return spinner;
  },

  spinnerImgNoID: function() {
    return new Element('img', {
                         src: Trisano.url('images/redbox_spinner.gif')
                       });
  },

  getHosFacRepeaterAjaxActions: function() {
    return $j("div#hospitalization_facilities").find("span.ajax-actions");
  },

  getPatientTeleRepeaterAjaxActions: function() {
    return $j("div#telephones").find("span.ajax-actions");
  },

  getRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getHosFacRepeaterAjaxActions();
    repeaters = repeaters.add(Trisano.Ajax.getPatientTeleRepeaterAjaxActions());
    return repeaters;
  },

  hideRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getRepeaterAjaxActions();
      repeaters.each(function(index) {
        this.remove();
      });
  },

  hidePatientTeleRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getPatientTeleRepeaterAjaxActions();
      repeaters.each(function(index) {
        this.remove();
      });
  },

  hideHosFacRepeaterAjaxActions: function() {
    var repeaters = Trisano.Ajax.getHosFacRepeaterAjaxActions();
      repeaters.each(function(index) {
        this.remove();
      });
  },

  hideRepeaterAjaxActionsAfterAddingRepeater: function() {
    $j("a#add-patient-telephone").live("click", function() {
      Trisano.Ajax.hidePatientTeleRepeaterAjaxActions();
    });
    $j("a#add-hospitalization-facilities").live("click", function() {
      Trisano.Ajax.hideHosFacRepeaterAjaxActions();
    });
  },

  enableRepeaterAjaxActions: function() {
    Trisano.Ajax.enableHosFacRepeaterAjaxActions();
    Trisano.Ajax.enablePatientTeleRepeaterAjaxActions();
  },

  enablePatientTeleRepeaterAjaxActions: function() {
    $j("a.save-new-patient-telephone").live("click", Trisano.Ajax.savePatientTelephone);

    $j("a.discard-new-patient-telephone").live("click", function() {
      $j(this).closest("div.phone").remove();
      $j("a#add-patient-telephone").show();
      return false;
    });

    $j("a#add-patient-telephone").live("click", function() {
      this.hide();
    });

    if(Trisano.Ajax.getPatientTeleRepeaterAjaxActions().length != 0) {
      // A blank template is being shown
      $j("a#add-patient-telephone").hide();
    }
  },

  enableHosFacRepeaterAjaxActions: function() {
    $j("a.save-new-hospital-participation").live("click", Trisano.Ajax.saveHospitalization);

    $j("a.discard-new-hospital-participation").live("click", function() {
      $j(this).closest("div.hospital").remove();
      $j("a#add-hospitalization-facilities").show();
      return false;
    });

    $j("a#add-hospitalization-facilities").live("click", function() {
      this.hide();
    });

    if(Trisano.Ajax.getHosFacRepeaterAjaxActions().length != 0) {
      // A blank template is being shown
      $j("a#add-hospitalization-facilities").hide();
    }
  },

  setupRepeaters: function() {
    if($j("form[class^=new_][class$=_event]").length!=0) {
      // New mode
      Trisano.Ajax.hideRepeaterAjaxActions();
      Trisano.Ajax.hideRepeaterAjaxActionsAfterAddingRepeater();
    } else {
      // Edit mode
      Trisano.Ajax.enableRepeaterAjaxActions();
    }
  },

  saveRepeaters: function() {
    var deferreds = Trisano.Ajax.saveHospitalizations();
    deferreds = $j.merge(deferreds, Trisano.Ajax.savePatientTelephones());
    return deferreds;
  },

  savePatientTelephones: function() {
    var deferreds = [];
    $j("div.phone a.save-new-patient-telephone").closest("div.phone").each(function() {
      deferreds.push(Trisano.Ajax.postPatientTelephone($j(this)));
    });
    return deferreds;
  },

  saveHospitalizations: function() {
    var deferreds = [];
    $j("div.hospital a.save-new-hospital-participation").closest("div.hospital").each(function() {
      deferreds.push(Trisano.Ajax.postHospitalization($j(this)));
    });
    return deferreds;
  },

  savePatientTelephone: function() {
    var data_source = $j(this).closest("div.phone");
    var result = Trisano.Ajax.postPatientTelephone(data_source);
    if(result.length == 0){
      // No inputs were populated. We want to do this here so that this message is only shown
      // when an individual hospitalization is saved, not when Save & Continue is used.
      data_source.prepend("Please fill out at least one field.");
    };
    return false;
  },

  saveHospitalization: function() {
    var data_source = $j(this).closest("div.hospital");
    var result = Trisano.Ajax.postHospitalization($j(data_source));
    if(result.length == 0){
      // No inputs were populated. We want to do this here so that this message is only shown
      // when an individual hospitalization is saved, not when Save & Continue is used.
      data_source.prepend("Please fill out at least one field.");
    };
    return false;
  },

  fieldsPopulated: function(data_source) {
    var populated = false;
    $j(data_source).find(":input[type!=hidden]").each(function(idx, elem){
      if($j(elem).is("[type=radio]") || $j(elem).is("[type=checkbox]")){
        if($j(elem).is(":checked")){
          populated = true;
          return true;
        }
      } else {
        if($j(elem).val().length != 0){
          populated = true;
          return true;
        }
      }
    });
    return populated;
  },

  postRepeater: function(data_source, fields, url, target, new_repeater_id) {
    if(!Trisano.Ajax.fieldsPopulated(data_source)) {
      return [];
    }

    var data = fields.serialize();

    // Because form questions name attribute does not match it's ID attribute,
    // if invalid data is submitted, Rails's form builder doesn't know how to
    // insert the form data back into the correct form question.
    // Here we collect all the visible questions IDs and map to their values
    // so that if we hit an error, we can repopulate below.
    var text_data = {};

    // Must refind elements directly form data source
    data_source.find(":input[type!=hidden][type!=checkbox][type!=radio]").each(function(i,e) {
      text_data[$j(e).attr('id')] = $j(e).val(); // { element_id: element_value }
    });

    // Must handle radio buttons and checkboxes seperately.
    var checked_data = {};
    data_source.find(":input[type!=hidden][type=checkbox]:checked").each(function(i,e) {
      checked_data[$j(e).attr('id')] = $j(e).val(); // { element_id: element_value }
    });
    data_source.find(":input[type!=hidden][type=radio]:checked").each(function(i,e) {
      checked_data[$j(e).attr('id')] = $j(e).val(); // { element_id: element_value }
    });

    return $j.ajax({
      url: url,
      data: data,
      dataType: 'html',
      type: "POST",
      beforeSend: function( xhr) {
        data_source.find("span.ajax-actions").replaceWith(Trisano.Ajax.spinnerImgNoID());
      },
      error: function(request, textStatus, error) {
        target.replaceWith(request.responseText);
        Trisano.Tabs.highlightTabsWithErrors();

        // Must manually repopulate the form fields data on an error
        $j.each(text_data, function(element_id,element_value) {
          // Strangly, you MUST operate on the DOM via $j, not $j(target) or $j(request.response)
          // otherwise changes won't be applied.
           var element = $j.find("#" + element_id);
           if(element){
              $j(element).val(element_value);

              // This line is required to make the testing system be able to see the value was
              // actually inserted. The Selenium RC version we're using cannot read the DOM
              // manipulation directly.
              $j(element).attr('value', element_value);
           }
        });

        // And again for checkboxes and radio buttons
        $j.each(checked_data, function(element_id,element_value) {
           var element = $j.find("#" + element_id);
           if(element){
              $j(element).prop("checked", true);
              $j(element).attr('checked', "checked"); //maybe redundant?
           }
        });
      },
      success: function(data, textStatus, request) {
        // Must be run before attempting to show a#add-hospitalization-facilities
        target.replaceWith(request.responseText);
        $j(new_repeater_id).show();
      }
    });
  },

  postPatientTelephone: function(data_source) {
    var patient_telephone_fields = data_source.find(":input");
    var url = Trisano.url("/human_events/" + $j("#id").attr('value') +"/patient_telephones");
    var target = data_source;
    var new_repeater_id = "a#add-patient-telephone";

    return Trisano.Ajax.postRepeater(data_source, patient_telephone_fields, url, target, new_repeater_id);
  },
  postHospitalization: function(data_source) {
    var hospitalization_fields = data_source.find(":input");
    // Immediately following the div.hospital the hospitalization_facilities id is
    // rendered. We must include this in the POST to avoid creating a new
    // HospitalizationFacility
    var facilities_hidden_fields = data_source.next(":input[type=hidden]");
    hospitalization_fields = hospitalization_fields.add(facilities_hidden_fields);

    var url = Trisano.url("/human_events/" + $j("#id").attr('value') +"/hospitalization_facilities");
    var target = data_source;
    var new_repeater_id = "a#add-hospitalization-facilities";

    return Trisano.Ajax.postRepeater(data_source, hospitalization_fields, url, target, new_repeater_id);
  }
};

// Prototype is lame. JQuery bits
$j('a.ajaxy.delete').livequery(function() {
  $j(this).click(function(evt) {
    evt.preventDefault();
    if ($j(evt.target).attr('data-confirm') != undefined && confirm($j(evt.target).attr('data-confirm'))) {
      $j($j(evt.target).attr('data-spinner')).show();
      $j.ajax({
        url: this.href,
        data: { _method: 'delete' },
        success: function() { $j($j(evt.target).attr('data-remove')).remove() },
        error: function() {
          $j($j(evt.target).attr('data-message-box')).text($j(evt.target).attr('data-error-message'))
        },
        complete: function() { $j($j(evt.target).attr('data-spinner')).hide() },
        type: "POST",
        dataType: "script"
      });
    }
  });
});
<|endoftext|>


<commit_before>/* jshint devel:true */
/* global define */
define(['underscore', 'backbone', 'oro/multiple-entity/view', 'oro/multiple-entity/model', 'oro/dialog-widget'],
function(_, Backbone, EntityView, MultipleEntityModel, DialogWidget) {
    'use strict';

    var $ = Backbone.$;

    /**
     * @export  oro/multiple-entity
     * @class   oro.multipleEntity
     * @extends Backbone.View
     */
    return Backbone.View.extend({
        options: {
            template: null,
            elementTemplate: null,
            entitiesContainerSelector: '.entities',
            name: null,
            collection: null,
            selectionUrl: null,
            addedElement: null,
            removedElement: null,
            defaultElement: null,
            itemsPerRow: 4,
            selectorWindowTitle: null
        },

        events: {
            'click .add-btn': 'addEntities'
        },

        initialize: function() {
            this.template = _.template(this.options.template)
            this.listenTo(this.getCollection(), 'add', this.addEntity);
            this.listenTo(this.getCollection(), 'reset', this.addAll);
            this.listenTo(this.getCollection(), 'remove', this.removeDefault);

            this.$addedEl = $(this.options.addedElement);
            this.$removedEl = $(this.options.removedElement);
            if (this.options.defaultElement) {
                this.listenTo(this.getCollection(), 'defaultChange', this.updateDefault);
                this.$defaultEl = this.$el.closest('form').find('[name$="[' + this.options.defaultElement + ']"]');
            }

            this.render();
        },

        handleRemove: function (item) {
            var itemId = item && item.get('id');
            if (!itemId) {
                return;
            }

            var addedElVal = this.$addedEl.val();
            var added = (addedElVal && addedElVal.split(',')) || [];

            var removedElVal = this.$removedEl.val();
            var removed = (removedElVal && removedElVal.split(',')) || [];

            if (_.contains(added, itemId)) {
                added = _.without(added, itemId);
                this.$addedEl.val(added.join(','));
                return;
            }

            if (!_.contains(added, itemId)) {
                removed.push(itemId);
                this.$removedEl.val(removed.join(','));
            }
        },

        removeDefault: function(item) {
            if (item.get('isDefault')) {
                this.$defaultEl.val('');
            }
        },

        updateDefault: function(item) {
            this.$defaultEl.val(item.get('id'));
        },

        getCollection: function() {
            return this.options.collection;
        },

        addAll: function(items) {
            this._resortCollection();
            this.$entitiesContainer.empty();
            items.each(function(item) {
                this.addEntity(item);
            }, this);
        },

        _resortCollection: function() {
            this.getCollection().comparator = function(model) {
                if (model.get('isDefault')) {
                    return 'A';
                } else {
                    return model.get('label');
                }
            };
            this.getCollection().sort();
        },

        addEntity: function(item) {
            if (item.get('id') == this.$defaultEl.val()) {
                item.set('isDefault', true);
            }
            var entityView = new EntityView({
                model: item,
                name: this.options.name,
                hasDefault: this.options.defaultElement,
                template: this.options.elementTemplate
            });
            entityView.on('removal', _.bind(this.handleRemove, this));
            this.$entitiesContainer.append(entityView.render().$el);
        },

        addEntities: function() {
            if (!this.selectorDialog) {
                var url = this.options.selectionUrl;
                var separator = url.indexOf('?') > -1 ? '&' : '?';
                this.selectorDialog = new DialogWidget({
                    url: url + separator
                        + 'added=' + this.$addedEl.val()
                        + '&removed=' + this.$removedEl.val()
                        + '&default=' + this.$defaultEl.val(),
                    title: this.options.selectorWindowTitle,
                    stateEnabled: false,
                    dialogOptions: {
                        'modal': true,
                        'width': 1024,
                        'height': 500,
                        'close': _.bind(function() {
                            this.selectorDialog = null;
                        }, this)
                    }
                });
                this.selectorDialog.on('completeSelection', _.bind(this.processSelectedEntities, this));
                this.selectorDialog.render();
            }
        },

        processSelectedEntities: function(added, addedModels, removed) {
            var intersection = _.intersection(added, removed);
            var addedNormalized = _.difference(added, intersection);
            var removedNormalized = _.difference(removed, intersection);
            this.$addedEl.val(addedNormalized.join(','));
            this.$removedEl.val(removedNormalized.join(','));

            _.each(addedModels, _.bind(function(model) {
                this.getCollection().add(model);
            }, this));
            for (var i = 0; i < removed.length; i++) {
                var model = this.getCollection().get(removed[i]);
                if (model) {
                    model.set('id', null);
                    model.destroy()
                }
            }

            this.selectorDialog.remove();
        },

        render: function() {
            this.$el.html(this.template());
            this.$entitiesContainer = this.$el.find(this.options.entitiesContainerSelector);

            return this;
        }
    });
});
<commit_msg>CRM-751: fixed added/removed items tracking in multiple-entity<commit_after>/* jshint devel:true */
/* global define */
define(['underscore', 'backbone', 'oro/multiple-entity/view', 'oro/multiple-entity/model', 'oro/dialog-widget'],
function(_, Backbone, EntityView, MultipleEntityModel, DialogWidget) {
    'use strict';

    var $ = Backbone.$;

    /**
     * @export  oro/multiple-entity
     * @class   oro.multipleEntity
     * @extends Backbone.View
     */
    return Backbone.View.extend({
        options: {
            template: null,
            elementTemplate: null,
            entitiesContainerSelector: '.entities',
            name: null,
            collection: null,
            selectionUrl: null,
            addedElement: null,
            removedElement: null,
            defaultElement: null,
            itemsPerRow: 4,
            selectorWindowTitle: null
        },

        events: {
            'click .add-btn': 'addEntities'
        },

        initialize: function() {
            this.template = _.template(this.options.template)
            this.listenTo(this.getCollection(), 'add', this.addEntity);
            this.listenTo(this.getCollection(), 'reset', this._onCollectionReset);
            this.listenTo(this.getCollection(), 'remove', this.removeDefault);

            this.$addedEl = $(this.options.addedElement);
            this.$removedEl = $(this.options.removedElement);
            if (this.options.defaultElement) {
                this.listenTo(this.getCollection(), 'defaultChange', this.updateDefault);
                this.$defaultEl = this.$el.closest('form').find('[name$="[' + this.options.defaultElement + ']"]');
            }
            this.initialCollectionItems = [];
            this.addedCollectionItems = [];
            this.removedCollectionItems = [];

            this.render();
        },

        handleRemove: function (item) {
            var itemId = item && item.get('id');
            if (!itemId) {
                return;
            }

            var addedElVal = this.$addedEl.val();
            var removedElVal = this.$removedEl.val();

            var added = (addedElVal && addedElVal.split(',')) || [];
            var removed = (removedElVal && removedElVal.split(',')) || [];

            if (_.contains(added, itemId)) {
                added = _.without(added, itemId);
            }
            if (!_.contains(removed, itemId)) {
                removed.push(itemId);
            }

            this.addedCollectionItems = added;
            this.removedCollectionItems = removed;

            this.$addedEl.val(added.join(','));
            this.$removedEl.val(removed.join(','));
        },

        removeDefault: function(item) {
            if (item.get('isDefault')) {
                this.$defaultEl.val('');
            }
        },

        updateDefault: function(item) {
            this.$defaultEl.val(item.get('id'));
        },

        getCollection: function() {
            return this.options.collection;
        },

        _onCollectionReset: function (items) {
            this._resortCollection();
            this.$entitiesContainer.empty();
            items.each(function(item) {
                this.addEntity(item);
            }, this);

            this.initialCollectionItems = this.getCollection().map(function (model) {
                return model.get('id');
            });
            this.addedCollectionItems = [];
            this.removedCollectionItems = [];
        },

        _isInitialCollectionItem: function (itemId) {
            var isInitial = !!_.find(this.initialCollectionItems, function (id) {
                return String(id) === String(itemId);
            });
            return isInitial;
        },

        _isAddedCollectionItem: function (itemId) {
            var isAdded = !!_.find(this.addedCollectionItems, function (id) {
                return String(id) === String(itemId);
            });
            return isAdded;
        },

        _isRemovedCollectionItem: function (itemId) {
            var isRemoved = !!_.find(this.removedCollectionItems, function (id) {
                return String(id) === String(itemId);
            });
            return isRemoved;
        },

        _resortCollection: function() {
            this.getCollection().comparator = function(model) {
                if (model.get('isDefault')) {
                    return 'A';
                } else {
                    return model.get('label');
                }
            };
            this.getCollection().sort();
        },

        addEntity: function(item) {
            if (item.get('id') == this.$defaultEl.val()) {
                item.set('isDefault', true);
            }
            var entityView = new EntityView({
                model: item,
                name: this.options.name,
                hasDefault: this.options.defaultElement,
                template: this.options.elementTemplate
            });
            entityView.on('removal', _.bind(this.handleRemove, this));
            this.$entitiesContainer.append(entityView.render().$el);
        },

        addEntities: function() {
            if (!this.selectorDialog) {
                var url = this.options.selectionUrl;
                var separator = url.indexOf('?') > -1 ? '&' : '?';
                this.selectorDialog = new DialogWidget({
                    url: url + separator
                        + 'added=' + this.$addedEl.val()
                        + '&removed=' + this.$removedEl.val()
                        + '&default=' + this.$defaultEl.val(),
                    title: this.options.selectorWindowTitle,
                    stateEnabled: false,
                    dialogOptions: {
                        'modal': true,
                        'width': 1024,
                        'height': 500,
                        'close': _.bind(function() {
                            this.selectorDialog = null;
                        }, this)
                    }
                });
                this.selectorDialog.on('completeSelection', _.bind(this.processSelectedEntities, this));
                this.selectorDialog.render();
            }
        },

        processSelectedEntities: function (added, addedModels, removed) {
            var self = this;

            _.intersection(added, removed).forEach(function (itemId) {
                if (self._isInitialCollectionItem(itemId)) {
                    added = _.without(added, itemId);
                    removed = _.without(removed, itemId);
                    return;
                }

                if (self._isAddedCollectionItem(itemId)) {
                    added = _.without(added, itemId);
                    return;
                }

                if (self._isRemovedCollectionItem(itemId)) {
                    removed = _.without(removed, itemId);
                }
            });

            this.addedCollectionItems = added;
            this.removedCollectionItems = removed;

            this.$addedEl.val(added.join(','));
            this.$removedEl.val(removed.join(','));

            _.each(addedModels, _.bind(function(model) {
                this.getCollection().add(model);
            }, this));
            for (var i = 0; i < removed.length; i++) {
                var model = this.getCollection().get(removed[i]);
                if (model) {
                    model.set('id', null);
                    model.destroy()
                }
            }

            this.selectorDialog.remove();
        },

        render: function() {
            this.$el.html(this.template());
            this.$entitiesContainer = this.$el.find(this.options.entitiesContainerSelector);

            return this;
        }
    });
});
<|endoftext|>


<commit_before>define(['underscore', 'asap'], function(_, asap) {
    'use strict';

    _.mixin({
        nl2br: function(str) {
            var breakTag = '
';
            return String(str).replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
        },

        trunc: function(str, maxLength, useWordBoundary, hellip) {
            hellip = hellip || '…';
            var toLong = str.length > maxLength;
            str = toLong ? str.substr(0, maxLength - 1) : str;
            var lastSpace = str.lastIndexOf(' ');
            str = useWordBoundary && toLong && lastSpace > 0 ? str.substr(0, lastSpace) : str;
            return toLong ? str + hellip : str;
        },

        isMobile: function() {
            var elem = document.getElementsByTagName('body')[0];
            return elem && (' ' + elem.className + ' ')
                    .replace(/[\t\r\n\f]/g, ' ')
                    .indexOf(' mobile-version ') !== -1;
        },

        /* This function is available in newer underscore/lodash versions */
        findIndex: function(collection, predicate) {
            for (var i = 0; i < collection.length; i++) {
                var item = collection[i];
                if (predicate(item)) {
                    return i;
                }
            }
        },

        /* This function is available in newer underscore/lodash versions */
        findLastIndex: function(collection, predicate) {
            for (var i = collection.length - 1; i >= 0; i--) {
                var item = collection[i];
                if (predicate(item)) {
                    return i;
                }
            }
        },

        trim: function(text) {
            return text.replace(/^\s*/, '').replace(/\s*$/, '');
        }
    });

    _.templateSettings.innerTempStart = '<%#';
    _.templateSettings.innerTempEnd = '#%>';

    _.template = _.wrap(_.template, function(original, text, settings, oldSettings) {
        if (!settings && oldSettings) {
            settings = oldSettings;
        }
        settings = _.defaults({}, settings, _.templateSettings);

        var regexStart = new RegExp('^' + settings.innerTempStart);
        var regexEnd = new RegExp(settings.innerTempEnd + '$');
        var evaluateStart = '(' + _.templateSettings.innerTempStart + ')';
        var evaluateEnd = '|(' + _.templateSettings.innerTempEnd + ')';

        var innerTempEvaluate = new RegExp(evaluateStart + evaluateEnd, 'g');

        text = _.trim(text).replace(regexStart, '').replace(regexEnd, '');

        var escapedText = text;

        var levelOffsets = {};
        var level = 0;
        var offsetDelta = 0;

        var escapeText = function(text) {
            return text.replace(/\&lt\;\%/g, '&lt;%').replace(/<%/g, '<%').replace(/%>/g, '%>');
        };

        text.replace(innerTempEvaluate, function(match, open, close, offset) {
            offset += offsetDelta;
            if (open) {
                level++;
                levelOffsets[level] = offset;
            }
            if (close && level) {
                var start = escapedText.slice(0, levelOffsets[level]);
                var end = escapedText.slice(offset + close.length);
                var escape = escapedText.slice(levelOffsets[level] + settings.innerTempStart.length, offset);
                var newEscape = escapeText(escape);

                offsetDelta += newEscape.length - escape.length - (settings.innerTempEnd.length * 2);
                escapedText = start + newEscape + end;
                level--;
            }

            // Adobe VMs need the match returned to produce the correct offset.
            return match;
        });
        arguments[1] = _.trim(escapedText);

        return original.apply(this, _.rest(arguments));
    });

    _.defer = asap;

    return _;
});
<commit_msg>BAP-12559: remove our underscore extend since it was implemented in new lib version<commit_after>define(['underscore', 'asap'], function(_, asap) {
    'use strict';

    _.mixin({
        nl2br: function(str) {
            var breakTag = '
';
            return String(str).replace(/([^>\r\n]?)(\r\n|\n\r|\r|\n)/g, '$1' + breakTag + '$2');
        },

        trunc: function(str, maxLength, useWordBoundary, hellip) {
            hellip = hellip || '…';
            var toLong = str.length > maxLength;
            str = toLong ? str.substr(0, maxLength - 1) : str;
            var lastSpace = str.lastIndexOf(' ');
            str = useWordBoundary && toLong && lastSpace > 0 ? str.substr(0, lastSpace) : str;
            return toLong ? str + hellip : str;
        },

        isMobile: function() {
            var elem = document.getElementsByTagName('body')[0];
            return elem && (' ' + elem.className + ' ')
                    .replace(/[\t\r\n\f]/g, ' ')
                    .indexOf(' mobile-version ') !== -1;
        },

        trim: function(text) {
            return text.replace(/^\s*/, '').replace(/\s*$/, '');
        }
    });

    _.templateSettings.innerTempStart = '<%#';
    _.templateSettings.innerTempEnd = '#%>';

    _.template = _.wrap(_.template, function(original, text, settings, oldSettings) {
        if (!settings && oldSettings) {
            settings = oldSettings;
        }
        settings = _.defaults({}, settings, _.templateSettings);

        var regexStart = new RegExp('^' + settings.innerTempStart);
        var regexEnd = new RegExp(settings.innerTempEnd + '$');
        var evaluateStart = '(' + _.templateSettings.innerTempStart + ')';
        var evaluateEnd = '|(' + _.templateSettings.innerTempEnd + ')';

        var innerTempEvaluate = new RegExp(evaluateStart + evaluateEnd, 'g');

        text = _.trim(text).replace(regexStart, '').replace(regexEnd, '');

        var escapedText = text;

        var levelOffsets = {};
        var level = 0;
        var offsetDelta = 0;

        var escapeText = function(text) {
            return text.replace(/\&lt\;\%/g, '&lt;%').replace(/<%/g, '<%').replace(/%>/g, '%>');
        };

        text.replace(innerTempEvaluate, function(match, open, close, offset) {
            offset += offsetDelta;
            if (open) {
                level++;
                levelOffsets[level] = offset;
            }
            if (close && level) {
                var start = escapedText.slice(0, levelOffsets[level]);
                var end = escapedText.slice(offset + close.length);
                var escape = escapedText.slice(levelOffsets[level] + settings.innerTempStart.length, offset);
                var newEscape = escapeText(escape);

                offsetDelta += newEscape.length - escape.length - (settings.innerTempEnd.length * 2);
                escapedText = start + newEscape + end;
                level--;
            }

            // Adobe VMs need the match returned to produce the correct offset.
            return match;
        });
        arguments[1] = _.trim(escapedText);

        return original.apply(this, _.rest(arguments));
    });

    _.defer = asap;

    return _;
});
<|endoftext|>


<commit_before>export { Base };

//!!! ...unfinished... (2022/05/30) Used by Operation.apply()
class OperationApplyArgs {

//   constructor() {
//     this.blockInput0 =
//     this.blockInput1 =
//
//     this.operationInput0 =
//     this.operationInput1 =
//     this.operationOutput0 =
//     this.operationOutput1 =;
  }

}

//!!! ...unfinished... (2022/05/30)
class TensorPlaceholderSet {

  constructor() {
    this.input0 = new TensorPlaceholder();
    this.input1 = new TensorPlaceholder();
    this.output0 = new TensorPlaceholder();
    this.output1 = new TensorPlaceholder();
  }

}

//!!! ...unfinished... (2022/05/30) should create objects representing this operation's output(s).
class Operation {

  constructor() {
    this.tensorPlaceholderSet = new TensorPlaceholderSet();
  }

}

//!!! ...unfinished... (2022/05/30) Rename Base to TensorPlaceholder.
// Add height, width, channelCount info.

/**
 * Count there are how many operations using this operation's output tensor.
 *
 * @member {any} tensorId
 *   This tensor's identifier.
 *
 * @member {object} operationObject
 *   The object which implements the operation. It should have the following method:
 *   - setKeepInputTensor( bKeepInputTensor0, bKeepInputTensor1 )
 *   - pfnOperation( inputTensor0, inputTensor1 )
 *

!!! ...unfinished... (2022/05/30) What if the previous operation has two outputs? (e.g. ConcatShuffleSplit)

 * @member {Base} input0
 *   The TensorOpCounter.Base object which represents this operation's first input.
 *
 * @member {Base} input1
 *   The TensorOpCounter.Base object which represents this operation's second input. It could be null which means this operation
 * does not have second input tensor.
 *
 * @member {Base[]} nextOperationArray
 *   The operations which will use the output tensor of this operation.
 *
 * @member {number} nextOperationsCount
 *   This operation's output tensor is used by how many operations which is behind this operation. This is the same as
 * this.nextOperationArray.length. If zero, the output tensor is not used by other operation.
 *
 *
 *
 */
class Base {

//!!! ...unfinished... (2022/05/30) should create objects representing this operation's output(s).

  constructor( tensorId, operationObject, input0, input1 ) {
    this.tensorId = tensorId;
    this.operationObject = operationObject;
    this.inputArray = [ input0, input1 ];
    this.nextOperationArray = [];

    // Record this operation is one of the input's operations.
    for ( let i = 0; i < this.inputArray.length; ++i ) {
      let input = this.inputArray[ i ];
      if ( input )
        input.nextOperationArray.push( this );
    }

    this.bKeepInputTensorArray = new Array( this.inputArray.length );
  }

  /**
   * Call the this.operationObject.setKeepInputTensor() according to：
   *   - whether the operation is the last operation of the this.input0 / this.input1.
   *   - whether the this.input0 / this.input1 is in alwaysKeepSet.
   *
   * @param {Set<Base>} alwaysKeepSet
   *   A set object. Its every element is TensorOpCounter.Base object. They represent tensors never be disposed. The this.input0
   * and this.input1 will be compared with them.
   */
  setKeepInputTensor_IfNotLastOperation_Or_In( alwaysKeepSet ) {

    if ( !this.operationObject )
      return; // Since there is no operation, there is no need to set up its keep-input flags.

    // Every input which needs to be disposed by this operation will have an entry in this Map.
    //
    // The key is the input TensorOpCounter.Base object.
    // The value is its array index in this.inputArray[] (also in this.bKeepInputTensorArray[]).
    let input_dispose_Map = new Map;

    for ( let i = 0; i < this.inputArray.length; ++i ) {
      let input = this.inputArray[ i ];

      if ( !input )
        continue; // Since the input does not exist, there is no need to dispose it.

      if ( ( alwaysKeepSet ) && ( alwaysKeepSet.has( input ) ) )
        continue; // The input in alwaysKeepSet should always be kept (always not to be disposed).

      if ( input.nextOperationArray.length <= 0 )
        continue; // Since the input does not processed by this operation, there is no need to dispose it. (shoud not happen)

      // If this operation is the last operation of the input tensor, this operation is responsible for disposing it.
      //
      // Note: If an input appears multiple times in this.inputArray[] (i.e. multiple inputs of this operation are the same one input),
      // the map will only record the last one. So that the input will only be disposed once (rather than multiple times).
      if ( input.nextOperationArray[ input.nextOperationArray.length - 1 ] == this )
        input_dispose_Map.set( input, i );

      // Otherwise, This operation uses the input tensor. There are, however, other operations still need use the same input tensor.
      // So the input tensor should not be disposed (i.e. should be kept) by this operation.
    }

    // Find out and mark all the input tensors should be disposed (i.e. will not be kept) by this operation.
    this.bKeepInputTensorArray.fill( true ); // Default is keep every input.
    for ( let [ input, arrayIndex ] of input_dispose_Map ) {
      this.bKeepInputTensorArray[ arrayIndex ] = false;
    }

    // Configure the operation to keep or dispose its inputs.
    this.operationObject.setKeepInputTensor( this.bKeepInputTensorArray[ 0 ], this.bKeepInputTensorArray[ 1 ] );
  }

  get nextOperationsCount() {
    return this.nextOperationArray.length;
  }

}
<commit_msg>Update TensorOpCounter.js<commit_after>export { Base };

//!!! ...unfinished... (2022/05/30) Used by Operation.apply()
class OperationApplyArgs {

//   constructor() {
//     this.blockInput0 =
//     this.blockInput1 =
//
//     this.operationInput0 =
//     this.operationInput1 =
//     this.operationOutput0 =
//     this.operationOutput1 =;
  }

}

//!!! ...unfinished... (2022/05/30)
class TensorPlaceholderSet {

  constructor() {
    this.input0 = new TensorPlaceholder();
    this.input1 = new TensorPlaceholder();
    this.output0 = new TensorPlaceholder();
    this.output1 = new TensorPlaceholder();
  }

}

//!!! ...unfinished... (2022/05/30) should create objects representing this operation's output(s).
class Operation {

  constructor() {
    this.tensorPlaceholderSet = new TensorPlaceholderSet();
  }

}

//!!! ...unfinished... (2022/05/30) Rename Base to TensorPlaceholder.
// Add height, width, channelCount info.

/**
 * Count there are how many operations using this operation's output tensor.
 *
 * @member {any} tensorId
 *   This tensor's identifier.
 *
 * @member {object} operationObject
 *   The object which implements the operation. It should have the following method:
 *   - setKeepInputTensor( bKeepInputTensor0, bKeepInputTensor1 )
 *   - pfnOperation( inputTensor0, inputTensor1 )
 *

!!! ...unfinished... (2022/05/30) What if the previous operation has two outputs? (e.g. ConcatShuffleSplit)

 * @member {Base} input0
 *   The TensorOpCounter.Base object which represents this operation's first input.
 *
 * @member {Base} input1
 *   The TensorOpCounter.Base object which represents this operation's second input. It could be null which means this operation
 * does not have second input tensor.
 *
 * @member {Base[]} nextOperationArray
 *   The operations which will use the output tensor of this operation.
 *
 * @member {number} nextOperationsCount
 *   This operation's output tensor is used by how many operations which is behind this operation. This is the same as
 * this.nextOperationArray.length. If zero, the output tensor is not used by other operation.
 *
 *
 *
 */
class Base {

//!!! ...unfinished... (2022/05/30) should create objects representing this operation's output(s).

  constructor( tensorId, operationObject, input0, input1 ) {
    this.tensorId = tensorId;
    this.operationObject = operationObject;
    this.inputArray = [ input0, input1 ];

//!!! ...unfinished... (2022/05/31)
// It seems that recording the final operation is enough. It is not necessary to record an array.
    this.nextOperationArray = [];

    // Record this operation is one of the input's operations.
    for ( let i = 0; i < this.inputArray.length; ++i ) {
      let input = this.inputArray[ i ];
      if ( input )
        input.nextOperationArray.push( this );
    }

    this.bKeepInputTensorArray = new Array( this.inputArray.length );
  }

  /**
   * Call the this.operationObject.setKeepInputTensor() according to：
   *   - whether the operation is the last operation of the this.input0 / this.input1.
   *   - whether the this.input0 / this.input1 is in alwaysKeepSet.
   *
   * @param {Set<Base>} alwaysKeepSet
   *   A set object. Its every element is TensorOpCounter.Base object. They represent tensors never be disposed. The this.input0
   * and this.input1 will be compared with them.
   */
  setKeepInputTensor_IfNotLastOperation_Or_In( alwaysKeepSet ) {

    if ( !this.operationObject )
      return; // Since there is no operation, there is no need to set up its keep-input flags.

    // Every input which needs to be disposed by this operation will have an entry in this Map.
    //
    // The key is the input TensorOpCounter.Base object.
    // The value is its array index in this.inputArray[] (also in this.bKeepInputTensorArray[]).
    let input_dispose_Map = new Map;

    for ( let i = 0; i < this.inputArray.length; ++i ) {
      let input = this.inputArray[ i ];

      if ( !input )
        continue; // Since the input does not exist, there is no need to dispose it.

      if ( ( alwaysKeepSet ) && ( alwaysKeepSet.has( input ) ) )
        continue; // The input in alwaysKeepSet should always be kept (always not to be disposed).

      if ( input.nextOperationArray.length <= 0 )
        continue; // Since the input does not processed by this operation, there is no need to dispose it. (shoud not happen)

      // If this operation is the last operation of the input tensor, this operation is responsible for disposing it.
      //
      // Note: If an input appears multiple times in this.inputArray[] (i.e. multiple inputs of this operation are the same one input),
      // the map will only record the last one. So that the input will only be disposed once (rather than multiple times).
      if ( input.nextOperationArray[ input.nextOperationArray.length - 1 ] == this )
        input_dispose_Map.set( input, i );

      // Otherwise, This operation uses the input tensor. There are, however, other operations still need use the same input tensor.
      // So the input tensor should not be disposed (i.e. should be kept) by this operation.
    }

    // Find out and mark all the input tensors should be disposed (i.e. will not be kept) by this operation.
    this.bKeepInputTensorArray.fill( true ); // Default is keep every input.
    for ( let [ input, arrayIndex ] of input_dispose_Map ) {
      this.bKeepInputTensorArray[ arrayIndex ] = false;
    }

    // Configure the operation to keep or dispose its inputs.
    this.operationObject.setKeepInputTensor( this.bKeepInputTensorArray[ 0 ], this.bKeepInputTensorArray[ 1 ] );
  }

  get nextOperationsCount() {
    return this.nextOperationArray.length;
  }

}
<|endoftext|>