<commit_before>var ResultsController = function () {

};

ResultsController.results = function () {
    var results = ResultsController.compute_results();
    if (results == null) {
        alert("You will never pay off this loan! Please try again!");
    }
    else {
        ResultsController.draw_total_results(results);
        ResultsController.draw_loan_results(results);
    }
    return results;
};

ResultsController.draw_total_results = function (results) {
    var source = $("#total-results-template").html();
    var template = Handlebars.compile(source);
    var html = template(results.totals);
    $("#total-results").empty().append(html);
    $("#total-results").hide().fadeIn('500');
};

ResultsController.draw_loan_results = function (results) {
    var source = $("#loan-results-template").html();
    var template = Handlebars.compile(source);
    var html = template(results);
    $("#loan-results").empty().append(html);
    $("#loan-results").hide().fadeIn('500');
    for (var loan_key in results.loans) {
        console.log(loan_key);
        Router.add_loan_table_result_listener(loan_key);
    }
};

/*
 Loan step by step

 Generate interest
 Pay min payment
 *Pay interest
 *Pay principal
 If leftover money, add to extra_payment
 Remove loans at 0
 Apply extra payment to loans until extra payment is 0 or remaining_loans is 0
 Sort based on payment type
 Remove loans as paid
 Increment month

 Loop above until remaining loans.size is 0

 */

ResultsController.loan_results = {"loans": {}, "totals": {} };

//dicts and arrays are passed by reference
// .add(1,'months');
// .format("MMMM YYYY")
// need to move current_interest to current_principal

ResultsController.compute_results = function () {

    var loans_dict = window.loans;

    var loans = [];
    for (var index in loans_dict) {
        loans.push(loans_dict[index]);
    }

    return ResultsController.calculate(loans, window.payment_type, window.monthly_payment);

};

ResultsController.calculate = function (loans, payment_type, monthly_payment) {
    ResultsController.loan_results = {"loans": {}, "totals": {} };
    var remaining_loans = {};
    var current_principal = {};
    var current_interest = {};
    var minimum_payments = {};
    var month = moment().startOf('month');

    for (var loan_key in loans) {
        ResultsController.loan_results.loans[loan_key] = {"rows": [], "loan_name": loans[loan_key].loanName,
            "total_date": "0", "total_interest_paid": 0, "id": loan_key, "starting_balance": loans[loan_key].currentBalance};
        current_principal[loan_key] = loans[loan_key].currentBalance;
        current_interest[loan_key] = 0;
        remaining_loans[loan_key] = loans[loan_key];
        ResultsController.log_default_line(loan_key, month);
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        ResultsController.log_balance_remaining_line(loan_key, current_balance);

    }

    //loop
    var counter = 0;
    while (Object.keys(remaining_loans).length > 0) {
        counter++;
        if (counter > 12*50){
            return null;
        }
        month.add(1, 'months');
        var extra_payment = monthly_payment;
        for (var loan_key in remaining_loans) {
            minimum_payments[loan_key] = remaining_loans[loan_key].minimumPayment;
            extra_payment = extra_payment - minimum_payments[loan_key];
            ResultsController.log_default_line(loan_key, month)
        }
        // pay minimums first
        ResultsController.pay_minimums(remaining_loans, current_principal, current_interest, minimum_payments);
        extra_payment = extra_payment + ResultsController.add_leftover_payments(minimum_payments);
        ResultsController.remove_paid_off_loans(remaining_loans, current_principal, current_interest, minimum_payments);
        ResultsController.apply_extra_payments(remaining_loans, current_principal, current_interest, payment_type, extra_payment, minimum_payments);
        for (var loan_key in remaining_loans) {
            var current_balance = current_principal[loan_key] + current_interest[loan_key];

            ResultsController.log_balance_remaining_line(loan_key, current_balance);
        }

        ResultsController.add_interest(remaining_loans, current_principal, current_interest);

    }

    ResultsController.log_totals(month);
    return ResultsController.loan_results;

};

ResultsController.remove_paid_off_loans = function (remaining_loans, current_principal, current_interest, minimum_payments) {
    for (var loan_key in remaining_loans) {
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        if (current_balance == 0) {
            ResultsController.remove_loan(remaining_loans, current_principal, current_interest, minimum_payments, loan_key);
        }
    }
};

ResultsController.remove_loan = function (remaining_loans, current_principal, current_interest, minimum_payments, loan_key) {
    delete remaining_loans[loan_key];
    delete current_principal[loan_key];
    delete current_interest[loan_key];
    delete minimum_payments[loan_key];
};

ResultsController.pay_minimums = function (remaining_loans, current_principal, current_interest, minimum_payments) {
    for (var loan_key in remaining_loans) {
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        var left_over = ResultsController.apply_payment(loan_key, minimum_payments[loan_key], current_interest, current_principal);
        minimum_payments[loan_key] = left_over;

    }
};

ResultsController.apply_payment = function (id, payment, interest_dict, principal_dict) {

    // interest higher than payment
    if (interest_dict[id] > payment) {
        ResultsController.log_interest_paid_line(id, payment);
        interest_dict[id] = interest_dict[id] - payment;
        return 0;
    }
    // interest equal to payment
    else if (interest_dict[id] == payment) {
        ResultsController.log_interest_paid_line(id, payment);
        interest_dict[id] = interest_dict[id] - payment;
        return 0;
    }
    // payment higher than interest left
    else {
        ResultsController.log_interest_paid_line(id, interest_dict[id]);
        var remaining_payment = payment - interest_dict[id];
        interest_dict[id] = 0;
        // apply to principal
        return ResultsController.principal_apply_payment(id, remaining_payment, principal_dict);
    }
};

ResultsController.principal_apply_payment = function (id, payment, principal_dict) {

    // principal higher than payment
    if (principal_dict[id] > payment) {
        ResultsController.log_principal_paid_line(id, payment);
        principal_dict[id] = principal_dict[id] - payment;
        return 0;
    }
    // principal equal to payment
    else if (principal_dict[id] == payment) {
        ResultsController.log_principal_paid_line(id, payment);
        principal_dict[id] = principal_dict[id] - payment;
        return 0;
    }
    // payment higher than principal left
    else {

        ResultsController.log_principal_paid_line(id, principal_dict[id]);
        var remaining_payment = payment - principal_dict[id];
        principal_dict[id] = 0;
        return remaining_payment;
    }
};

//assuming monthly
ResultsController.add_interest = function (remaining_loans, current_principal, current_interest) {
    for (var loan_key in remaining_loans) {
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        //assuming monthly
        var interest_generated = current_balance * (remaining_loans[loan_key].interestRate / 100 / 12);
        current_interest[loan_key] = current_interest[loan_key] + ResultsController.precise_round(interest_generated, 2);
    }
};

ResultsController.apply_extra_payments = function (remaining_loans, current_principal, current_interest, payment_type, extra_payment, minimum_payments) {
    var sorted_keys = ResultsController.sort_loans(remaining_loans, current_principal, current_interest, payment_type);
    for (var key in sorted_keys) {
        if (extra_payment == 0 || Object.keys(remaining_loans).length == 0) {
            return extra_payment;
        }
        extra_payment = ResultsController.apply_payment(sorted_keys[key], extra_payment, current_interest, current_principal);
        if ((current_principal[sorted_keys[key]] + current_interest[sorted_keys[key]]) == 0) {
            ResultsController.remove_loan(remaining_loans, current_principal, current_interest, minimum_payments, sorted_keys[key]);
        }
    }
};

ResultsController.add_leftover_payments = function (minimum_payments) {
    var leftover = 0;
    for (var key in minimum_payments) {
        leftover += minimum_payments[key];
    }
    return leftover;
};

ResultsController.precise_round = function (num, decimals) {
    var t = Math.pow(10, decimals);
    return parseFloat((Math.round((num * t) + (decimals > 0 ? 1 : 0) * (Math.sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals));
};

ResultsController.sort_loans = function (remaining_loans, current_principal, current_interest, payment_type) {
    if (payment_type == "snowball") {
        return Object.keys(remaining_loans).sort(function (a, b) {
            return (current_interest[a] + current_principal[a]) - (current_interest[b] + current_principal[b])
        });
    }
    else if (payment_type == "avalanche") {
        return Object.keys(remaining_loans).sort(function (a, b) {
            return (remaining_loans[b].interestRate) - (remaining_loans[a].interestRate)
        });
    }
    else {
        console.log("something fucky happened")
    }
};

// not sure where I used this
ResultsController.round_interest_diff = function (a) {
    if (a > 0) {
        a = Math.ceil(a);
    }
    else if (a < 0) {
        a = Math.floor(a);

    }
    else {
        return a;
    }
};

ResultsController.arraysEqual = function (a, b) {
    if (a === b) return true;
    if (a == null || b == null) return false;
    if (a.length != b.length) return false;

    // If you don't care about the order of the elements inside
    // the array, you should sort both arrays here.

    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false;
    }
    return true;
};

// loan_results = { "0": [line, line], "1": [line, line] }
// line = {month, payment,principal_paid,interest_paid,balance_remaining}
ResultsController.log_default_line = function (id, date) {
    ResultsController.loan_results.loans[id].rows.push({});
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["date"] = date.format("MMMM YYYY");
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"] = 0;
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["principal_paid"] = 0;
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["interest_paid"] = 0;
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["balance_remaining"] = 0;
};

ResultsController.log_date_line = function (id, date) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["date"] = date.format("MMMM YYYY");
};

ResultsController.log_payment_line = function (id, payment) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"] += ResultsController.precise_round(payment, 2);
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"] =
        ResultsController.precise_round(ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"], 2);
};

ResultsController.log_principal_paid_line = function (id, principal_paid) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["principal_paid"] += ResultsController.precise_round(principal_paid, 2);
    ResultsController.log_payment_line(id, principal_paid);
};

ResultsController.log_interest_paid_line = function (id, interest_paid) {

    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["interest_paid"] += ResultsController.precise_round(interest_paid, 2);
    ResultsController.log_payment_line(id, interest_paid);
};

ResultsController.log_balance_remaining_line = function (id, balance_remaining) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["balance_remaining"] += ResultsController.precise_round(balance_remaining, 2);
};

ResultsController.log_totals = function (date) {
    ResultsController.loan_results.totals["total_date"] = date.format("MMMM YYYY");
    var total_interest_paid = 0;
    for (var loan_key in  ResultsController.loan_results.loans) {
        ResultsController.loan_results.loans[loan_key].total_interest_paid = 0;
        for (var line_key in ResultsController.loan_results.loans[loan_key].rows) {
            ResultsController.loan_results.loans[loan_key].total_interest_paid += ResultsController.loan_results.loans[loan_key].rows[line_key]["interest_paid"];
        }
        ResultsController.loan_results.loans[loan_key].total_interest_paid = ResultsController.precise_round(ResultsController.loan_results.loans[loan_key].total_interest_paid, 2);
        ResultsController.loan_results.loans[loan_key].total_date = ResultsController.loan_results.loans[loan_key].rows[ResultsController.loan_results.loans[loan_key].rows.length - 1]["date"];
        total_interest_paid += ResultsController.loan_results.loans[loan_key].total_interest_paid
    }
    ResultsController.loan_results.totals["total_interest_paid"] = ResultsController.precise_round(total_interest_paid,2);
};

<commit_msg>changed never paid off<commit_after>var ResultsController = function () {

};

ResultsController.results = function () {
    var results = ResultsController.compute_results();
    if (results == null) {
        alert("The loan payoff is more than 122 years, longer than the oldest person ever. You probably need to pay more on the loan or you will never pay it off.");
    }
    else {
        ResultsController.draw_total_results(results);
        ResultsController.draw_loan_results(results);
    }
    return results;
};

ResultsController.draw_total_results = function (results) {
    var source = $("#total-results-template").html();
    var template = Handlebars.compile(source);
    var html = template(results.totals);
    $("#total-results").empty().append(html);
    $("#total-results").hide().fadeIn('500');
};

ResultsController.draw_loan_results = function (results) {
    var source = $("#loan-results-template").html();
    var template = Handlebars.compile(source);
    var html = template(results);
    $("#loan-results").empty().append(html);
    $("#loan-results").hide().fadeIn('500');
    for (var loan_key in results.loans) {
        console.log(loan_key);
        Router.add_loan_table_result_listener(loan_key);
    }
};

/*
 Loan step by step

 Generate interest
 Pay min payment
 *Pay interest
 *Pay principal
 If leftover money, add to extra_payment
 Remove loans at 0
 Apply extra payment to loans until extra payment is 0 or remaining_loans is 0
 Sort based on payment type
 Remove loans as paid
 Increment month

 Loop above until remaining loans.size is 0

 */

ResultsController.loan_results = {"loans": {}, "totals": {} };

//dicts and arrays are passed by reference
// .add(1,'months');
// .format("MMMM YYYY")
// need to move current_interest to current_principal

ResultsController.compute_results = function () {

    var loans_dict = window.loans;

    var loans = [];
    for (var index in loans_dict) {
        loans.push(loans_dict[index]);
    }

    return ResultsController.calculate(loans, window.payment_type, window.monthly_payment);

};

ResultsController.calculate = function (loans, payment_type, monthly_payment) {
    ResultsController.loan_results = {"loans": {}, "totals": {} };
    var remaining_loans = {};
    var current_principal = {};
    var current_interest = {};
    var minimum_payments = {};
    var month = moment().startOf('month');

    for (var loan_key in loans) {
        ResultsController.loan_results.loans[loan_key] = {"rows": [], "loan_name": loans[loan_key].loanName,
            "total_date": "0", "total_interest_paid": 0, "id": loan_key, "starting_balance": loans[loan_key].currentBalance};
        current_principal[loan_key] = loans[loan_key].currentBalance;
        current_interest[loan_key] = 0;
        remaining_loans[loan_key] = loans[loan_key];
        ResultsController.log_default_line(loan_key, month);
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        ResultsController.log_balance_remaining_line(loan_key, current_balance);

    }

    //loop
    var counter = 0;
    while (Object.keys(remaining_loans).length > 0) {
        counter++;
        if (counter > 12*122){
            return null;
        }
        month.add(1, 'months');
        var extra_payment = monthly_payment;
        for (var loan_key in remaining_loans) {
            minimum_payments[loan_key] = remaining_loans[loan_key].minimumPayment;
            extra_payment = extra_payment - minimum_payments[loan_key];
            ResultsController.log_default_line(loan_key, month)
        }
        // pay minimums first
        ResultsController.pay_minimums(remaining_loans, current_principal, current_interest, minimum_payments);
        extra_payment = extra_payment + ResultsController.add_leftover_payments(minimum_payments);
        ResultsController.remove_paid_off_loans(remaining_loans, current_principal, current_interest, minimum_payments);
        ResultsController.apply_extra_payments(remaining_loans, current_principal, current_interest, payment_type, extra_payment, minimum_payments);
        for (var loan_key in remaining_loans) {
            var current_balance = current_principal[loan_key] + current_interest[loan_key];

            ResultsController.log_balance_remaining_line(loan_key, current_balance);
        }

        ResultsController.add_interest(remaining_loans, current_principal, current_interest);

    }

    ResultsController.log_totals(month);
    return ResultsController.loan_results;

};

ResultsController.remove_paid_off_loans = function (remaining_loans, current_principal, current_interest, minimum_payments) {
    for (var loan_key in remaining_loans) {
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        if (current_balance == 0) {
            ResultsController.remove_loan(remaining_loans, current_principal, current_interest, minimum_payments, loan_key);
        }
    }
};

ResultsController.remove_loan = function (remaining_loans, current_principal, current_interest, minimum_payments, loan_key) {
    delete remaining_loans[loan_key];
    delete current_principal[loan_key];
    delete current_interest[loan_key];
    delete minimum_payments[loan_key];
};

ResultsController.pay_minimums = function (remaining_loans, current_principal, current_interest, minimum_payments) {
    for (var loan_key in remaining_loans) {
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        var left_over = ResultsController.apply_payment(loan_key, minimum_payments[loan_key], current_interest, current_principal);
        minimum_payments[loan_key] = left_over;

    }
};

ResultsController.apply_payment = function (id, payment, interest_dict, principal_dict) {

    // interest higher than payment
    if (interest_dict[id] > payment) {
        ResultsController.log_interest_paid_line(id, payment);
        interest_dict[id] = interest_dict[id] - payment;
        return 0;
    }
    // interest equal to payment
    else if (interest_dict[id] == payment) {
        ResultsController.log_interest_paid_line(id, payment);
        interest_dict[id] = interest_dict[id] - payment;
        return 0;
    }
    // payment higher than interest left
    else {
        ResultsController.log_interest_paid_line(id, interest_dict[id]);
        var remaining_payment = payment - interest_dict[id];
        interest_dict[id] = 0;
        // apply to principal
        return ResultsController.principal_apply_payment(id, remaining_payment, principal_dict);
    }
};

ResultsController.principal_apply_payment = function (id, payment, principal_dict) {

    // principal higher than payment
    if (principal_dict[id] > payment) {
        ResultsController.log_principal_paid_line(id, payment);
        principal_dict[id] = principal_dict[id] - payment;
        return 0;
    }
    // principal equal to payment
    else if (principal_dict[id] == payment) {
        ResultsController.log_principal_paid_line(id, payment);
        principal_dict[id] = principal_dict[id] - payment;
        return 0;
    }
    // payment higher than principal left
    else {

        ResultsController.log_principal_paid_line(id, principal_dict[id]);
        var remaining_payment = payment - principal_dict[id];
        principal_dict[id] = 0;
        return remaining_payment;
    }
};

//assuming monthly
ResultsController.add_interest = function (remaining_loans, current_principal, current_interest) {
    for (var loan_key in remaining_loans) {
        var current_balance = current_principal[loan_key] + current_interest[loan_key];
        //assuming monthly
        var interest_generated = current_balance * (remaining_loans[loan_key].interestRate / 100 / 12);
        current_interest[loan_key] = current_interest[loan_key] + ResultsController.precise_round(interest_generated, 2);
    }
};

ResultsController.apply_extra_payments = function (remaining_loans, current_principal, current_interest, payment_type, extra_payment, minimum_payments) {
    var sorted_keys = ResultsController.sort_loans(remaining_loans, current_principal, current_interest, payment_type);
    for (var key in sorted_keys) {
        if (extra_payment == 0 || Object.keys(remaining_loans).length == 0) {
            return extra_payment;
        }
        extra_payment = ResultsController.apply_payment(sorted_keys[key], extra_payment, current_interest, current_principal);
        if ((current_principal[sorted_keys[key]] + current_interest[sorted_keys[key]]) == 0) {
            ResultsController.remove_loan(remaining_loans, current_principal, current_interest, minimum_payments, sorted_keys[key]);
        }
    }
};

ResultsController.add_leftover_payments = function (minimum_payments) {
    var leftover = 0;
    for (var key in minimum_payments) {
        leftover += minimum_payments[key];
    }
    return leftover;
};

ResultsController.precise_round = function (num, decimals) {
    var t = Math.pow(10, decimals);
    return parseFloat((Math.round((num * t) + (decimals > 0 ? 1 : 0) * (Math.sign(num) * (10 / Math.pow(100, decimals)))) / t).toFixed(decimals));
};

ResultsController.sort_loans = function (remaining_loans, current_principal, current_interest, payment_type) {
    if (payment_type == "snowball") {
        return Object.keys(remaining_loans).sort(function (a, b) {
            return (current_interest[a] + current_principal[a]) - (current_interest[b] + current_principal[b])
        });
    }
    else if (payment_type == "avalanche") {
        return Object.keys(remaining_loans).sort(function (a, b) {
            return (remaining_loans[b].interestRate) - (remaining_loans[a].interestRate)
        });
    }
    else {
        console.log("something fucky happened")
    }
};

// not sure where I used this
ResultsController.round_interest_diff = function (a) {
    if (a > 0) {
        a = Math.ceil(a);
    }
    else if (a < 0) {
        a = Math.floor(a);

    }
    else {
        return a;
    }
};

ResultsController.arraysEqual = function (a, b) {
    if (a === b) return true;
    if (a == null || b == null) return false;
    if (a.length != b.length) return false;

    // If you don't care about the order of the elements inside
    // the array, you should sort both arrays here.

    for (var i = 0; i < a.length; ++i) {
        if (a[i] !== b[i]) return false;
    }
    return true;
};

// loan_results = { "0": [line, line], "1": [line, line] }
// line = {month, payment,principal_paid,interest_paid,balance_remaining}
ResultsController.log_default_line = function (id, date) {
    ResultsController.loan_results.loans[id].rows.push({});
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["date"] = date.format("MMMM YYYY");
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"] = 0;
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["principal_paid"] = 0;
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["interest_paid"] = 0;
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["balance_remaining"] = 0;
};

ResultsController.log_date_line = function (id, date) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["date"] = date.format("MMMM YYYY");
};

ResultsController.log_payment_line = function (id, payment) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"] += ResultsController.precise_round(payment, 2);
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"] =
        ResultsController.precise_round(ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["payment"], 2);
};

ResultsController.log_principal_paid_line = function (id, principal_paid) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["principal_paid"] += ResultsController.precise_round(principal_paid, 2);
    ResultsController.log_payment_line(id, principal_paid);
};

ResultsController.log_interest_paid_line = function (id, interest_paid) {

    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["interest_paid"] += ResultsController.precise_round(interest_paid, 2);
    ResultsController.log_payment_line(id, interest_paid);
};

ResultsController.log_balance_remaining_line = function (id, balance_remaining) {
    ResultsController.loan_results.loans[id].rows[ResultsController.loan_results.loans[id].rows.length - 1]["balance_remaining"] += ResultsController.precise_round(balance_remaining, 2);
};

ResultsController.log_totals = function (date) {
    ResultsController.loan_results.totals["total_date"] = date.format("MMMM YYYY");
    var total_interest_paid = 0;
    for (var loan_key in  ResultsController.loan_results.loans) {
        ResultsController.loan_results.loans[loan_key].total_interest_paid = 0;
        for (var line_key in ResultsController.loan_results.loans[loan_key].rows) {
            ResultsController.loan_results.loans[loan_key].total_interest_paid += ResultsController.loan_results.loans[loan_key].rows[line_key]["interest_paid"];
        }
        ResultsController.loan_results.loans[loan_key].total_interest_paid = ResultsController.precise_round(ResultsController.loan_results.loans[loan_key].total_interest_paid, 2);
        ResultsController.loan_results.loans[loan_key].total_date = ResultsController.loan_results.loans[loan_key].rows[ResultsController.loan_results.loans[loan_key].rows.length - 1]["date"];
        total_interest_paid += ResultsController.loan_results.loans[loan_key].total_interest_paid
    }
    ResultsController.loan_results.totals["total_interest_paid"] = ResultsController.precise_round(total_interest_paid,2);
};

<|endoftext|>


<commit_before>'use strict';

var fs = require('fs');
var path = require('path');
var whacko = require('whacko');
var async = require('async');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

function PathMap () {
  EventEmitter.call(this);

  this.length = 0;
  this.paths = {};
};

util.inherits(PathMap, EventEmitter);

PathMap.prototype.insert = function(filename, param) {
  if (this.find(filename)) {
    return this;
  }

  param = param || {};

  this.paths[filename] = {param: param};
  this.length++;
  this.emit('insert', filename);

  return this;
};

PathMap.prototype.erase = function(filename) {
  if (this.find(filename)) {
    delete this.paths[filename];
    this.length--;
  }

  this.emit('erase', filename);

  return this;
};

PathMap.prototype.find = function(filename) {
  return this.paths[filename];
};

PathMap.prototype.empty = function() {
  return this.length === 0;
};

function Bundler (opts) {
  EventEmitter.call(this);

  this.opts = opts || {};

  // create a map for components
  this._components = new PathMap();

  // create a output file
  this._fd = fs.openSync(opts.output, 'w');

  // create a write queue
  this._writeq = async.queue(function (task, done) {
    fs.write(this._fd, task.data, null, null, function(err) {
      done(err, task);
    });
  }.bind(this), opts.workerCount || 4);

  // save options
  this._excludes = opts.excludes;

  // create components map
  this._components.on('erase', function() {
    this.emit('erase');
    if (this._components.empty()) {
      fs.closeSync(this._fd);
      this.emit('fin');
    }
  }.bind(this));

  // status information
  this.status = {
    writens: [],
    reads: []
  };

  // bind write / read events
  this.on('writen', function(filename) {
    this.status.writens.push(filename);
  }).on('read', function(filename) {
    this.status.reads.push(filename);
  });
};

util.inherits(Bundler, EventEmitter);

Bundler.prototype._import = function (filename, parent) {
  var self = this;
  var components = self._components;

  // exclude the file that already read
  if (components.find(filename)) {
    return;
  }

  // register the file to the list that it will be concatenated with output file
  components.insert(filename, parent ? 'root' : path.basename(filename) + '|' + parent);

  fs.readFile(filename, 'utf8', function(err, data) {
    if (err) {
      throw err;
    }

    var $ = whacko.load(data);

    self.emit('read', filename);

    // tie links existing on the file in the bundle
    $('link[rel="import"][href]').each(function() {
      var to = path.resolve(path.dirname(filename), $(this).attr('href'));
      self._import(to, parent);
    });

    // exclude the file then erase filename on the list
    if (self._excludes.test(filename)) {
      components.erase(filename);
      return;
    }

    // push filedata to writing queue, then erase filename on the list
    self._writeq.push({
      filename: filename,
      basename: path.basename(filename),
      data: data
    }, function(err, task) {
      components.erase(task.filename);
      self.emit('writen', task.filename);
    });
  });
}

Bundler.prototype.tieup = function () {
  this._import(this.opts.input);
}

function polyflow(action, flags, done) {
  var bundler = new Bundler({
    excludes: /\/elements\/|polymer.html$/,
    input: flags.i,
    output: path.resolve(path.dirname(flags.i), flags.o ? flags.o : 'components.html')
  });

  bundler.on('fin', function() {
    console.log('GOT SIGNAL, SEND SIGNAL');
    console.log(bundler.status);
    process.exit(0);
  });

  bundler.tieup();

  process.stdin.resume();
}

module.exports = polyflow;
<commit_msg>dump element, script and stylrs<commit_after>'use strict';

var fs = require('fs');
var path = require('path');
var whacko = require('whacko');
var async = require('async');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

function PathMap () {
  EventEmitter.call(this);

  this.length = 0;
  this.paths = {};
};

util.inherits(PathMap, EventEmitter);

PathMap.prototype.insert = function(filename, param) {
  if (this.find(filename)) {
    return this;
  }

  param = param || {};

  this.paths[filename] = {param: param};
  this.length++;
  this.emit('insert', filename);

  return this;
};

PathMap.prototype.erase = function(filename) {
  if (this.find(filename)) {
    delete this.paths[filename];
    this.length--;
  }

  this.emit('erase', filename);

  return this;
};

PathMap.prototype.find = function(filename) {
  return this.paths[filename];
};

PathMap.prototype.empty = function() {
  return this.length === 0;
};

function Bundler (opts) {
  EventEmitter.call(this);

  this.opts = opts || {};

  // initialize source data structure for js/html/style
  this._datas = {
    htmls: '',
    scripts: '',
    styles: ''
  };

  // create a map for paths
  this._paths = new PathMap();

  this.html = whacko.load('<!DOCTYPE html><html><body></body><html>');

  // create a write queue
  this._writeq = async.queue(function (task, done) {
    var $ = task.$;
    var script = $('script:not([type]):not([src]), script[type="text/javascript"]:not([src])');
    var element = $('polymer-element:not([assetpath])');

    console.log(script);
    script.each(function() {
      this._datas.script += $(this).html();
      console.log($(this).html());
    }.bind(this));

    script.remove();
    $('head').remove();
    $('link[rel="import"][href]').remove();

    // this.html.add($('body').html());
    this.html('body').prepend($('body').html());
    done(null, task);

  }.bind(this), opts.workerCount || 4);

  // save options
  this._excludes = opts.excludes;

  // create paths map
  this._paths.on('erase', function() {
    this.emit('erase');
    if (this._paths.empty()) {
      // create a output file
      var html = fs.openSync(this.opts.output, 'w');
      var script = fs.openSync(this.opts.output + '.js', 'w');

      // writing
      fs.write(html, this.html.html(), null, null, function(err) {
        fs.write(script, this._datas.script, null, null, function(err) {
          fs.closeSync(html);
          fs.closeSync(script);
          this.emit('fin');
        }.bind(this));
      }.bind(this));
    }
  }.bind(this));

  // status information
  this.status = {
    writens: [],
    reads: []
  };

  // bind write / read events
  this.on('writen', function(filename) {
    this.status.writens.push(filename);
  }).on('read', function(filename) {
    this.status.reads.push(filename);
  });
};

util.inherits(Bundler, EventEmitter);

Bundler.prototype._tieupStyle = function (filename, parent) {
}

Bundler.prototype._import = function (filename, parent) {
  var self = this;
  var paths = self._paths;

  // exclude the file that already read
  if (paths.find(filename)) {
    return;
  }

  // register the file to the list that it will be concatenated with output file
  paths.insert(filename, parent ? 'root' : path.basename(filename) + '|' + parent);

  fs.readFile(filename, 'utf8', function(err, data) {
    if (err) {
      throw err;
    }

    var $ = whacko.load(data);

    self.emit('read', filename);

    // tie links existing on the file in the bundle
    $('link[rel="import"][href]').each(function() {
      var to = path.resolve(path.dirname(filename), $(this).attr('href'));
      self._import(to, parent);
    });

    // exclude the file then erase filename on the list
    if (self._excludes.test(filename)) {
      paths.erase(filename);
      return;
    }

    // push filedata to writing queue, then erase filename on the list
    self._writeq.push({
      filename: filename,
      basename: path.basename(filename),
      $: $,
      data: data // THINK: delete?
    }, function(err, task) {
      paths.erase(task.filename);
      self.emit('writen', task.filename);
    });
  });
}

Bundler.prototype.tieup = function () {
  this._import(this.opts.input);
}

function polyflow(action, flags, done) {
  var bundler = new Bundler({
    excludes: /index.html$|\/elements\/|polymer.html$/,
    input: flags.i,
    output: path.resolve(path.dirname(flags.i), flags.o ? flags.o : 'components.html')
  });

  bundler.on('fin', function() {
    console.log('GOT SIGNAL, SEND SIGNAL');
    // console.log(bundler.status);
    process.exit(0);
  });

  bundler.tieup();

  process.stdin.resume();
}

module.exports = polyflow;
<|endoftext|>


<commit_before>/**
 * @provides phabricator-draggable-list
 * @requires javelin-install
 *           javelin-dom
 *           javelin-stratcom
 *           javelin-util
 *           javelin-vector
 *           javelin-magical-init
 * @javelin
 */

JX.install('DraggableList', {

  construct : function(sigil, root) {
    this._sigil = sigil;
    this._root = root || document.body;
    this._group = [this];

    // NOTE: Javelin does not dispatch mousemove by default.
    JX.enableDispatch(document.body, 'mousemove');

    JX.DOM.listen(this._root, 'mousedown', sigil, JX.bind(this, this._ondrag));
    JX.Stratcom.listen('mousemove', null, JX.bind(this, this._onmove));
    JX.Stratcom.listen('scroll', null, JX.bind(this, this._onmove));
    JX.Stratcom.listen('mouseup', null, JX.bind(this, this._ondrop));
  },

  events : [
    'didLock',
    'didUnlock',
    'shouldBeginDrag',
    'didBeginDrag',
    'didCancelDrag',
    'didEndDrag',
    'didDrop',
    'didSend',
    'didReceive'],

  properties : {
    findItemsHandler : null
  },

  members : {
    _root : null,
    _dragging : null,
    _locked : 0,
    _origin : null,
    _originScroll : null,
    _target : null,
    _targets : null,
    _dimensions : null,
    _ghostHandler : null,
    _ghostNode : null,
    _group : null,
    _lastMousePosition: null,
    _lastAdjust: null,

    getRootNode : function() {
      return this._root;
    },

    setGhostHandler : function(handler) {
      this._ghostHandler = handler;
      return this;
    },

    getGhostHandler : function() {
      return this._ghostHandler || JX.bind(this, this._defaultGhostHandler);
    },

    getGhostNode : function() {
      if (!this._ghostNode) {
        this._ghostNode = JX.$N('li', {className: 'drag-ghost'});
      }
      return this._ghostNode;
    },

    setGhostNode : function(node) {
      this._ghostNode = node;
      return this;
    },

    setGroup : function(lists) {
      var result = [];
      var need_self = true;
      for (var ii = 0; ii < lists.length; ii++) {
        if (lists[ii] == this) {
          need_self = false;
        }
        result.push(lists[ii]);
      }

      if (need_self) {
        result.push(this);
      }

      this._group = result;
      return this;
    },

    _canDragX : function() {
      return this._hasGroup();
    },

    _hasGroup : function() {
      return (this._group.length > 1);
    },

    _defaultGhostHandler : function(ghost, target) {
      var parent;

      if (!this._hasGroup()) {
        parent = this._dragging.parentNode;
      } else {
        parent = this.getRootNode();
      }

      if (target && target.nextSibling) {
        parent.insertBefore(ghost, target.nextSibling);
      } else if (!target && parent.firstChild) {
        parent.insertBefore(ghost, parent.firstChild);
      } else {
        parent.appendChild(ghost);
      }
    },

    findItems : function() {
      var handler = this.getFindItemsHandler();
      if (__DEV__) {
        if (!handler) {
          JX.$E('JX.Draggable.findItems(): No findItemsHandler set!');
        }
      }

      return handler();
    },

    _ondrag : function(e) {
      if (this._dragging) {
        // Don't start dragging if we're already dragging something.
        return;
      }

      if (this._locked) {
        // Don't start drag operations while locked.
        return;
      }

      if (!e.isNormalMouseEvent()) {
        // Don't start dragging for shift click, right click, etc.
        return;
      }

      if (this.invoke('shouldBeginDrag', e).getPrevented()) {
        return;
      }

      if (e.getNode('tag:a')) {
        // Never start a drag if we're somewhere inside an <a> tag. This makes
        // links unclickable in Firefox.
        return;
      }

      if (JX.Stratcom.pass()) {
        // Let other handlers deal with this event before we do.
        return;
      }

      e.kill();

      this._dragging = e.getNode(this._sigil);
      this._origin = JX.$V(e);
      this._originScroll = JX.Vector.getAggregateScrollForNode(this._dragging);
      this._dimensions = JX.$V(this._dragging);

      for (var ii = 0; ii < this._group.length; ii++) {
        this._group[ii]._clearTarget();
      }

      if (!this.invoke('didBeginDrag', this._dragging).getPrevented()) {
        // Set the height of all the ghosts in the group. In the normal case,
        // this just sets this list's ghost height.
        for (var jj = 0; jj < this._group.length; jj++) {
          var ghost = this._group[jj].getGhostNode();
          ghost.style.height = JX.Vector.getDim(this._dragging).y + 'px';
        }

        JX.DOM.alterClass(this._dragging, 'drag-dragging', true);
      }
    },

    _getTargets : function() {
      if (this._targets === null) {
        var targets = [];
        var items = this.findItems();
        for (var ii = 0; ii < items.length; ii++) {
          var item = items[ii];

          var ipos = JX.$V(item);
          if (item == this._dragging) {
            // If the item we're measuring is also the item we're dragging,
            // we need to measure its position as though it was still in the
            // list, not its current position in the document (which is
            // under the cursor). To do this, adjust the measured position by
            // removing the offsets we added to put the item underneath the
            // cursor.
            if (this._lastAdjust) {
              ipos.x -= this._lastAdjust.x;
              ipos.y -= this._lastAdjust.y;
            }
          }

          targets.push({
            item: items[ii],
            y: ipos.y + (JX.Vector.getDim(items[ii]).y / 2)
          });
        }
        targets.sort(function(u, v) { return v.y - u.y; });
        this._targets = targets;
      }

      return this._targets;
    },

    _dirtyTargetCache: function() {
      if (this._hasGroup()) {
        var group = this._group;
        for (var ii = 0; ii < group.length; ii++) {
          group[ii]._targets = null;
        }
      } else {
        this._targets = null;
      }

      return this;
    },

    _getTargetList : function(p) {
      var target_list;
      if (this._hasGroup()) {
        var group = this._group;
        for (var ii = 0; ii < group.length; ii++) {
          var root = group[ii].getRootNode();
          var rp = JX.$V(root);
          var rd = JX.Vector.getDim(root);

          var is_target = false;
          if (p.x >= rp.x && p.y >= rp.y) {
            if (p.x <= (rp.x + rd.x) && p.y <= (rp.y + rd.y)) {
              is_target = true;
              target_list = group[ii];
            }
          }

          JX.DOM.alterClass(root, 'drag-target-list', is_target);
        }
      } else {
        target_list = this;
      }

      return target_list;
    },

    _setTarget : function(cur_target) {
      var ghost = this.getGhostNode();
      var target = this._target;

      if (cur_target !== target) {
        this._clearTarget();
        if (cur_target !== false) {
          var ok = this.getGhostHandler()(ghost, cur_target);
          // If the handler returns explicit `false`, prevent the drag.
          if (ok === false) {
            cur_target = false;
          }
        }

        this._target = cur_target;
      }

      return this;
    },

    _clearTarget : function() {
      var target = this._target;
      var ghost = this.getGhostNode();

      if (target !== false) {
        JX.DOM.remove(ghost);
      }

      this._target = false;

      // Clear the target position cache, since adding or removing ghosts
      // changes element positions.
      this._dirtyTargetCache();

      return this;
    },

    _getCurrentTarget : function(p) {
      var ghost = this.getGhostNode();
      var targets = this._getTargets();
      var dragging = this._dragging;

      // Find the node we're dragging the object underneath. This is the first
      // node in the list that's above the cursor. If that node is the node
      // we're dragging or its predecessor, don't select a target, because the
      // operation would be a no-op.

      // NOTE: When we're dragging into the first position in the list, we
      // use the target `null`. When we don't have a valid target, we use
      // the target `false`. Spooky! Magic! Anyway, `null` and `false` mean
      // completely different things.

      var cur_target = null;
      var trigger;
      for (var ii = 0; ii < targets.length; ii++) {
        trigger = targets[ii].y;

        // If the cursor is above this target, we aren't dropping underneath it.
        if (trigger >= p.y) {
          continue;
        }

        // Don't choose the dragged row or its predecessor as targets.
        cur_target = targets[ii].item;
        if (!dragging) {
          // If the item on the cursor isn't from this list, it can't be
          // dropped onto itself or its predecessor in this list.
        } else {
          if (cur_target === dragging) {
            cur_target = false;
          }
          if (targets[ii - 1] && (targets[ii - 1].item === dragging)) {
            cur_target = false;
          }
        }

        break;
      }

      // If the dragged row is the first row, don't allow it to be dragged
      // into the first position, since this operation doesn't make sense.
      if (dragging && cur_target === null) {
        var first_item = targets[targets.length - 1].item;
        if (dragging === first_item) {
          cur_target = false;
        }
      }

      return cur_target;
    },

    _onmove : function(e) {
      // We'll get a callback here for "mousemove" (and can determine the
      // location of the cursor) and also for "scroll" (and can not). If this
      // is a move, save the mouse position, so if we get a scroll next we can
      // reuse the known position.

      if (e.getType() == 'mousemove') {
        this._lastMousePosition = JX.$V(e);
      }

      if (!this._dragging) {
        return;
      }

      if (!this._lastMousePosition) {
        return;
      }

      if (e.getType() == 'scroll') {
        // If this is a scroll event, the positions of drag targets may have
        // changed.
        this._dirtyTargetCache();
      }

      var p = JX.$V(this._lastMousePosition.x, this._lastMousePosition.y);

      var group = this._group;
      var target_list = this._getTargetList(p);

      // Compute the size and position of the drop target indicator, because we
      // need to update our static position computations to account for it.

      var cur_target = false;
      if (target_list) {
        cur_target = target_list._getCurrentTarget(p);
      }

      // If we've selected a new target, update the UI to show where we're
      // going to drop the row.

      for (var ii = 0; ii < group.length; ii++) {
        if (group[ii] == target_list) {
          group[ii]._setTarget(cur_target);
        } else {
          group[ii]._clearTarget();
        }
      }

      // If the drop target indicator is above the cursor in the document,
      // adjust the cursor position for the change in node document position.
      // Do this before choosing a new target to avoid a flash of nonsense.

      var scroll = JX.Vector.getAggregateScrollForNode(this._dragging);

      var origin = {
        x: this._origin.x + (this._originScroll.x - scroll.x),
        y: this._origin.y + (this._originScroll.y - scroll.y)
      };

      var adjust_h = 0;
      var adjust_y = 0;
      if (this._target !== false) {
        var ghost = this.getGhostNode();
        adjust_h = JX.Vector.getDim(ghost).y;
        adjust_y = JX.$V(ghost).y;

        if (adjust_y <= origin.y) {
          p.y -= adjust_h;
        }
      }

      if (this._canDragX()) {
        p.x -= origin.x;
      } else {
        p.x = 0;
      }

      p.y -= origin.y;
      this._lastAdjust = new JX.Vector(p.x, p.y);
      p.setPos(this._dragging);

      e.kill();
    },

    _ondrop : function(e) {
      if (!this._dragging) {
        return;
      }

      var p = JX.$V(e);

      var dragging = this._dragging;
      this._dragging = null;

      var target = false;
      var ghost = false;

      var target_list = this._getTargetList(p);
      if (target_list) {
        target = target_list._target;
        ghost = target_list.getGhostNode();
      }

      JX.$V(0, 0).setPos(dragging);

      if (target !== false) {
        JX.DOM.remove(dragging);
        JX.DOM.replace(ghost, dragging);
        this.invoke('didSend', dragging, target_list);
        target_list.invoke('didReceive', dragging, this);
        target_list.invoke('didDrop', dragging, target, this);
      } else {
        this.invoke('didCancelDrag', dragging);
      }

      var group = this._group;
      for (var ii = 0; ii < group.length; ii++) {
        JX.DOM.alterClass(group[ii].getRootNode(), 'drag-target-list', false);
        group[ii]._clearTarget();
        group[ii]._lastAdjust = null;
      }

      if (!this.invoke('didEndDrag', dragging).getPrevented()) {
        JX.DOM.alterClass(dragging, 'drag-dragging', false);
      }

      e.kill();
    },

    lock : function() {
      for (var ii = 0; ii < this._group.length; ii++) {
        this._group[ii]._lock();
      }
      return this;
    },

    _lock : function() {
      this._locked++;
      if (this._locked === 1) {
        this.invoke('didLock');
      }
      return this;
    },

    unlock: function() {
      for (var ii = 0; ii < this._group.length; ii++) {
        this._group[ii]._unlock();
      }
      return this;
    },

    _unlock : function() {
      if (__DEV__) {
        if (!this._locked) {
          JX.$E('JX.Draggable.unlock(): Draggable is not locked!');
        }
      }
      this._locked--;
      if (!this._locked) {
        this.invoke('didUnlock');
      }
      return this;
    }

  }

});
<commit_msg>Remove an unused property from draggable lists<commit_after>/**
 * @provides phabricator-draggable-list
 * @requires javelin-install
 *           javelin-dom
 *           javelin-stratcom
 *           javelin-util
 *           javelin-vector
 *           javelin-magical-init
 * @javelin
 */

JX.install('DraggableList', {

  construct : function(sigil, root) {
    this._sigil = sigil;
    this._root = root || document.body;
    this._group = [this];

    // NOTE: Javelin does not dispatch mousemove by default.
    JX.enableDispatch(document.body, 'mousemove');

    JX.DOM.listen(this._root, 'mousedown', sigil, JX.bind(this, this._ondrag));
    JX.Stratcom.listen('mousemove', null, JX.bind(this, this._onmove));
    JX.Stratcom.listen('scroll', null, JX.bind(this, this._onmove));
    JX.Stratcom.listen('mouseup', null, JX.bind(this, this._ondrop));
  },

  events : [
    'didLock',
    'didUnlock',
    'shouldBeginDrag',
    'didBeginDrag',
    'didCancelDrag',
    'didEndDrag',
    'didDrop',
    'didSend',
    'didReceive'],

  properties : {
    findItemsHandler : null
  },

  members : {
    _root : null,
    _dragging : null,
    _locked : 0,
    _origin : null,
    _originScroll : null,
    _target : null,
    _targets : null,
    _ghostHandler : null,
    _ghostNode : null,
    _group : null,
    _lastMousePosition: null,
    _lastAdjust: null,

    getRootNode : function() {
      return this._root;
    },

    setGhostHandler : function(handler) {
      this._ghostHandler = handler;
      return this;
    },

    getGhostHandler : function() {
      return this._ghostHandler || JX.bind(this, this._defaultGhostHandler);
    },

    getGhostNode : function() {
      if (!this._ghostNode) {
        this._ghostNode = JX.$N('li', {className: 'drag-ghost'});
      }
      return this._ghostNode;
    },

    setGhostNode : function(node) {
      this._ghostNode = node;
      return this;
    },

    setGroup : function(lists) {
      var result = [];
      var need_self = true;
      for (var ii = 0; ii < lists.length; ii++) {
        if (lists[ii] == this) {
          need_self = false;
        }
        result.push(lists[ii]);
      }

      if (need_self) {
        result.push(this);
      }

      this._group = result;
      return this;
    },

    _canDragX : function() {
      return this._hasGroup();
    },

    _hasGroup : function() {
      return (this._group.length > 1);
    },

    _defaultGhostHandler : function(ghost, target) {
      var parent;

      if (!this._hasGroup()) {
        parent = this._dragging.parentNode;
      } else {
        parent = this.getRootNode();
      }

      if (target && target.nextSibling) {
        parent.insertBefore(ghost, target.nextSibling);
      } else if (!target && parent.firstChild) {
        parent.insertBefore(ghost, parent.firstChild);
      } else {
        parent.appendChild(ghost);
      }
    },

    findItems : function() {
      var handler = this.getFindItemsHandler();
      if (__DEV__) {
        if (!handler) {
          JX.$E('JX.Draggable.findItems(): No findItemsHandler set!');
        }
      }

      return handler();
    },

    _ondrag : function(e) {
      if (this._dragging) {
        // Don't start dragging if we're already dragging something.
        return;
      }

      if (this._locked) {
        // Don't start drag operations while locked.
        return;
      }

      if (!e.isNormalMouseEvent()) {
        // Don't start dragging for shift click, right click, etc.
        return;
      }

      if (this.invoke('shouldBeginDrag', e).getPrevented()) {
        return;
      }

      if (e.getNode('tag:a')) {
        // Never start a drag if we're somewhere inside an <a> tag. This makes
        // links unclickable in Firefox.
        return;
      }

      if (JX.Stratcom.pass()) {
        // Let other handlers deal with this event before we do.
        return;
      }

      e.kill();

      this._dragging = e.getNode(this._sigil);
      this._origin = JX.$V(e);
      this._originScroll = JX.Vector.getAggregateScrollForNode(this._dragging);

      for (var ii = 0; ii < this._group.length; ii++) {
        this._group[ii]._clearTarget();
      }

      if (!this.invoke('didBeginDrag', this._dragging).getPrevented()) {
        // Set the height of all the ghosts in the group. In the normal case,
        // this just sets this list's ghost height.
        for (var jj = 0; jj < this._group.length; jj++) {
          var ghost = this._group[jj].getGhostNode();
          ghost.style.height = JX.Vector.getDim(this._dragging).y + 'px';
        }

        JX.DOM.alterClass(this._dragging, 'drag-dragging', true);
      }
    },

    _getTargets : function() {
      if (this._targets === null) {
        var targets = [];
        var items = this.findItems();
        for (var ii = 0; ii < items.length; ii++) {
          var item = items[ii];

          var ipos = JX.$V(item);
          if (item == this._dragging) {
            // If the item we're measuring is also the item we're dragging,
            // we need to measure its position as though it was still in the
            // list, not its current position in the document (which is
            // under the cursor). To do this, adjust the measured position by
            // removing the offsets we added to put the item underneath the
            // cursor.
            if (this._lastAdjust) {
              ipos.x -= this._lastAdjust.x;
              ipos.y -= this._lastAdjust.y;
            }
          }

          targets.push({
            item: items[ii],
            y: ipos.y + (JX.Vector.getDim(items[ii]).y / 2)
          });
        }
        targets.sort(function(u, v) { return v.y - u.y; });
        this._targets = targets;
      }

      return this._targets;
    },

    _dirtyTargetCache: function() {
      if (this._hasGroup()) {
        var group = this._group;
        for (var ii = 0; ii < group.length; ii++) {
          group[ii]._targets = null;
        }
      } else {
        this._targets = null;
      }

      return this;
    },

    _getTargetList : function(p) {
      var target_list;
      if (this._hasGroup()) {
        var group = this._group;
        for (var ii = 0; ii < group.length; ii++) {
          var root = group[ii].getRootNode();
          var rp = JX.$V(root);
          var rd = JX.Vector.getDim(root);

          var is_target = false;
          if (p.x >= rp.x && p.y >= rp.y) {
            if (p.x <= (rp.x + rd.x) && p.y <= (rp.y + rd.y)) {
              is_target = true;
              target_list = group[ii];
            }
          }

          JX.DOM.alterClass(root, 'drag-target-list', is_target);
        }
      } else {
        target_list = this;
      }

      return target_list;
    },

    _setTarget : function(cur_target) {
      var ghost = this.getGhostNode();
      var target = this._target;

      if (cur_target !== target) {
        this._clearTarget();
        if (cur_target !== false) {
          var ok = this.getGhostHandler()(ghost, cur_target);
          // If the handler returns explicit `false`, prevent the drag.
          if (ok === false) {
            cur_target = false;
          }
        }

        this._target = cur_target;
      }

      return this;
    },

    _clearTarget : function() {
      var target = this._target;
      var ghost = this.getGhostNode();

      if (target !== false) {
        JX.DOM.remove(ghost);
      }

      this._target = false;

      // Clear the target position cache, since adding or removing ghosts
      // changes element positions.
      this._dirtyTargetCache();

      return this;
    },

    _getCurrentTarget : function(p) {
      var ghost = this.getGhostNode();
      var targets = this._getTargets();
      var dragging = this._dragging;

      // Find the node we're dragging the object underneath. This is the first
      // node in the list that's above the cursor. If that node is the node
      // we're dragging or its predecessor, don't select a target, because the
      // operation would be a no-op.

      // NOTE: When we're dragging into the first position in the list, we
      // use the target `null`. When we don't have a valid target, we use
      // the target `false`. Spooky! Magic! Anyway, `null` and `false` mean
      // completely different things.

      var cur_target = null;
      var trigger;
      for (var ii = 0; ii < targets.length; ii++) {
        trigger = targets[ii].y;

        // If the cursor is above this target, we aren't dropping underneath it.
        if (trigger >= p.y) {
          continue;
        }

        // Don't choose the dragged row or its predecessor as targets.
        cur_target = targets[ii].item;
        if (!dragging) {
          // If the item on the cursor isn't from this list, it can't be
          // dropped onto itself or its predecessor in this list.
        } else {
          if (cur_target === dragging) {
            cur_target = false;
          }
          if (targets[ii - 1] && (targets[ii - 1].item === dragging)) {
            cur_target = false;
          }
        }

        break;
      }

      // If the dragged row is the first row, don't allow it to be dragged
      // into the first position, since this operation doesn't make sense.
      if (dragging && cur_target === null) {
        var first_item = targets[targets.length - 1].item;
        if (dragging === first_item) {
          cur_target = false;
        }
      }

      return cur_target;
    },

    _onmove : function(e) {
      // We'll get a callback here for "mousemove" (and can determine the
      // location of the cursor) and also for "scroll" (and can not). If this
      // is a move, save the mouse position, so if we get a scroll next we can
      // reuse the known position.

      if (e.getType() == 'mousemove') {
        this._lastMousePosition = JX.$V(e);
      }

      if (!this._dragging) {
        return;
      }

      if (!this._lastMousePosition) {
        return;
      }

      if (e.getType() == 'scroll') {
        // If this is a scroll event, the positions of drag targets may have
        // changed.
        this._dirtyTargetCache();
      }

      var p = JX.$V(this._lastMousePosition.x, this._lastMousePosition.y);

      var group = this._group;
      var target_list = this._getTargetList(p);

      // Compute the size and position of the drop target indicator, because we
      // need to update our static position computations to account for it.

      var cur_target = false;
      if (target_list) {
        cur_target = target_list._getCurrentTarget(p);
      }

      // If we've selected a new target, update the UI to show where we're
      // going to drop the row.

      for (var ii = 0; ii < group.length; ii++) {
        if (group[ii] == target_list) {
          group[ii]._setTarget(cur_target);
        } else {
          group[ii]._clearTarget();
        }
      }

      // If the drop target indicator is above the cursor in the document,
      // adjust the cursor position for the change in node document position.
      // Do this before choosing a new target to avoid a flash of nonsense.

      var scroll = JX.Vector.getAggregateScrollForNode(this._dragging);

      var origin = {
        x: this._origin.x + (this._originScroll.x - scroll.x),
        y: this._origin.y + (this._originScroll.y - scroll.y)
      };

      var adjust_h = 0;
      var adjust_y = 0;
      if (this._target !== false) {
        var ghost = this.getGhostNode();
        adjust_h = JX.Vector.getDim(ghost).y;
        adjust_y = JX.$V(ghost).y;

        if (adjust_y <= origin.y) {
          p.y -= adjust_h;
        }
      }

      if (this._canDragX()) {
        p.x -= origin.x;
      } else {
        p.x = 0;
      }

      p.y -= origin.y;
      this._lastAdjust = new JX.Vector(p.x, p.y);
      p.setPos(this._dragging);

      e.kill();
    },

    _ondrop : function(e) {
      if (!this._dragging) {
        return;
      }

      var p = JX.$V(e);

      var dragging = this._dragging;
      this._dragging = null;

      var target = false;
      var ghost = false;

      var target_list = this._getTargetList(p);
      if (target_list) {
        target = target_list._target;
        ghost = target_list.getGhostNode();
      }

      JX.$V(0, 0).setPos(dragging);

      if (target !== false) {
        JX.DOM.remove(dragging);
        JX.DOM.replace(ghost, dragging);
        this.invoke('didSend', dragging, target_list);
        target_list.invoke('didReceive', dragging, this);
        target_list.invoke('didDrop', dragging, target, this);
      } else {
        this.invoke('didCancelDrag', dragging);
      }

      var group = this._group;
      for (var ii = 0; ii < group.length; ii++) {
        JX.DOM.alterClass(group[ii].getRootNode(), 'drag-target-list', false);
        group[ii]._clearTarget();
        group[ii]._lastAdjust = null;
      }

      if (!this.invoke('didEndDrag', dragging).getPrevented()) {
        JX.DOM.alterClass(dragging, 'drag-dragging', false);
      }

      e.kill();
    },

    lock : function() {
      for (var ii = 0; ii < this._group.length; ii++) {
        this._group[ii]._lock();
      }
      return this;
    },

    _lock : function() {
      this._locked++;
      if (this._locked === 1) {
        this.invoke('didLock');
      }
      return this;
    },

    unlock: function() {
      for (var ii = 0; ii < this._group.length; ii++) {
        this._group[ii]._unlock();
      }
      return this;
    },

    _unlock : function() {
      if (__DEV__) {
        if (!this._locked) {
          JX.$E('JX.Draggable.unlock(): Draggable is not locked!');
        }
      }
      this._locked--;
      if (!this._locked) {
        this.invoke('didUnlock');
      }
      return this;
    }

  }

});
<|endoftext|>


<commit_before>'use strict';

var db = require('../../db')
  , serializeValue = require('dbjs/_setup/serialize/value')
  , mano = require('mano')
  , dbDriver = mano.dbDriver
  , deferred = require('deferred')
  , userStorage = dbDriver.getStorage('user')
  , User = require('../../model/user')(db);

module.exports = function (data) {
	var def = deferred(), email;
	try {
		email = User.prototype.getOwnDescriptor('email').type.validate(data.email);
		def.resolve(email);
	} catch (err) {
		def.reject(err);
	}

	return def.promise.then(function (email) {
		return userStorage.searchOne({
			key: 'email',
			value: serializeValue(email)
		}, function (foundRecord) {
			return foundRecord;
		}).then(function (foundRecord) {
			if (foundRecord) return;
			return mano.queryMemoryDb([], 'addUser', JSON.stringify(data));
		});
	});
};
<commit_msg>#1912 renamed the function<commit_after>'use strict';

var db = require('../../db')
  , serializeValue = require('dbjs/_setup/serialize/value')
  , mano = require('mano')
  , dbDriver = mano.dbDriver
  , deferred = require('deferred')
  , userStorage = dbDriver.getStorage('user')
  , User = require('../../model/user')(db);

module.exports = function (data) {
	var def = deferred(), email;
	try {
		email = User.prototype.getOwnDescriptor('email').type.validate(data.email);
		def.resolve(email);
	} catch (err) {
		def.reject(err);
	}

	return def.promise.then(function (email) {
		return userStorage.searchOne({
			key: 'email',
			value: serializeValue(email)
		}, function (foundRecord) {
			return foundRecord;
		}).then(function (foundRecord) {
			if (foundRecord) return;
			return mano.queryMemoryDb([], 'addUserByEmail', JSON.stringify(data));
		});
	});
};
<|endoftext|>


<commit_before>angular.module('app.controllers').controller('home', function ($scope, $timeout, socialActivity, homeCtrlParams,
        profile, activity, groups, $ionicScrollDelegate, favorite, $ionicPlatform) {
  $scope.filter = homeCtrlParams.filter;

  $scope.isLoadMore = false;
  var activityCollection = activity.getActivities();

  $scope.changeGroupFilter = function(group){
    var isDifferentGroupThanCurrentlyActive = $scope.filter.selectedGroup != group
    if(!$scope.isSpinnerShow && isDifferentGroupThanCurrentlyActive)
      $scope.showSpinner();

    if(isDifferentGroupThanCurrentlyActive){
      // we want to make sure the spinner is displayed prior the rendering starts
      $timeout(function(){
        $scope.filter.selectedGroup = group
      }, 0);
    }

  }

  function refreshListOfActivities() {
    if(homeCtrlParams.filter.selectedGroup)
      $scope.activities = homeCtrlParams.filter.selectedGroup.activities
    else
      $scope.activities = activityCollection.getFilteredModels();

    //we want to wait until rendering is finished
    $timeout(function(){
      $scope.hideSpinner()
    }, 0);
  }

  function setFiltersData() {
    homeCtrlParams.filter.groups = groups.groupsJoinedByCurrentUser();
    _(homeCtrlParams.filter.groups).each(function (group) {
      group.activities = activityCollection.getFilteredModels(group);
      group.inPriorityZoneCount = activityCollection.inPriorityZoneCount(group.activities);
      group.read = !_.some(group.activities, function (item) {
        return !item.get('read');
      });
      if (homeCtrlParams.filter.selectedGroup && homeCtrlParams.filter.selectedGroup.id === group.id) {
        homeCtrlParams.filter.selectedGroup = group;
      }
    });
    homeCtrlParams.filter.inPriorityZoneCount = activityCollection.inPriorityZoneCount(activityCollection.getFilteredModels());
  }

  function prepare() {
    homeCtrlParams.loaded = true;
    activityCollection.sort();
    setFiltersData();

    $scope.loading = false;
    $ionicScrollDelegate.resize();
  }

  function loadActivities(loadType) {
    var prevSize = activityCollection.size();
    activity.load(loadType).then(function () {
      if (loadType === 'append' && prevSize === activityCollection.size()) {
        $scope.isLoadMore = false;
      } else {
        $scope.isLoadMore = true;
      }
      prepare();

      $scope.$emit('home.activities-reloaded');
      $scope.$broadcast('scroll.refreshComplete');
      $scope.$broadcast('scroll.infiniteScrollComplete');
      refreshListOfActivities();
    }).finally(socialActivity.load);
  }

  $scope.togglePostWindow = function () {
    $scope.showPostWindow = !$scope.showPostWindow;
    $scope.execApply();
  };

  $scope.createNewContent = function (type) {
    var selectedGroup = homeCtrlParams.filter.selectedGroup ? homeCtrlParams.filter.selectedGroup.id : ''
    var p = '/'+type+'/create/' + selectedGroup
    $scope.path(p);
    $scope.showPostWindow = false;
  };

  $scope.filterLineStep = function () {
    return 5;
  };

  $scope.steps = function () {
    var items = homeCtrlParams.filter.groups.length - 3;
    items = items > 0 ? items : 0;
    var step = $scope.filterLineStep();
    var stepsCount = Math.ceil(items / step);
    var steps = [3];
    for (var i = 1; i < stepsCount; i++) {
      steps.push(i * step + 3);
    }
    if (items && 0 === items % step) {
      steps.push(steps[steps.length - 1] + step);
    }
    return steps;
  };

  $scope.loadMoreActivities = function () {
    loadActivities('append');
  };

  $scope.pullToRefresh = function () {
    loadActivities('clearAndLoad');
  };

  $scope.$watch('filter.selectedGroup', refreshListOfActivities);
  $scope.$watch('loading', function () {
    if ($scope.loading) {
      $scope.showSpinner();
    } else if ($scope.loading === false) {
      $scope.hideSpinner();
    }
  });

  $scope.$on('notification.received', function () {
    loadActivities('refresh');
  });

  $scope.$on('activity.reload', function () {
    loadActivities('refresh');
  });

  //move scroll to top when filter is changed
  $scope.$watch('filter.selectedGroup', function (nVal) {
    if (typeof (nVal) !== undefined) {
      $ionicPlatform.ready(function () {
        if($ionicScrollDelegate){
          $ionicScrollDelegate.resize();
          $ionicScrollDelegate.scrollTop();
        }
      })
    }
  });

  //call this when this view is loaded because this view is cached
  $scope.$on('$ionicView.enter', function () {
    if (!profile.get()) {
      profile.load();
    }

    if (!homeCtrlParams.loaded) {
      if (activityCollection.size() === 0) {
        $scope.isLoadMore = true;
      } else {
        loadActivities('refresh');
      }
    } else {
      prepare();
    }

  });
  //call this because cache may be loaded
  setFiltersData();
  refreshListOfActivities();
});

angular.module('app.controllers').run(function (homeCtrlParams, $document, $rootScope) {
  $document.bind('resume', function () {
    homeCtrlParams.loaded = false;
    $rootScope.$broadcast('activity.reload');
    $rootScope.execApply();
  });
});

angular.module('app.controllers').controller('preload', function (topBar, session, profile, $location) {
  if (session.token) {
    if (session.is_registration_complete) {
      profile.load().then(function () {
        profile.checkRemind();
      });
      if (!$location.path() || '/' === $location.path() || '/preload' === $location.path()) {
        $location.path('/main');
      }
    } else {
      $location.path('/profile');
    }
  } else {
    $location.path('/login');
  }
});

angular.module('app.controllers').directive('iActivity', function ($rootScope, questions, petitions, discussion, elapsedFilter, follows, session, iParse, $sce, favorite, microPetitions, leaderContentHelper, posts, userPetitions) {

  function eventCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/event.html';
  }

  function newsCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/news.html';
    $scope.entity = $scope.activity.get('entity');
    $scope.rate = function (action) {
      $scope.sending = true;
      discussion.loadRoot('poll', $scope.entity.id).then(function (comment) {
        discussion.rate(comment, action).then(function (comment) {
          $scope.activity.set('rate_up', comment.rate_up).set('rate_down', comment.rate_down);
          $scope.sending = false;
        }, function () {
          $scope.sending = false;
        });
      });
    };
  }

  function paymentCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/payment.html';
  }

  function postCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/post.html';
    $scope.currentUserIsActivityOwner = $scope.activity.get('owner').id == session.user_id
    $scope.booster = $scope.activity.get('owner').type === 'group' ? 100 : $scope.activity.getQuorumCompletedPercent();

    $scope.upvote = function(){
      var postID = $scope.activity.get('entity').id
      $scope.sending = true;
      posts.upvote(postID).then(function(answer){
        $scope.activity.setAnswer(answer);
        $scope.sending = false;
        $scope.showToast('Post upvoted!');
      })
    }

    $scope.downvote = function(){
      var postID = $scope.activity.get('entity').id
      $scope.sending = true;
      posts.downvote(postID).then(function(answer){
        $scope.activity.setAnswer(answer);
        $scope.sending = false;
        $scope.showToast('Post downvoted!');
      })
    }

    $scope.unvote = function(){
      var postID = $scope.activity.get('entity').id
      $scope.sending = true;
      posts.unvote(postID).then(function(answer){
        $scope.activity.unAnswer();
        $scope.sending = false;
        $scope.showToast('Post vote undo successful!');
      })
    }

  }

  function userPetitionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/user-petition.html';

    $scope.sign = function () {
      var userPetitionID = $scope.activity.get('entity').id
      $scope.sending = true
      userPetitions.sign(userPetitionID).then(function(){
          $scope.activity.markAsSigned()
          $scope.sending = false;
          $scope.showToast('User petition signed.');
      })
    };

    $scope.unsign = function () {
      $scope.sending = true;
      var userPetitionID = $scope.activity.get('entity').id;
      userPetitions.unsign(userPetitionID).then(function (response) {
        $scope.activity.markAsUnsigned()
        $scope.sending = false;
        $scope.showToast('User petition unsigned.');
      });
    };

    $scope.subscribeToNotifications = function(){
      var userPetitionID = $scope.activity.get('entity').id;
      userPetitions.subscribeToNotifications(userPetitionID).then(function (response) {
        $scope.activity.markAsSubscribed()
        $scope.showToast('Subscribed to petition notifications.');
      })
    }

    $scope.unsubscribeFromNotifications = function(){
      var userPetitionID = $scope.activity.get('entity').id;
      userPetitions.unsubscribeFromNotifications(userPetitionID).then(function (response) {
        $scope.activity.markAsUnsubscribed()
        $scope.showToast('Unsubscribed from petition notifications.');
      })
    }
  }

  function petitionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/user-petition.html';

    $scope.sign = function () {
      var microPetitionID = $scope.activity.get('entity').id
      $scope.sending = true
      microPetitions.signLongPetition(microPetitionID).then(function(){
          $scope.activity.markAsSigned()
          $scope.sending = false;
          $scope.showToast('Petition signed.');
      })
    };

    $scope.unsign = function () {
      $scope.sending = true;
      var microPetitionID = $scope.activity.get('entity').id;
      microPetitions.unsignLongPetition(microPetitionID).then(function (response) {
        $scope.activity.markAsSigned()
        $scope.sending = false;
        $scope.showToast('Petition unsigned.');
      });
    };
  }

  function questionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/question.html';
  }

  var ctrlByType = {
    'leader-event': eventCtrl,
    'leader-news': newsCtrl,
    'crowdfunding-payment-request': paymentCtrl,
    'payment-request': paymentCtrl,
    'post': postCtrl,
    'user-petition': userPetitionCtrl,
    'petition': petitionCtrl,
    'question': questionCtrl
  };

  return {
    scope: {
      activity: '='
    },
    restrict: 'E',
    template: '<ng-include src="templateSrc"></ng-include>',
    controller: function ($scope) {
      $scope.showToast = $rootScope.showToast;
      $scope.navigateTo = $rootScope.navigateTo;
      $scope.isBookmarked = function(){
        return favorite.isBookmarked($scope.activity)
      }
      $scope.bookmarkButtonClicked = function(){
        if($scope.isBookmarked()){
          favorite.removeBookmark($scope.activity)
          $scope.showToast('Item was removed from Favorites');
        } else {
         favorite.addBookmark($scope.activity)
         $scope.showToast('Item was added to Favorites');
        }
      }
      $scope.navigateToActivity = function (activity, focus, e) {

        activity.setRead();
        if (e && e.target.tagName.toLowerCase() === 'hash-tag') {
          $rootScope.openTag(angular.element(e.target).text());
        } else {
          $rootScope.navigateTo('activity', activity, focus);
        }
      };

      // if($scope.activity.get('entity').id == 242)
      //     console.log(JSON.stringify($scope.activity))

      $scope.title = $scope.activity.get('title');
      var description_raw = $scope.activity.get('description_html')
      if(description_raw == null || description_raw.length == 0)
        description_raw = $scope.activity.get('description')
      $scope.description = iParse.wrapHashTags(description_raw)
      $scope.avatar_file_path = $scope.activity.get('owner').avatar_file_path;
      $scope.iconClass = $scope.activity.getIcon();
      $scope.official_title = $scope.activity.get('owner').official_title;
      $scope.full_name = [$scope.activity.get('owner').first_name, $scope.activity.get('owner').last_name].join(' ');
      $scope.owner_info_1 = $scope.activity.get('owner_info_1');
      $scope.sent_at_elapsed = elapsedFilter($scope.activity.get('sent_at'));
      $scope.responses_count = $scope.activity.get('responses_count');
      $scope.comments_count = $scope.activity.get('comments_count') - 1;
      $scope.isDefaultAvatar = $rootScope.isDefaultAvatar($scope.avatar_file_path);

      var activityOwnerID = $scope.activity.get('owner').id
      var activityOwnerFollow = follows.getOrCreateUser(activityOwnerID);

      $scope.showFollow = follows.loaded && activityOwnerID != session.user_id
      $scope.followClicked = function(){
        if(activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && activityOwnerFollow.isApproved())
          $scope.showToast('You are following this user');
        else if (activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && !activityOwnerFollow.isApproved())
          $scope.showToast('Waiting for user to approve...');
        else {
          $scope.sending = true;
          activityOwnerFollow.followByCurrentUser().then(function () {
            $scope.activity.followable = false;
            $scope.sending = false;
            $scope.showToast('Follow request sent!');
            follows.load().then(function(){
              activityOwnerFollow = follows.getOrCreateUser(activityOwnerID);
            })
          });
          msg = 'Follow request sent!'
        }
      }

      $scope.openUrlInExternalBrowser = function(url){
        if(url.substring(0,4) != 'http')
          url = 'http://'+url
        $rootScope.openSystem(url)
      }

      $scope.followIcons = function(){
        if(activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && activityOwnerFollow.isApproved())
          return ['icon ion-person calm', 'icon ion-minus-circled']
        else if(activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && !activityOwnerFollow.isApproved())
          return ['icon ion-person', 'icon ion-android-time calm']
        else
          return ['icon ion-person', 'icon ion-plus-circled calm']
      }

      var activityType = $scope.activity.dataType()
      if (ctrlByType[activityType]) {
        ctrlByType[activityType]($scope);
      } else {
        $scope.templateSrc = 'templates/home/activities/default.html';
      }
    }
  };
});<commit_msg>minor<commit_after>angular.module('app.controllers').controller('home', function ($scope, $timeout, socialActivity, homeCtrlParams,
        profile, activity, groups, $ionicScrollDelegate, favorite, $ionicPlatform) {
  $scope.filter = homeCtrlParams.filter;

  $scope.isLoadMore = false;
  var activityCollection = activity.getActivities();

  $scope.changeGroupFilter = function(group){
    var isDifferentGroupThanCurrentlyActive = $scope.filter.selectedGroup != group
    if(!$scope.isSpinnerShow && isDifferentGroupThanCurrentlyActive)
      $scope.showSpinner();

    if(isDifferentGroupThanCurrentlyActive){
      // we want to make sure the spinner is displayed prior the rendering starts
      $timeout(function(){
        $scope.filter.selectedGroup = group
      }, 0);
    }

  }

  function refreshListOfActivities() {
    if(homeCtrlParams.filter.selectedGroup)
      $scope.activities = homeCtrlParams.filter.selectedGroup.activities
    else
      $scope.activities = activityCollection.getFilteredModels();

    //we want to wait until rendering is finished
    $timeout(function(){
      $scope.hideSpinner()
    }, 0);
  }

  function setFiltersData() {
    homeCtrlParams.filter.groups = groups.groupsJoinedByCurrentUser();
    _(homeCtrlParams.filter.groups).each(function (group) {
      group.activities = activityCollection.getFilteredModels(group);
      group.inPriorityZoneCount = activityCollection.inPriorityZoneCount(group.activities);
      group.read = !_.some(group.activities, function (item) {
        return !item.get('read');
      });
      if (homeCtrlParams.filter.selectedGroup && homeCtrlParams.filter.selectedGroup.id === group.id) {
        homeCtrlParams.filter.selectedGroup = group;
      }
    });
    homeCtrlParams.filter.inPriorityZoneCount = activityCollection.inPriorityZoneCount(activityCollection.getFilteredModels());
  }

  function prepare() {
    homeCtrlParams.loaded = true;
    activityCollection.sort();
    setFiltersData();

    $scope.loading = false;
    $ionicScrollDelegate.resize();
  }

  function loadActivities(loadType) {
    var prevSize = activityCollection.size();
    activity.load(loadType).then(function () {
      if (loadType === 'append' && prevSize === activityCollection.size()) {
        $scope.isLoadMore = false;
      } else {
        $scope.isLoadMore = true;
      }
      prepare();

      $scope.$emit('home.activities-reloaded');
      $scope.$broadcast('scroll.refreshComplete');
      $scope.$broadcast('scroll.infiniteScrollComplete');
      refreshListOfActivities();
    }).finally(socialActivity.load);
  }

  $scope.togglePostWindow = function () {
    $scope.showPostWindow = !$scope.showPostWindow;
    $scope.execApply();
  };

  $scope.createNewContent = function (type) {
    var selectedGroup = homeCtrlParams.filter.selectedGroup ? homeCtrlParams.filter.selectedGroup.id : ''
    var p = '/'+type+'/create/' + selectedGroup
    $scope.path(p);
    $scope.showPostWindow = false;
  };

  $scope.filterLineStep = function () {
    return 5;
  };

  $scope.steps = function () {
    var items = homeCtrlParams.filter.groups.length - 3;
    items = items > 0 ? items : 0;
    var step = $scope.filterLineStep();
    var stepsCount = Math.ceil(items / step);
    var steps = [3];
    for (var i = 1; i < stepsCount; i++) {
      steps.push(i * step + 3);
    }
    if (items && 0 === items % step) {
      steps.push(steps[steps.length - 1] + step);
    }
    return steps;
  };

  $scope.loadMoreActivities = function () {
    loadActivities('append');
  };

  $scope.pullToRefresh = function () {
    loadActivities('clearAndLoad');
  };

  $scope.$watch('filter.selectedGroup', refreshListOfActivities);
  $scope.$watch('loading', function () {
    if ($scope.loading) {
      $scope.showSpinner();
    } else if ($scope.loading === false) {
      $scope.hideSpinner();
    }
  });

  $scope.$on('notification.received', function () {
    loadActivities('refresh');
  });

  $scope.$on('activity.reload', function () {
    loadActivities('refresh');
  });

  //move scroll to top when filter is changed
  $scope.$watch('filter.selectedGroup', function (nVal) {
    if (typeof (nVal) !== undefined) {
      $ionicPlatform.ready(function () {
        if($ionicScrollDelegate){
          $ionicScrollDelegate.resize();
          $ionicScrollDelegate.scrollTop();
        }
      })
    }
  });

  //call this when this view is loaded because this view is cached
  $scope.$on('$ionicView.enter', function () {
    if (!profile.get()) {
      profile.load();
    }

    if (!homeCtrlParams.loaded) {
      if (activityCollection.size() === 0) {
        $scope.isLoadMore = true;
      } else {
        loadActivities('refresh');
      }
    } else {
      prepare();
    }

  });
  //call this because cache may be loaded
  setFiltersData();
  refreshListOfActivities();
});

angular.module('app.controllers').run(function (homeCtrlParams, $document, $rootScope) {
  $document.bind('resume', function () {
    homeCtrlParams.loaded = false;
    $rootScope.$broadcast('activity.reload');
    $rootScope.execApply();
  });
});

angular.module('app.controllers').controller('preload', function (topBar, session, profile, $location) {
  if (session.token) {
    if (session.is_registration_complete) {
      profile.load().then(function () {
        profile.checkRemind();
      });
      if (!$location.path() || '/' === $location.path() || '/preload' === $location.path()) {
        $location.path('/main');
      }
    } else {
      $location.path('/profile');
    }
  } else {
    $location.path('/login');
  }
});

angular.module('app.controllers').directive('iActivity', function ($rootScope, questions, petitions, discussion, elapsedFilter, follows, session, iParse, $sce, favorite, microPetitions, posts, userPetitions) {

  function eventCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/event.html';
  }

  function newsCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/news.html';
    $scope.entity = $scope.activity.get('entity');
    $scope.rate = function (action) {
      $scope.sending = true;
      discussion.loadRoot('poll', $scope.entity.id).then(function (comment) {
        discussion.rate(comment, action).then(function (comment) {
          $scope.activity.set('rate_up', comment.rate_up).set('rate_down', comment.rate_down);
          $scope.sending = false;
        }, function () {
          $scope.sending = false;
        });
      });
    };
  }

  function paymentCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/payment.html';
  }

  function postCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/post.html';
    $scope.currentUserIsActivityOwner = $scope.activity.get('owner').id == session.user_id
    $scope.booster = $scope.activity.get('owner').type === 'group' ? 100 : $scope.activity.getQuorumCompletedPercent();

    $scope.upvote = function(){
      var postID = $scope.activity.get('entity').id
      $scope.sending = true;
      posts.upvote(postID).then(function(answer){
        $scope.activity.setAnswer(answer);
        $scope.sending = false;
        $scope.showToast('Post upvoted!');
      })
    }

    $scope.downvote = function(){
      var postID = $scope.activity.get('entity').id
      $scope.sending = true;
      posts.downvote(postID).then(function(answer){
        $scope.activity.setAnswer(answer);
        $scope.sending = false;
        $scope.showToast('Post downvoted!');
      })
    }

    $scope.unvote = function(){
      var postID = $scope.activity.get('entity').id
      $scope.sending = true;
      posts.unvote(postID).then(function(answer){
        $scope.activity.unAnswer();
        $scope.sending = false;
        $scope.showToast('Post vote undo successful!');
      })
    }

  }

  function userPetitionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/user-petition.html';

    $scope.sign = function () {
      var userPetitionID = $scope.activity.get('entity').id
      $scope.sending = true
      userPetitions.sign(userPetitionID).then(function(){
          $scope.activity.markAsSigned()
          $scope.sending = false;
          $scope.showToast('User petition signed.');
      })
    };

    $scope.unsign = function () {
      $scope.sending = true;
      var userPetitionID = $scope.activity.get('entity').id;
      userPetitions.unsign(userPetitionID).then(function (response) {
        $scope.activity.markAsUnsigned()
        $scope.sending = false;
        $scope.showToast('User petition unsigned.');
      });
    };

    $scope.subscribeToNotifications = function(){
      var userPetitionID = $scope.activity.get('entity').id;
      userPetitions.subscribeToNotifications(userPetitionID).then(function (response) {
        $scope.activity.markAsSubscribed()
        $scope.showToast('Subscribed to petition notifications.');
      })
    }

    $scope.unsubscribeFromNotifications = function(){
      var userPetitionID = $scope.activity.get('entity').id;
      userPetitions.unsubscribeFromNotifications(userPetitionID).then(function (response) {
        $scope.activity.markAsUnsubscribed()
        $scope.showToast('Unsubscribed from petition notifications.');
      })
    }
  }

  function petitionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/user-petition.html';

    $scope.sign = function () {
      var microPetitionID = $scope.activity.get('entity').id
      $scope.sending = true
      microPetitions.signLongPetition(microPetitionID).then(function(){
          $scope.activity.markAsSigned()
          $scope.sending = false;
          $scope.showToast('Petition signed.');
      })
    };

    $scope.unsign = function () {
      $scope.sending = true;
      var microPetitionID = $scope.activity.get('entity').id;
      microPetitions.unsignLongPetition(microPetitionID).then(function (response) {
        $scope.activity.markAsSigned()
        $scope.sending = false;
        $scope.showToast('Petition unsigned.');
      });
    };
  }

  function questionCtrl($scope) {
    $scope.templateSrc = 'templates/home/activities/question.html';
  }

  var ctrlByType = {
    'leader-event': eventCtrl,
    'leader-news': newsCtrl,
    'crowdfunding-payment-request': paymentCtrl,
    'payment-request': paymentCtrl,
    'post': postCtrl,
    'user-petition': userPetitionCtrl,
    'petition': petitionCtrl,
    'question': questionCtrl
  };

  return {
    scope: {
      activity: '='
    },
    restrict: 'E',
    template: '<ng-include src="templateSrc"></ng-include>',
    controller: function ($scope) {
      $scope.showToast = $rootScope.showToast;
      $scope.navigateTo = $rootScope.navigateTo;
      $scope.isBookmarked = function(){
        return favorite.isBookmarked($scope.activity)
      }
      $scope.bookmarkButtonClicked = function(){
        if($scope.isBookmarked()){
          favorite.removeBookmark($scope.activity)
          $scope.showToast('Item was removed from Favorites');
        } else {
         favorite.addBookmark($scope.activity)
         $scope.showToast('Item was added to Favorites');
        }
      }
      $scope.navigateToActivity = function (activity, focus, e) {

        activity.setRead();
        if (e && e.target.tagName.toLowerCase() === 'hash-tag') {
          $rootScope.openTag(angular.element(e.target).text());
        } else {
          $rootScope.navigateTo('activity', activity, focus);
        }
      };

      // if($scope.activity.get('entity').id == 242)
      //     console.log(JSON.stringify($scope.activity))

      $scope.title = $scope.activity.get('title');
      var description_raw = $scope.activity.get('description_html')
      if(description_raw == null || description_raw.length == 0)
        description_raw = $scope.activity.get('description')
      $scope.description = iParse.wrapHashTags(description_raw)
      $scope.avatar_file_path = $scope.activity.get('owner').avatar_file_path;
      $scope.iconClass = $scope.activity.getIcon();
      $scope.official_title = $scope.activity.get('owner').official_title;
      $scope.full_name = [$scope.activity.get('owner').first_name, $scope.activity.get('owner').last_name].join(' ');
      $scope.owner_info_1 = $scope.activity.get('owner_info_1');
      $scope.sent_at_elapsed = elapsedFilter($scope.activity.get('sent_at'));
      $scope.responses_count = $scope.activity.get('responses_count');
      $scope.comments_count = $scope.activity.get('comments_count') - 1;
      $scope.isDefaultAvatar = $rootScope.isDefaultAvatar($scope.avatar_file_path);

      var activityOwnerID = $scope.activity.get('owner').id
      var activityOwnerFollow = follows.getOrCreateUser(activityOwnerID);

      $scope.showFollow = follows.loaded && activityOwnerID != session.user_id
      $scope.followClicked = function(){
        if(activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && activityOwnerFollow.isApproved())
          $scope.showToast('You are following this user');
        else if (activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && !activityOwnerFollow.isApproved())
          $scope.showToast('Waiting for user to approve...');
        else {
          $scope.sending = true;
          activityOwnerFollow.followByCurrentUser().then(function () {
            $scope.activity.followable = false;
            $scope.sending = false;
            $scope.showToast('Follow request sent!');
            follows.load().then(function(){
              activityOwnerFollow = follows.getOrCreateUser(activityOwnerID);
            })
          });
          msg = 'Follow request sent!'
        }
      }

      $scope.openUrlInExternalBrowser = function(url){
        if(url.substring(0,4) != 'http')
          url = 'http://'+url
        $rootScope.openSystem(url)
      }

      $scope.followIcons = function(){
        if(activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && activityOwnerFollow.isApproved())
          return ['icon ion-person calm', 'icon ion-minus-circled']
        else if(activityOwnerFollow && activityOwnerFollow.isFollowedByCurrentUser() && !activityOwnerFollow.isApproved())
          return ['icon ion-person', 'icon ion-android-time calm']
        else
          return ['icon ion-person', 'icon ion-plus-circled calm']
      }

      var activityType = $scope.activity.dataType()
      if (ctrlByType[activityType]) {
        ctrlByType[activityType]($scope);
      } else {
        $scope.templateSrc = 'templates/home/activities/default.html';
      }
    }
  };
});<|endoftext|>