<commit_before>// TODO: Refactor... switch from jQuery to Vue.js
// public methods
function resetTabsSlider() {
    // Scan/Copy Transitions
    // Scan Tab
    $("#scan-tab").off().on("click", function () {
        resetTabsSlider();
        activateTab("#scan");
    });

    // Copy tab
    $("#copy-tab").off().on("click", function () {
        resetTabsSlider();
        activateTab("#copy");

        $("#tabsSlider").addClass("slide-copy");
    });

    // Altcoins tab
    $("#altcoins-tab").off().on("click", function () {
        resetTabsSlider();
        activateTab("#altcoins");

        $("#tabsSlider").addClass("slide-altcoins");
    });

    $("#tabsSlider").removeClass("slide-copy");
    $("#tabsSlider").removeClass("slide-altcoins");

    $("#scan-tab").removeClass("active");
    $("#copy-tab").removeClass("active");
    $("#altcoins-tab").removeClass("active");

    $("#copy").hide();
    $("#copy").removeClass("active");

    $("#scan").hide();
    $("#scan").removeClass("active");

    $("#altcoins").hide();
    $("#altcoins").removeClass("active");

    closePaymentMethodDialog(null);
}

function changeCurrency(currency) {
    if (currency !== null && srvModel.paymentMethodId !== currency) {
        $(".payment__currencies").hide();
        $(".payment__spinner").show();
        checkoutCtrl.scanDisplayQr = "";
        srvModel.paymentMethodId = currency;
        fetchStatus();
        setTimeout(function(){
            resetTabsSlider();
            if ($("#tabsSlider").hasClass("slide-copy")) {
                activateTab("#copy");
            } else if ($("#tabsSlider").hasClass("slide-altcoins")) {
                activateTab("#altcoins");
            } else {
                activateTab("#scan");
            }
        },50);

    }
    return false;
}

function onDataCallback(jsonData) {
    var newStatus = jsonData.status;

    if (newStatus === "complete" ||
        newStatus === "confirmed" ||
        newStatus === "paid") {
        if ($(".modal-dialog").hasClass("expired")) {
            $(".modal-dialog").removeClass("expired");
            $("#expired").removeClass("active");
        }

        $(".modal-dialog").addClass("paid");

        resetTabsSlider();
        $("#paid").addClass("active");
        if (!jsonData.isModal && jsonData.redirectAutomatically && jsonData.merchantRefLink) {
            $(".payment__spinner").show();
            setTimeout(function () {
                window.location = jsonData.merchantRefLink;
            }, 2000);
        }
    }

    if (newStatus === "expired" || newStatus === "invalid") { //TODO: different state if the invoice is invalid (failed to confirm after timeout)
        if ($(".modal-dialog").hasClass("paid")) {
            $(".modal-dialog").removeClass("paid");
            $("#paid").removeClass("active");
        }

        $(".timer-row").removeClass("expiring-soon");
        $(".timer-row__spinner").html("");
        $("#emailAddressView").removeClass("active");
        $(".modal-dialog").addClass("expired");
        $("#expired").addClass("active");

        resetTabsSlider();
    }

    if (checkoutCtrl.srvModel.status !== newStatus) {
        window.parent.postMessage({ "invoiceId": srvModel.invoiceId, "status": newStatus }, "*");
    }

    // restoring qr code view only when currency is switched
    if (jsonData.paymentMethodId === srvModel.paymentMethodId &&
        checkoutCtrl.scanDisplayQr === "") {
        checkoutCtrl.scanDisplayQr = jsonData.invoiceBitcoinUrlQR;
    }

    if (jsonData.paymentMethodId === srvModel.paymentMethodId) {
        $(".payment__currencies").show();
        $(".payment__spinner").hide();
    }

    if (jsonData.isLightning && checkoutCtrl.lndModel === null) {
        var lndModel = {
            toggle: 0
        };

        checkoutCtrl.lndModel = lndModel;
    }

    if (!jsonData.isLightning) {
        checkoutCtrl.lndModel = null;
    }

    // displaying satoshis for lightning payments
    jsonData.cryptoCodeSrv = jsonData.cryptoCode;
    if (jsonData.isLightning && checkoutCtrl.lightningAmountInSatoshi && jsonData.cryptoCode === "BTC") {
        var SATOSHIME = 100000000;
        jsonData.cryptoCode = "Sats";
        jsonData.btcDue = numberFormatted(jsonData.btcDue * SATOSHIME);
        jsonData.btcPaid = numberFormatted(jsonData.btcPaid * SATOSHIME);
        jsonData.networkFee = numberFormatted(jsonData.networkFee * SATOSHIME);
        jsonData.orderAmount = numberFormatted(jsonData.orderAmount * SATOSHIME);
    }

    // expand line items to show details on amount due for multi-transaction payment
    if (checkoutCtrl.srvModel.txCount === 1 && jsonData.txCount > 1) {
        onlyExpandLineItems();
    }

    // updating ui
    checkoutCtrl.srvModel = jsonData;
}

function numberFormatted(x) {
    var rounded = Math.round(x);
    var parts = rounded.toString().split(".");
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, " ");
    return parts.join(".");
}

function fetchStatus() {
    $.ajax({
        url: window.location.pathname + "/status?invoiceId=" + srvModel.invoiceId + "&paymentMethodId=" + srvModel.paymentMethodId,
        type: "GET",
        cache: false
    }).done(function (data) {
        onDataCallback(data);
    }).fail(function (jqXHR, textStatus, errorThrown) {

    });
}

function lndToggleBolt11() {
    checkoutCtrl.lndModel.toggle = 0;
    checkoutCtrl.scanDisplayQr = checkoutCtrl.srvModel.invoiceBitcoinUrlQR;
}

function lndToggleNode() {
    checkoutCtrl.lndModel.toggle = 1;
    checkoutCtrl.scanDisplayQr = checkoutCtrl.srvModel.peerInfo;
}

var lineItemsExpanded = false;
function toggleLineItems() {
    $("line-items").toggleClass("expanded");
    lineItemsExpanded ? $("line-items").slideUp() : $("line-items").slideDown();
    lineItemsExpanded = !lineItemsExpanded;

    $(".buyerTotalLine").toggleClass("expanded");
    $(".single-item-order__right__btc-price__chevron").toggleClass("expanded");
}

function onlyExpandLineItems() {
    if (!lineItemsExpanded) {
        toggleLineItems();
    }
}

function activateTab(senderName) {
    $(senderName + "-tab").addClass("active");

    $(senderName).show();
    $(senderName).addClass("active");
}

// private methods
$(document).ready(function () {
    // initialize
    onDataCallback(srvModel);
    // initial expand of line items to show error message if multiple payments needed
    if (srvModel.status === "new" && srvModel.txCount > 1) {
        onlyExpandLineItems();
    }

    // check if the Document expired
    if (srvModel.expirationSeconds > 0) {
        progressStart(srvModel.maxTimeSeconds); // Progress bar

        if (srvModel.requiresRefundEmail && !validateEmail(srvModel.customerEmail))
            showEmailForm();
        else
            hideEmailForm();
    }

    $(".close-action").on("click", function () {
        $("invoice").fadeOut(300, function () {
            window.parent.postMessage("close", "*");
        });
    });

    window.parent.postMessage("loaded", "*");
    jQuery("invoice").fadeOut(0);
    jQuery("invoice").fadeIn(300);

    // eof initialize

    // Expand Line-Items
    $(".buyerTotalLine").click(function () {
        toggleLineItems();
    });

    // FUNCTIONS
    function hideEmailForm() {
        $("#emailAddressView").removeClass("active");
        $("placeholder-refundEmail").html(srvModel.customerEmail);

        // Remove Email mode
        $(".modal-dialog").removeClass("enter-purchaser-email");
        $("#scan").addClass("active");
    }
    // Email Form
    // Setup Email mode
    function showEmailForm() {
        $(".modal-dialog").addClass("enter-purchaser-email");

        $("#emailAddressForm .action-button").click(function () {
            var emailAddress = $("#emailAddressFormInput").val();
            if (validateEmail(emailAddress)) {
                $("#emailAddressForm .input-wrapper bp-loading-button .action-button").addClass("loading");
                // Push the email to a server, once the reception is confirmed move on
                srvModel.customerEmail = emailAddress;
                $.ajax({
                    url: window.location.pathname + "/UpdateCustomer?invoiceId=" + srvModel.invoiceId,
                    type: "POST",
                    data: JSON.stringify({ Email: srvModel.customerEmail }),
                    contentType: "application/json; charset=utf-8"
                }).done(function () {
                    hideEmailForm();
                }).fail(function (jqXHR, textStatus, errorThrown) {

                })
                    .always(function () {
                        $("#emailAddressForm .input-wrapper bp-loading-button .action-button").removeClass("loading");
                    });
            } else {
                $("#emailAddressForm").addClass("ng-touched ng-dirty ng-submitted ng-invalid");
            }

            return false;
        });
    }

    /* =============== Even listeners =============== */

    // Email
    $("#emailAddressFormInput").change(function () {
        if ($("#emailAddressForm").hasClass("ng-submitted")) {
            $("#emailAddressForm").removeClass("ng-submitted");
        }
    });

    // Payment received
    // Should connect using webhook ?
    // If notification received

    var socket = null;
    var supportsWebSockets = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
    if (supportsWebSockets) {
        var loc = window.location, ws_uri;
        if (loc.protocol === "https:") {
            ws_uri = "wss:";
        } else {
            ws_uri = "ws:";
        }
        ws_uri += "//" + loc.host;
        ws_uri += loc.pathname + "/status/ws?invoiceId=" + srvModel.invoiceId;
        try {
            socket = new WebSocket(ws_uri);
            socket.onmessage = function (e) {
                fetchStatus();
            };
            socket.onerror = function (e) {
                console.error("Error while connecting to websocket for invoice notifications (callback)");
            };
        }
        catch (e) {
            console.error("Error while connecting to websocket for invoice notifications");
        }
    }

    var watcher = setInterval(function () {
        if (socket === null || socket.readyState !== 1) {
            fetchStatus();
        }
    }, 2000);

    $(".menu__item").click(function () {
        $(".menu__scroll .menu__item").removeClass("selected");
        $(this).addClass("selected");
        language();
        $(".selector span").text($(".selected").text());
        // function to load contents in different language should go there
    });

    // Timer Countdown && Progress bar
    function progressStart(timerMax) {
        var end = new Date(); // Setup Time Variable, should come from server
        end.setSeconds(end.getSeconds() + srvModel.expirationSeconds);
        timerMax *= 1000; // Usually 15 minutes = 9000 second= 900000 ms
        animateUpdate(); //Launch it

        function animateUpdate() {
            var now = new Date();
            var timeDiff = end.getTime() - now.getTime();
            var perc = 100 - Math.round(timeDiff / timerMax * 100);
            var status = checkoutCtrl.srvModel.status;

            updateTimer(timeDiff / 1000);

            if (perc === 75 && (status === "paidPartial" || status === "new")) {
                $(".timer-row").addClass("expiring-soon");
                checkoutCtrl.expiringSoon = true;
                updateProgress(perc);
            }
            if (perc <= 100) {
                updateProgress(perc);

                var timeoutVal = 300; // Timeout calc
                setTimeout(animateUpdate, timeoutVal);
            }

            //if (perc >= 100 && status === "expired") {
            //    onDataCallback(status);
            //}
        }

        function updateProgress(percentage) {
            $('.timer-row__progress-bar').css("width", percentage + "%");
        }

        function updateTimer(timer) {
            var display = $(".timer-row__time-left");
            if (timer >= 0) {
                var minutes = parseInt(timer / 60, 10);
                minutes = minutes < 10 ? "0" + minutes : minutes;

                var seconds = parseInt(timer % 60, 10);
                seconds = seconds < 10 ? "0" + seconds : seconds;

                display.text(minutes + ":" + seconds);
            } else {
                display.text("00:00");
            }
        }
    }

    // Clipboard Copy
    var copySpan = new Clipboard('._copySpan', {
        target: function (trigger) {
            return copyElement(trigger, 0, 65).firstChild;
        }
    });
    var copyInput = new Clipboard('._copyInput', {
        target: function (trigger) {
            return copyElement(trigger, 4, 65).firstChild;
        }
    });

    function copyElement(trigger, popupLeftModifier, popupTopModifier) {
        var elm = $(trigger);
        var position = elm.offset();
        position.top -= popupLeftModifier;
        position.left += (elm.width() / 2) - popupTopModifier;
        $(".copyLabelPopup").css(position).addClass("copied");

        elm.removeClass("copy-cursor").addClass("clipboardCopied");
        setTimeout(clearSelection, 100);
        setTimeout(function () {
            elm.removeClass("clipboardCopied").addClass("copy-cursor");
            $(".copyLabelPopup").removeClass("copied");
        }, 1000);
        return trigger;
    }
    function clearSelection() {
        if (window.getSelection) { window.getSelection().removeAllRanges(); }
        else if (document.selection) { document.selection.empty(); }
    }
    // EOF Copy

    // Disable enter key
    $(document).keypress(
        function (event) {
            if (event.which === '13') {
                event.preventDefault();
            }
        }
    );

});

// Validate Email address
function validateEmail(email) {
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
}
<commit_msg>fix #994 tab slider not always working on checkout (#995)<commit_after>// TODO: Refactor... switch from jQuery to Vue.js
// public methods
function resetTabsSlider() {
    // Scan/Copy Transitions
    // Scan Tab
    $("#scan-tab").off().on("click", function () {
        resetTabsSlider();
        activateTab("#scan");
    });

    // Copy tab
    $("#copy-tab").off().on("click", function () {
        resetTabsSlider();
        activateTab("#copy");

        $("#tabsSlider").addClass("slide-copy");
    });

    // Altcoins tab
    $("#altcoins-tab").off().on("click", function () {
        resetTabsSlider();
        activateTab("#altcoins");

        $("#tabsSlider").addClass("slide-altcoins");
    });

    $("#tabsSlider").removeClass("slide-copy");
    $("#tabsSlider").removeClass("slide-altcoins");

    $("#scan-tab").removeClass("active");
    $("#copy-tab").removeClass("active");
    $("#altcoins-tab").removeClass("active");

    $("#copy").hide();
    $("#copy").removeClass("active");

    $("#scan").hide();
    $("#scan").removeClass("active");

    $("#altcoins").hide();
    $("#altcoins").removeClass("active");

    closePaymentMethodDialog(null);
}

function changeCurrency(currency) {
    if (currency !== null && srvModel.paymentMethodId !== currency) {
        $(".payment__currencies").hide();
        $(".payment__spinner").show();
        checkoutCtrl.scanDisplayQr = "";
        srvModel.paymentMethodId = currency;
        fetchStatus();
        setTimeout(function(){
            resetTabsSlider();
            if ($("#tabsSlider").hasClass("slide-copy")) {
                activateTab("#copy");
            } else if ($("#tabsSlider").hasClass("slide-altcoins")) {
                activateTab("#altcoins");
            } else {
                activateTab("#scan");
            }
        },50);

    }
    return false;
}

function onDataCallback(jsonData) {
    var newStatus = jsonData.status;

    if (newStatus === "complete" ||
        newStatus === "confirmed" ||
        newStatus === "paid") {
        if ($(".modal-dialog").hasClass("expired")) {
            $(".modal-dialog").removeClass("expired");
            $("#expired").removeClass("active");
        }

        $(".modal-dialog").addClass("paid");

        resetTabsSlider();
        $("#paid").addClass("active");
        if (!jsonData.isModal && jsonData.redirectAutomatically && jsonData.merchantRefLink) {
            $(".payment__spinner").show();
            setTimeout(function () {
                window.location = jsonData.merchantRefLink;
            }, 2000);
        }
    }

    if (newStatus === "expired" || newStatus === "invalid") { //TODO: different state if the invoice is invalid (failed to confirm after timeout)
        if ($(".modal-dialog").hasClass("paid")) {
            $(".modal-dialog").removeClass("paid");
            $("#paid").removeClass("active");
        }

        $(".timer-row").removeClass("expiring-soon");
        $(".timer-row__spinner").html("");
        $("#emailAddressView").removeClass("active");
        $(".modal-dialog").addClass("expired");
        $("#expired").addClass("active");

        resetTabsSlider();
    }

    if (checkoutCtrl.srvModel.status !== newStatus) {
        window.parent.postMessage({ "invoiceId": srvModel.invoiceId, "status": newStatus }, "*");
    }

    // restoring qr code view only when currency is switched
    if (jsonData.paymentMethodId === srvModel.paymentMethodId &&
        checkoutCtrl.scanDisplayQr === "") {
        checkoutCtrl.scanDisplayQr = jsonData.invoiceBitcoinUrlQR;
    }

    if (jsonData.paymentMethodId === srvModel.paymentMethodId) {
        $(".payment__currencies").show();
        $(".payment__spinner").hide();
    }

    if (jsonData.isLightning && checkoutCtrl.lndModel === null) {
        var lndModel = {
            toggle: 0
        };

        checkoutCtrl.lndModel = lndModel;
    }

    if (!jsonData.isLightning) {
        checkoutCtrl.lndModel = null;
    }

    // displaying satoshis for lightning payments
    jsonData.cryptoCodeSrv = jsonData.cryptoCode;
    if (jsonData.isLightning && checkoutCtrl.lightningAmountInSatoshi && jsonData.cryptoCode === "BTC") {
        var SATOSHIME = 100000000;
        jsonData.cryptoCode = "Sats";
        jsonData.btcDue = numberFormatted(jsonData.btcDue * SATOSHIME);
        jsonData.btcPaid = numberFormatted(jsonData.btcPaid * SATOSHIME);
        jsonData.networkFee = numberFormatted(jsonData.networkFee * SATOSHIME);
        jsonData.orderAmount = numberFormatted(jsonData.orderAmount * SATOSHIME);
    }

    // expand line items to show details on amount due for multi-transaction payment
    if (checkoutCtrl.srvModel.txCount === 1 && jsonData.txCount > 1) {
        onlyExpandLineItems();
    }

    // updating ui
    checkoutCtrl.srvModel = jsonData;
}

function numberFormatted(x) {
    var rounded = Math.round(x);
    var parts = rounded.toString().split(".");
    parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, " ");
    return parts.join(".");
}

function fetchStatus() {
    $.ajax({
        url: window.location.pathname + "/status?invoiceId=" + srvModel.invoiceId + "&paymentMethodId=" + srvModel.paymentMethodId,
        type: "GET",
        cache: false
    }).done(function (data) {
        onDataCallback(data);
    }).fail(function (jqXHR, textStatus, errorThrown) {

    });
}

function lndToggleBolt11() {
    checkoutCtrl.lndModel.toggle = 0;
    checkoutCtrl.scanDisplayQr = checkoutCtrl.srvModel.invoiceBitcoinUrlQR;
}

function lndToggleNode() {
    checkoutCtrl.lndModel.toggle = 1;
    checkoutCtrl.scanDisplayQr = checkoutCtrl.srvModel.peerInfo;
}

var lineItemsExpanded = false;
function toggleLineItems() {
    $("line-items").toggleClass("expanded");
    lineItemsExpanded ? $("line-items").slideUp() : $("line-items").slideDown();
    lineItemsExpanded = !lineItemsExpanded;

    $(".buyerTotalLine").toggleClass("expanded");
    $(".single-item-order__right__btc-price__chevron").toggleClass("expanded");
}

function onlyExpandLineItems() {
    if (!lineItemsExpanded) {
        toggleLineItems();
    }
}

function activateTab(senderName) {
    $(senderName + "-tab").addClass("active");

    $(senderName).show();
    $(senderName).addClass("active");
}

// private methods
$(document).ready(function () {
    // initialize
    onDataCallback(srvModel);
    // initial expand of line items to show error message if multiple payments needed
    if (srvModel.status === "new" && srvModel.txCount > 1) {
        onlyExpandLineItems();
    }

    // check if the Document expired
    if (srvModel.expirationSeconds > 0) {
        progressStart(srvModel.maxTimeSeconds); // Progress bar

        if (srvModel.requiresRefundEmail && !validateEmail(srvModel.customerEmail))
            showEmailForm();
        else
            hideEmailForm();
    }

    $(".close-action").on("click", function () {
        $("invoice").fadeOut(300, function () {
            window.parent.postMessage("close", "*");
        });
    });

    window.parent.postMessage("loaded", "*");
    jQuery("invoice").fadeOut(0);
    jQuery("invoice").fadeIn(300);

    // eof initialize

    // Expand Line-Items
    $(".buyerTotalLine").click(function () {
        toggleLineItems();
    });

    // FUNCTIONS
    function hideEmailForm() {
        $("#emailAddressView").removeClass("active");
        $("placeholder-refundEmail").html(srvModel.customerEmail);

        // Remove Email mode
        $(".modal-dialog").removeClass("enter-purchaser-email");
        $("#scan").addClass("active");
    }
    // Email Form
    // Setup Email mode
    function showEmailForm() {
        $(".modal-dialog").addClass("enter-purchaser-email");

        $("#emailAddressForm .action-button").click(function () {
            var emailAddress = $("#emailAddressFormInput").val();
            if (validateEmail(emailAddress)) {
                $("#emailAddressForm .input-wrapper bp-loading-button .action-button").addClass("loading");
                // Push the email to a server, once the reception is confirmed move on
                srvModel.customerEmail = emailAddress;
                $.ajax({
                    url: window.location.pathname + "/UpdateCustomer?invoiceId=" + srvModel.invoiceId,
                    type: "POST",
                    data: JSON.stringify({ Email: srvModel.customerEmail }),
                    contentType: "application/json; charset=utf-8"
                }).done(function () {
                    hideEmailForm();
                }).fail(function (jqXHR, textStatus, errorThrown) {

                })
                    .always(function () {
                        $("#emailAddressForm .input-wrapper bp-loading-button .action-button").removeClass("loading");
                    });
            } else {
                $("#emailAddressForm").addClass("ng-touched ng-dirty ng-submitted ng-invalid");
            }

            return false;
        });
    }

    /* =============== Even listeners =============== */

    // Email
    $("#emailAddressFormInput").change(function () {
        if ($("#emailAddressForm").hasClass("ng-submitted")) {
            $("#emailAddressForm").removeClass("ng-submitted");
        }
    });

    // Payment received
    // Should connect using webhook ?
    // If notification received

    var socket = null;
    var supportsWebSockets = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
    if (supportsWebSockets) {
        var loc = window.location, ws_uri;
        if (loc.protocol === "https:") {
            ws_uri = "wss:";
        } else {
            ws_uri = "ws:";
        }
        ws_uri += "//" + loc.host;
        ws_uri += loc.pathname + "/status/ws?invoiceId=" + srvModel.invoiceId;
        try {
            socket = new WebSocket(ws_uri);
            socket.onmessage = function (e) {
                fetchStatus();
            };
            socket.onerror = function (e) {
                console.error("Error while connecting to websocket for invoice notifications (callback)");
            };
        }
        catch (e) {
            console.error("Error while connecting to websocket for invoice notifications");
        }
    }

    var watcher = setInterval(function () {
        if (socket === null || socket.readyState !== 1) {
            fetchStatus();
        }
    }, 2000);

    $(".menu__item").click(function () {
        $(".menu__scroll .menu__item").removeClass("selected");
        $(this).addClass("selected");
        language();
        $(".selector span").text($(".selected").text());
        // function to load contents in different language should go there
    });

    // Timer Countdown && Progress bar
    function progressStart(timerMax) {
        var end = new Date(); // Setup Time Variable, should come from server
        end.setSeconds(end.getSeconds() + srvModel.expirationSeconds);
        timerMax *= 1000; // Usually 15 minutes = 9000 second= 900000 ms
        animateUpdate(); //Launch it

        function animateUpdate() {
            var now = new Date();
            var timeDiff = end.getTime() - now.getTime();
            var perc = 100 - Math.round(timeDiff / timerMax * 100);
            var status = checkoutCtrl.srvModel.status;

            updateTimer(timeDiff / 1000);

            if (perc === 75 && (status === "paidPartial" || status === "new")) {
                $(".timer-row").addClass("expiring-soon");
                checkoutCtrl.expiringSoon = true;
                updateProgress(perc);
            }
            if (perc <= 100) {
                updateProgress(perc);

                var timeoutVal = 300; // Timeout calc
                setTimeout(animateUpdate, timeoutVal);
            }

            //if (perc >= 100 && status === "expired") {
            //    onDataCallback(status);
            //}
        }

        function updateProgress(percentage) {
            $('.timer-row__progress-bar').css("width", percentage + "%");
        }

        function updateTimer(timer) {
            var display = $(".timer-row__time-left");
            if (timer >= 0) {
                var minutes = parseInt(timer / 60, 10);
                minutes = minutes < 10 ? "0" + minutes : minutes;

                var seconds = parseInt(timer % 60, 10);
                seconds = seconds < 10 ? "0" + seconds : seconds;

                display.text(minutes + ":" + seconds);
            } else {
                display.text("00:00");
            }
        }
    }

    // Clipboard Copy
    var copySpan = new Clipboard('._copySpan', {
        target: function (trigger) {
            return copyElement(trigger, 0, 65).firstChild;
        }
    });
    var copyInput = new Clipboard('._copyInput', {
        target: function (trigger) {
            return copyElement(trigger, 4, 65).firstChild;
        }
    });

    function copyElement(trigger, popupLeftModifier, popupTopModifier) {
        var elm = $(trigger);
        var position = elm.offset();
        position.top -= popupLeftModifier;
        position.left += (elm.width() / 2) - popupTopModifier;
        $(".copyLabelPopup").css(position).addClass("copied");

        elm.removeClass("copy-cursor").addClass("clipboardCopied");
        setTimeout(clearSelection, 100);
        setTimeout(function () {
            elm.removeClass("clipboardCopied").addClass("copy-cursor");
            $(".copyLabelPopup").removeClass("copied");
        }, 1000);
        return trigger;
    }
    function clearSelection() {
        if (window.getSelection) { window.getSelection().removeAllRanges(); }
        else if (document.selection) { document.selection.empty(); }
    }
    // EOF Copy

    // Disable enter key
    $(document).keypress(
        function (event) {
            if (event.which === '13') {
                event.preventDefault();
            }
        }
    );
    resetTabsSlider();

});

// Validate Email address
function validateEmail(email) {
    var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
    return re.test(email);
}
<|endoftext|>


<commit_before>/*Copyright 2018 Bang & Olufsen A/S
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/

// SETUP MANAGER FOR BEOCREATE ELEMENTS

	var extensions = beo.extensions;
	var selectedExtension = beo.selectedExtension;
	var debug = beo.debug;

	var defaultSettings = {
			"firstTimeSetup": true
		};
	var settings = JSON.parse(JSON.stringify(defaultSettings));

	var version = require("./package.json").version;

	beo.bus.on('general', function(event) {

		if (event.header == "activatedExtension") {
			selectedExtension = event.content.extension;
			if (setupFlow.length > 2) {
				checkIfMoreSteps(true);
				beo.bus.emit("ui", {target: "setup", header: "extensionChanged", content: {selectedExtension: selectedExtension}});
			}
		}

	});

	var restartAfter = false;
	var setupFlow = [{extension: "setup", shown: false, allowAdvancing: true}, {extension: "setup-finish", shown: false, allowAdvancing: true}];
	// Setup is in the flow by default and two times, because it shows both introduction and finish screens.

	beo.bus.on('setup', function(event) {

		if (event.header == "settings") {
			if (event.content.settings) {
				settings = Object.assign(settings, event.content.settings);
			}
		}

		if (event.header == "getSetupStatus") {
			// The client always asks for this when it connects.
			if (setupFlow.length == 2) {
				// No (actual) extensions in the setup flow.
				beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: [], setup: beo.setup, selectedExtension: selectedExtension}});
			} else {
				if (!beo.setup) {
					// Setup will start with the first extension when the client connects for the first time.
					beo.setup = true;
					//setupFlow.unshift({extension: "setup", shown: false, allowAdvancing: true}); // Add the "welcome" screen to the beginning of the flow.
					selectedExtension = setupFlow[0].extension;
					beo.bus.emit("setup", {header: "startingSetup", content: {withExtension: setupFlow[0].extension}});
					beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: setupFlow, setup: beo.setup, selectedExtension: selectedExtension, reset: true, restartAfter: restartAfter, firstTime: settings.firstTimeSetup}});
				} else {
					// If setup is already underway, just send the current status. The UI should pick up.
					beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: setupFlow, setup: beo.setup, selectedExtension: selectedExtension, restartAfter: restartAfter, firstTime: settings.firstTimeSetup}});
				}
			}

		}

		if (event.header == "nextStep") {
			for (var i = 0; i < setupFlow.length; i++) {
				if (setupFlow[i].extension == selectedExtension) {
					if (setupFlow[i+1]) {
						// Command the next step.
						beo.bus.emit("setup", {header: "advancing", content: {fromExtension: selectedExtension}});
						beo.bus.emit("ui", {target: "setup", header: "showExtension", content: {extension: setupFlow[i+1].extension}});
					} else {
						// No more extensions, finish.
						setupFlow = [{extension: "setup", shown: false, allowAdvancing: true}, {extension: "setup-finish", shown: false, allowAdvancing: true}];
						beo.setup = false;
						beo.bus.emit("setup", {header: "finishingSetup"});
						beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: [], setup: "finished", selectedExtension: selectedExtension}});
						settings.firstTimeSetup = false;
						beo.bus.emit("settings", {header: "saveSettings", content: {extension: "setup", settings: settings}});
						if (restartAfter) {
							beo.bus.emit("general", {header: "requestReboot", content: {extension: "setup"}});
						}
					}
					break;
				}
			}
		}

	});

	function joinSetupFlow(extension, options) {
		// An extension can join the setup flow at any time, but it might not get its preferred placement if some extension have already been shown to the user.

		joins = true;
		for (var i = 0; i < setupFlow.length; i++) {
			// Check that this extension doesn't already exist in the flow.
			if (setupFlow[i].extension == extension) {
				joins = false;
				break;
			}
		}
		if (extension == "setup" && extension == "setup-finish") joins = false;
		if (joins) {
			// Create an entry.
			allowAdvancing = (options.allowAdvancing) ? true : false
			newStep = {extension: extension, shown: false, allowAdvancing: allowAdvancing};
			if (options.before) newStep.before = options.before;
			if (options.after) newStep.after = options.after;
			// Next, determine the placement.
			endIndex = setupFlow.length-1; // Default to adding the extension to the end, but before the finishing step.

			// First fill out these indexes, then check if we need to resolve conflicts:
			beforeExisting = null; // If the new extension wants to be before an existing one.
			beforeNew = null; // If an existing extension wants to be before the new one.
			afterExisting = null; // If the new extension wants to be after an existing one.
			afterNew = null; // If an existing extension wants to be after the new one.
			lastShown = 0;

			for (var i = 1; i < setupFlow.length; i++) {
				if (newStep.before) {
					if (newStep.before.indexOf(setupFlow[i].extension) != -1) {
						// Only find the first extension that matches.
						if (beforeExisting == null) beforeExisting = i;
					}
				}
				if (newStep.after) {
					if (newStep.after.indexOf(setupFlow[i].extension) != -1) {
						afterExisting = i+1;
					}
				}
				if (setupFlow[i].before) {
					if (setupFlow[i].before.indexOf(newStep.extension) != -1) {
						beforeNew = i+1;
					}
				}
				if (setupFlow[i].after) {
					if (setupFlow[i].after.indexOf(newStep.extension) != -1) {
						afterNew = i;
					}
				}
				if (setupFlow[i].shown) lastShown = i+1;
			}
			if (beforeExisting == null && afterExisting == null && beforeNew == null && afterNew == null) {
				placedIndex = endIndex;
			} else {
				placedIndex = Math.max(beforeExisting, afterExisting, beforeNew, afterNew, lastShown);
			}

			setupFlow.splice(placedIndex, 0, newStep); // Add to the flow.
			beo.bus.emit("ui", {target: "setup", header: "joinSetupFlow", content: {extension: extension, setupFlow: setupFlow}});
			if (debug) console.log("Extension '"+extension+"' joined setup flow.");
			checkIfMoreSteps();
		}
		return joins;
	}

	function allowAdvancing(extension, allow) {
		// Controls whether or not the "Next Step" button is enabled for a given extension.
		if (extension != "setup" && extension != "setup-finish") {
			for (var i = 0; i < setupFlow.length; i++) {
				if (setupFlow[i].extension == extension) {
					setupFlow[i].allowAdvancing = allow;
					beo.bus.emit("ui", {target: "setup", header: "allowAdvancing", content: {extension: extension, allow: allow}});
					break;
				}
			}
		}
	}

	function restartWhenComplete(extension, restart) {
		// Allows an extension to request system reboot when the setup is complete.
		restartAfterTemp = false;
		for (var i = 0; i < setupFlow.length; i++) {
			if (setupFlow[i].extension == extension) {
				setupFlow[i].restart = restart;
				break;
			}
			if (setupFlow[i].restart) restartAfterTemp = true;
		}
		if (restartAfter != restartAfterTemp) {
			restartAfter = restartAfterTemp;
			beo.bus.emit("ui", {target: "setup", header: "restartAfter", content: {restartAfter: restartAfter}});
		}
	}

	function leaveSetupFlow(extension) {
		// If an extension decides it doesn't need setup anymore, it can leave the setup flow even during setup if it hasn't been shown yet.
		leaves = false;
		if (extension != "setup" && extension != "setup-finish") {
			for (var i = 0; i < setupFlow.length; i++) {
				if (setupFlow[i].extension == extension) {
					if (!setupFlow[i].shown) {
						leaves = true;
					} else {
						// If the extension can't be removed (it has been already shown), allow advancing over it.
						setupFlow[i].allowAdvancing = true;
						beo.bus.emit("ui", {target: "setup", header: "allowAdvancing", content: {extension: extension, allow: true}});
					}
					break;
				}
			}
			if (leaves) {
				setupFlow.splice(i, 1);
				beo.bus.emit("ui", {target: "setup", header: "leaveSetupFlow", content: {extension: extension, setupFlow: setupFlow}});
				checkIfMoreSteps();
			}
		}
		return leaves;
	}

	function checkIfMoreSteps(setShown) {
		for (var i = 0; i < setupFlow.length; i++) {
			// Check and send if more extensions are queued. Controls if the assistant bar shows "Next Step" or "Finish Setup".
			if (setupFlow[i].extension == selectedExtension) {
				if (setShown) setupFlow[i].shown = true;
				if (i < setupFlow.length-1) {
					beo.bus.emit("ui", {target: "setup", header: "assistantButton", content: {lastStep: false}});
				} else {
					beo.bus.emit("ui", {target: "setup", header: "assistantButton", content: {lastStep: true}});
				}
				break;
			}
		}
	}

module.exports = {
	joinSetupFlow: joinSetupFlow,
	leaveSetupFlow: leaveSetupFlow,
	allowAdvancing: allowAdvancing,
	restartWhenComplete: restartWhenComplete,
	setupFlow: setupFlow,
	version: version
};

<commit_msg>Temporarily use fixed sort in setup flow<commit_after>/*Copyright 2018 Bang & Olufsen A/S
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.*/

// SETUP MANAGER FOR BEOCREATE ELEMENTS

	var extensions = beo.extensions;
	var selectedExtension = beo.selectedExtension;
	var debug = beo.debug;

	var defaultSettings = {
			"firstTimeSetup": true
		};
	var settings = JSON.parse(JSON.stringify(defaultSettings));

	var version = require("./package.json").version;

	beo.bus.on('general', function(event) {

		if (event.header == "activatedExtension") {
			selectedExtension = event.content.extension;
			if (setupFlow.length > 2) {
				checkIfMoreSteps(true);
				beo.bus.emit("ui", {target: "setup", header: "extensionChanged", content: {selectedExtension: selectedExtension}});
			}
		}

	});

	var restartAfter = false;
	var setupFlow = [{extension: "setup", shown: false, allowAdvancing: true}, {extension: "setup-finish", shown: false, allowAdvancing: true}];
	// Setup is in the flow by default and two times, because it shows both introduction and finish screens.

	beo.bus.on('setup', function(event) {

		if (event.header == "settings") {
			if (event.content.settings) {
				settings = Object.assign(settings, event.content.settings);
			}
		}

		if (event.header == "getSetupStatus") {
			// The client always asks for this when it connects.
			if (setupFlow.length == 2) {
				// No (actual) extensions in the setup flow.
				beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: [], setup: beo.setup, selectedExtension: selectedExtension}});
			} else {
				if (!beo.setup) {
					// Setup will start with the first extension when the client connects for the first time.
					beo.setup = true;
					//setupFlow.unshift({extension: "setup", shown: false, allowAdvancing: true}); // Add the "welcome" screen to the beginning of the flow.
					selectedExtension = setupFlow[0].extension;
					beo.bus.emit("setup", {header: "startingSetup", content: {withExtension: setupFlow[0].extension}});
					beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: setupFlow, setup: beo.setup, selectedExtension: selectedExtension, reset: true, restartAfter: restartAfter, firstTime: settings.firstTimeSetup}});
				} else {
					// If setup is already underway, just send the current status. The UI should pick up.
					beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: setupFlow, setup: beo.setup, selectedExtension: selectedExtension, restartAfter: restartAfter, firstTime: settings.firstTimeSetup}});
				}
			}

		}

		if (event.header == "nextStep") {
			for (var i = 0; i < setupFlow.length; i++) {
				if (setupFlow[i].extension == selectedExtension) {
					if (setupFlow[i+1]) {
						// Command the next step.
						beo.bus.emit("setup", {header: "advancing", content: {fromExtension: selectedExtension}});
						beo.bus.emit("ui", {target: "setup", header: "showExtension", content: {extension: setupFlow[i+1].extension}});
					} else {
						// No more extensions, finish.
						setupFlow = [{extension: "setup", shown: false, allowAdvancing: true}, {extension: "setup-finish", shown: false, allowAdvancing: true}];
						beo.setup = false;
						beo.bus.emit("setup", {header: "finishingSetup"});
						beo.bus.emit("ui", {target: "setup", header: "setupStatus", content: {setupFlow: [], setup: "finished", selectedExtension: selectedExtension}});
						settings.firstTimeSetup = false;
						beo.bus.emit("settings", {header: "saveSettings", content: {extension: "setup", settings: settings}});
						if (restartAfter) {
							beo.bus.emit("general", {header: "requestReboot", content: {extension: "setup"}});
						}
					}
					break;
				}
			}
		}

	});

	function joinSetupFlow(extension, options) {
		// An extension can join the setup flow at any time, but it might not get its preferred placement if some extension have already been shown to the user.

		joins = true;
		for (var i = 0; i < setupFlow.length; i++) {
			// Check that this extension doesn't already exist in the flow.
			if (setupFlow[i].extension == extension) {
				joins = false;
				break;
			}
		}
		if (extension == "setup" && extension == "setup-finish") joins = false;
		if (joins) {
			// Create an entry.
			allowAdvancing = (options.allowAdvancing) ? true : false
			newStep = {extension: extension, shown: false, allowAdvancing: allowAdvancing};
			/*if (options.before) newStep.before = options.before;
			if (options.after) newStep.after = options.after;
			// Next, determine the placement.
			endIndex = setupFlow.length-1; // Default to adding the extension to the end, but before the finishing step.

			// First fill out these indexes, then check if we need to resolve conflicts:
			beforeExisting = null; // If the new extension wants to be before an existing one.
			beforeNew = null; // If an existing extension wants to be before the new one.
			afterExisting = null; // If the new extension wants to be after an existing one.
			afterNew = null; // If an existing extension wants to be after the new one.
			lastShown = 0;

			for (var i = 1; i < setupFlow.length; i++) {
				if (newStep.before) {
					if (newStep.before.indexOf(setupFlow[i].extension) != -1) {
						// Only find the first extension that matches.
						if (beforeExisting == null) beforeExisting = i;
					}
				}
				if (newStep.after) {
					if (newStep.after.indexOf(setupFlow[i].extension) != -1) {
						afterExisting = i+1;
					}
				}
				if (setupFlow[i].before) {
					if (setupFlow[i].before.indexOf(newStep.extension) != -1) {
						beforeNew = i+1;
					}
				}
				if (setupFlow[i].after) {
					if (setupFlow[i].after.indexOf(newStep.extension) != -1) {
						afterNew = i;
					}
				}
				if (setupFlow[i].shown) lastShown = i+1;
			}
			if (beforeExisting == null && afterExisting == null && beforeNew == null && afterNew == null) {
				placedIndex = endIndex;
			} else {
				placedIndex = Math.max(beforeExisting, afterExisting, beforeNew, afterNew, lastShown);
			}

			setupFlow.splice(placedIndex, 0, newStep); // Add to the flow.*/

			// Temporary fixed sort.
			theOrder = ["setup", "choose-country", "network", "speaker-preset", "product-information", "setup-finish"];
			tempFlow = [];
			for (i in setupFlow) {
				tempFlow[theOrder.indexOf(setupFlow[i].extension)] = setupFlow[i];
			}
			index = theOrder.indexOf(extension);
			tempFlow[index] = {extension: extension, before: options.before, after: options.after};
			setupFlow = tempFlow.filter(function (el) {
		  		return el != null;
			});

			beo.bus.emit("ui", {target: "setup", header: "joinSetupFlow", content: {extension: extension, setupFlow: setupFlow}});
			if (debug) console.log("Extension '"+extension+"' joined setup flow.");
			checkIfMoreSteps();
		}
		return joins;
	}

	function allowAdvancing(extension, allow) {
		// Controls whether or not the "Next Step" button is enabled for a given extension.
		if (extension != "setup" && extension != "setup-finish") {
			for (var i = 0; i < setupFlow.length; i++) {
				if (setupFlow[i].extension == extension) {
					setupFlow[i].allowAdvancing = allow;
					beo.bus.emit("ui", {target: "setup", header: "allowAdvancing", content: {extension: extension, allow: allow}});
					break;
				}
			}
		}
	}

	function restartWhenComplete(extension, restart) {
		// Allows an extension to request system reboot when the setup is complete.
		restartAfterTemp = false;
		for (var i = 0; i < setupFlow.length; i++) {
			if (setupFlow[i].extension == extension) {
				setupFlow[i].restart = restart;
				break;
			}
			if (setupFlow[i].restart) restartAfterTemp = true;
		}
		if (restartAfter != restartAfterTemp) {
			restartAfter = restartAfterTemp;
			beo.bus.emit("ui", {target: "setup", header: "restartAfter", content: {restartAfter: restartAfter}});
		}
	}

	function leaveSetupFlow(extension) {
		// If an extension decides it doesn't need setup anymore, it can leave the setup flow even during setup if it hasn't been shown yet.
		leaves = false;
		if (extension != "setup" && extension != "setup-finish") {
			for (var i = 0; i < setupFlow.length; i++) {
				if (setupFlow[i].extension == extension) {
					if (!setupFlow[i].shown) {
						leaves = true;
					} else {
						// If the extension can't be removed (it has been already shown), allow advancing over it.
						setupFlow[i].allowAdvancing = true;
						beo.bus.emit("ui", {target: "setup", header: "allowAdvancing", content: {extension: extension, allow: true}});
					}
					break;
				}
			}
			if (leaves) {
				setupFlow.splice(i, 1);
				beo.bus.emit("ui", {target: "setup", header: "leaveSetupFlow", content: {extension: extension, setupFlow: setupFlow}});
				checkIfMoreSteps();
			}
		}
		return leaves;
	}

	function checkIfMoreSteps(setShown) {
		for (var i = 0; i < setupFlow.length; i++) {
			// Check and send if more extensions are queued. Controls if the assistant bar shows "Next Step" or "Finish Setup".
			if (setupFlow[i].extension == selectedExtension) {
				if (setShown) setupFlow[i].shown = true;
				if (i < setupFlow.length-1) {
					beo.bus.emit("ui", {target: "setup", header: "assistantButton", content: {lastStep: false}});
				} else {
					beo.bus.emit("ui", {target: "setup", header: "assistantButton", content: {lastStep: true}});
				}
				break;
			}
		}
	}

module.exports = {
	joinSetupFlow: joinSetupFlow,
	leaveSetupFlow: leaveSetupFlow,
	allowAdvancing: allowAdvancing,
	restartWhenComplete: restartWhenComplete,
	setupFlow: setupFlow,
	version: version
};

<|endoftext|>


<commit_before>/*jslint node: true */
"use strict";
var db = require('./db.js');

function generateQueriesToArchiveJoint(conn, objJoint, reason, arrQueries, cb){
	var func = (reason === 'uncovered') ? generateQueriesToRemoveJoint : generateQueriesToVoidJoint;
	func(conn, objJoint.unit.unit, arrQueries, function(){
		conn.addQuery(arrQueries, "INSERT "+conn.getIgnore()+" INTO archived_joints (unit, reason, json) VALUES (?,?,?)",
			[objJoint.unit.unit, reason, JSON.stringify(objJoint)]);
		cb();
	});
}

function generateQueriesToRemoveJoint(conn, unit, arrQueries, cb){
	generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
		conn.addQuery(arrQueries, "DELETE FROM sent_mnemonics WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM witness_list_hashes WHERE witness_list_unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM earned_headers_commission_recipients WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM unit_witnesses WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM authentifiers WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM unit_authors WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM parenthoods WHERE child_unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM address_definition_changes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM inputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM outputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM spend_proofs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM data_feeds WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM poll_choices WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM polls WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM votes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attested_fields WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attestations WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_denominations WHERE asset=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_attestors WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM assets WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM messages WHERE unit=?", [unit]);
	//	conn.addQuery(arrQueries, "DELETE FROM balls WHERE unit=?", [unit]); // if it has a ball, it can't be uncovered
		conn.addQuery(arrQueries, "DELETE FROM units WHERE unit=?", [unit]);
		cb();
	});
}

function generateQueriesToVoidJoint(conn, unit, arrQueries, cb){
	generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
		// we keep witnesses, author addresses, and the unit itself
		conn.addQuery(arrQueries, "DELETE FROM witness_list_hashes WHERE witness_list_unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM earned_headers_commission_recipients WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM authentifiers WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "UPDATE unit_authors SET definition_chash=NULL WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM address_definition_changes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM inputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM outputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM spend_proofs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM data_feeds WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM poll_choices WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM polls WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM votes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attested_fields WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attestations WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_denominations WHERE asset=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_attestors WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM assets WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM messages WHERE unit=?", [unit]);
		cb();
	});
}

function generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
		generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
			generateQueriesToUnspendWitnessingOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb);
		});
	});
}

function generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	conn.query(
		"SELECT src_unit, src_message_index, src_output_index \n\
		FROM inputs \n\
		WHERE inputs.unit=? \n\
			AND inputs.type='transfer' \n\
			AND NOT EXISTS ( \n\
				SELECT 1 FROM inputs AS alt_inputs \n\
				WHERE inputs.src_unit=alt_inputs.src_unit \n\
					AND inputs.src_message_index=alt_inputs.src_message_index \n\
					AND inputs.src_output_index=alt_inputs.src_output_index \n\
					AND alt_inputs.type='transfer' \n\
					AND inputs.unit!=alt_inputs.unit \n\
			)",
		[unit],
		function(rows){
			rows.forEach(function(row){
				conn.addQuery(
					arrQueries,
					"UPDATE outputs SET is_spent=0 WHERE unit=? AND message_index=? AND output_index=?",
					[row.src_unit, row.src_message_index, row.src_output_index]
				);
			});
			cb();
		}
	);
}

function generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	conn.query(
		"SELECT headers_commission_outputs.address, headers_commission_outputs.main_chain_index \n\
		FROM inputs \n\
		CROSS JOIN headers_commission_outputs \n\
			ON inputs.from_main_chain_index <= +headers_commission_outputs.main_chain_index \n\
			AND inputs.to_main_chain_index >= +headers_commission_outputs.main_chain_index \n\
			AND inputs.address = headers_commission_outputs.address \n\
		WHERE inputs.unit=? \n\
			AND inputs.type='headers_commission' \n\
			AND NOT EXISTS ( \n\
				SELECT 1 FROM inputs AS alt_inputs \n\
				WHERE headers_commission_outputs.main_chain_index >= alt_inputs.from_main_chain_index \n\
					AND headers_commission_outputs.main_chain_index <= alt_inputs.to_main_chain_index \n\
					AND inputs.address=alt_inputs.address \n\
					AND alt_inputs.type='headers_commission' \n\
					AND inputs.unit!=alt_inputs.unit \n\
			)",
		[unit],
		function(rows){
			rows.forEach(function(row){
				conn.addQuery(
					arrQueries,
					"UPDATE headers_commission_outputs SET is_spent=0 WHERE address=? AND main_chain_index=?",
					[row.address, row.main_chain_index]
				);
			});
			cb();
		}
	);
}

function generateQueriesToUnspendWitnessingOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	conn.query(
		"SELECT witnessing_outputs.address, witnessing_outputs.main_chain_index \n\
		FROM inputs \n\
		CROSS JOIN witnessing_outputs \n\
			ON inputs.from_main_chain_index <= +witnessing_outputs.main_chain_index \n\
			AND inputs.to_main_chain_index >= +witnessing_outputs.main_chain_index \n\
			AND inputs.address = witnessing_outputs.address \n\
		WHERE inputs.unit=? \n\
			AND inputs.type='witnessing' \n\
			AND NOT EXISTS ( \n\
				SELECT 1 FROM inputs AS alt_inputs \n\
				WHERE witnessing_outputs.main_chain_index >= alt_inputs.from_main_chain_index \n\
					AND witnessing_outputs.main_chain_index <= alt_inputs.to_main_chain_index \n\
					AND inputs.address=alt_inputs.address \n\
					AND alt_inputs.type='witnessing' \n\
					AND inputs.unit!=alt_inputs.unit \n\
			)",
		[unit],
		function(rows){
			rows.forEach(function(row){
				conn.addQuery(
					arrQueries,
					"UPDATE witnessing_outputs SET is_spent=0 WHERE address=? AND main_chain_index=?",
					[row.address, row.main_chain_index]
				);
			});
			cb();
		}
	);
}

exports.generateQueriesToArchiveJoint = generateQueriesToArchiveJoint;

<commit_msg>purge asset_metadata when deleting unit<commit_after>/*jslint node: true */
"use strict";
var db = require('./db.js');

function generateQueriesToArchiveJoint(conn, objJoint, reason, arrQueries, cb){
	var func = (reason === 'uncovered') ? generateQueriesToRemoveJoint : generateQueriesToVoidJoint;
	func(conn, objJoint.unit.unit, arrQueries, function(){
		conn.addQuery(arrQueries, "INSERT "+conn.getIgnore()+" INTO archived_joints (unit, reason, json) VALUES (?,?,?)",
			[objJoint.unit.unit, reason, JSON.stringify(objJoint)]);
		cb();
	});
}

function generateQueriesToRemoveJoint(conn, unit, arrQueries, cb){
	generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
		conn.addQuery(arrQueries, "DELETE FROM sent_mnemonics WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM witness_list_hashes WHERE witness_list_unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM earned_headers_commission_recipients WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM unit_witnesses WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM authentifiers WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM unit_authors WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM parenthoods WHERE child_unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM address_definition_changes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM inputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM outputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM spend_proofs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM data_feeds WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM poll_choices WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM polls WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM votes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attested_fields WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attestations WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_metadata WHERE asset=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_denominations WHERE asset=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_attestors WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM assets WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM messages WHERE unit=?", [unit]);
	//	conn.addQuery(arrQueries, "DELETE FROM balls WHERE unit=?", [unit]); // if it has a ball, it can't be uncovered
		conn.addQuery(arrQueries, "DELETE FROM units WHERE unit=?", [unit]);
		cb();
	});
}

function generateQueriesToVoidJoint(conn, unit, arrQueries, cb){
	generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
		// we keep witnesses, author addresses, and the unit itself
		conn.addQuery(arrQueries, "DELETE FROM witness_list_hashes WHERE witness_list_unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM earned_headers_commission_recipients WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM authentifiers WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "UPDATE unit_authors SET definition_chash=NULL WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM address_definition_changes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM inputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM outputs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM spend_proofs WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM data_feeds WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM poll_choices WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM polls WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM votes WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attested_fields WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM attestations WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_metadata WHERE asset=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_denominations WHERE asset=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM asset_attestors WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM assets WHERE unit=?", [unit]);
		conn.addQuery(arrQueries, "DELETE FROM messages WHERE unit=?", [unit]);
		cb();
	});
}

function generateQueriesToUnspendOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
		generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(conn, unit, arrQueries, function(){
			generateQueriesToUnspendWitnessingOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb);
		});
	});
}

function generateQueriesToUnspendTransferOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	conn.query(
		"SELECT src_unit, src_message_index, src_output_index \n\
		FROM inputs \n\
		WHERE inputs.unit=? \n\
			AND inputs.type='transfer' \n\
			AND NOT EXISTS ( \n\
				SELECT 1 FROM inputs AS alt_inputs \n\
				WHERE inputs.src_unit=alt_inputs.src_unit \n\
					AND inputs.src_message_index=alt_inputs.src_message_index \n\
					AND inputs.src_output_index=alt_inputs.src_output_index \n\
					AND alt_inputs.type='transfer' \n\
					AND inputs.unit!=alt_inputs.unit \n\
			)",
		[unit],
		function(rows){
			rows.forEach(function(row){
				conn.addQuery(
					arrQueries,
					"UPDATE outputs SET is_spent=0 WHERE unit=? AND message_index=? AND output_index=?",
					[row.src_unit, row.src_message_index, row.src_output_index]
				);
			});
			cb();
		}
	);
}

function generateQueriesToUnspendHeadersCommissionOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	conn.query(
		"SELECT headers_commission_outputs.address, headers_commission_outputs.main_chain_index \n\
		FROM inputs \n\
		CROSS JOIN headers_commission_outputs \n\
			ON inputs.from_main_chain_index <= +headers_commission_outputs.main_chain_index \n\
			AND inputs.to_main_chain_index >= +headers_commission_outputs.main_chain_index \n\
			AND inputs.address = headers_commission_outputs.address \n\
		WHERE inputs.unit=? \n\
			AND inputs.type='headers_commission' \n\
			AND NOT EXISTS ( \n\
				SELECT 1 FROM inputs AS alt_inputs \n\
				WHERE headers_commission_outputs.main_chain_index >= alt_inputs.from_main_chain_index \n\
					AND headers_commission_outputs.main_chain_index <= alt_inputs.to_main_chain_index \n\
					AND inputs.address=alt_inputs.address \n\
					AND alt_inputs.type='headers_commission' \n\
					AND inputs.unit!=alt_inputs.unit \n\
			)",
		[unit],
		function(rows){
			rows.forEach(function(row){
				conn.addQuery(
					arrQueries,
					"UPDATE headers_commission_outputs SET is_spent=0 WHERE address=? AND main_chain_index=?",
					[row.address, row.main_chain_index]
				);
			});
			cb();
		}
	);
}

function generateQueriesToUnspendWitnessingOutputsSpentInArchivedUnit(conn, unit, arrQueries, cb){
	conn.query(
		"SELECT witnessing_outputs.address, witnessing_outputs.main_chain_index \n\
		FROM inputs \n\
		CROSS JOIN witnessing_outputs \n\
			ON inputs.from_main_chain_index <= +witnessing_outputs.main_chain_index \n\
			AND inputs.to_main_chain_index >= +witnessing_outputs.main_chain_index \n\
			AND inputs.address = witnessing_outputs.address \n\
		WHERE inputs.unit=? \n\
			AND inputs.type='witnessing' \n\
			AND NOT EXISTS ( \n\
				SELECT 1 FROM inputs AS alt_inputs \n\
				WHERE witnessing_outputs.main_chain_index >= alt_inputs.from_main_chain_index \n\
					AND witnessing_outputs.main_chain_index <= alt_inputs.to_main_chain_index \n\
					AND inputs.address=alt_inputs.address \n\
					AND alt_inputs.type='witnessing' \n\
					AND inputs.unit!=alt_inputs.unit \n\
			)",
		[unit],
		function(rows){
			rows.forEach(function(row){
				conn.addQuery(
					arrQueries,
					"UPDATE witnessing_outputs SET is_spent=0 WHERE address=? AND main_chain_index=?",
					[row.address, row.main_chain_index]
				);
			});
			cb();
		}
	);
}

exports.generateQueriesToArchiveJoint = generateQueriesToArchiveJoint;

<|endoftext|>


<commit_before>/*
* $ lightbox_me
* By: Buck Wilson
* Version : 2.3
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

(function($) {

    $.fn.lightbox_me = function(options) {

        return this.each(function() {

            var
                opts = $.extend({}, $.fn.lightbox_me.defaults, options),
                $overlay = $(),
                $self = $(this),
                $iframe = $(''),
                ie6 = ($.browser.msie && $.browser.version < 7);

            if (opts.showOverlay) {
                //check if there's an existing overlay, if so, make subequent ones clear
               var $currentOverlays = $(".js_lb_overlay:visible");
                if ($currentOverlays.length > 0){
                    $overlay = $('
');
                } else {
                    $overlay = $('
');
                }
            }

            /*----------------------------------------------------
               DOM Building
            ---------------------------------------------------- */
            if (ie6) {
                var src = /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank';
                $iframe.attr('src', src);
                $('body').append($iframe);
            } // iframe shim for ie6, to hide select elements
            $('body').append($self.hide()).append($overlay);

            /*----------------------------------------------------
               Overlay CSS stuffs
            ---------------------------------------------------- */

            // set css of the overlay
            if (opts.showOverlay) {
                setOverlayHeight(); // pulled this into a function because it is called on window resize.
                $overlay.css({ position: 'absolute', width: '100%', top: 0, left: 0, right: 0, bottom: 0, zIndex: (opts.zIndex + 2), display: 'none' });
				if (!$overlay.hasClass('lb_overlay_clear')){
                	$overlay.css(opts.overlayCSS);
                }
            }

            /*----------------------------------------------------
               Animate it in.
            ---------------------------------------------------- */
               //
            if (opts.showOverlay) {
                $overlay.fadeIn(opts.overlaySpeed, function() {
                    setSelfPosition();
                    $self[opts.appearEffect](opts.lightboxSpeed, function() { setOverlayHeight(); setSelfPosition(); opts.onLoad()});
                });
            } else {
                setSelfPosition();
                $self[opts.appearEffect](opts.lightboxSpeed, function() { opts.onLoad()});
            }

            /*----------------------------------------------------
               Hide parent if parent specified (parentLightbox should be jquery reference to any parent lightbox)
            ---------------------------------------------------- */
            if (opts.parentLightbox) {
                opts.parentLightbox.fadeOut(200);
            }

            /*----------------------------------------------------
               Bind Events
            ---------------------------------------------------- */

            $(window).resize(setOverlayHeight)
                     .resize(setSelfPosition)
                     .scroll(setSelfPosition);

            $(window).bind('keyup.lightbox_me', observeKeyPress);

            if (opts.closeClick) {
                $overlay.click(function(e) { closeLightbox(); e.preventDefault; });
            }
            $self.delegate(opts.closeSelector, "click", function(e) {
                closeLightbox(); e.preventDefault();
            });
            $self.bind('close', closeLightbox);
            $self.bind('reposition', setSelfPosition);

            /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
              -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */

            /*----------------------------------------------------
               Private Functions
            ---------------------------------------------------- */

            /* Remove or hide all elements */
            function closeLightbox() {
                var s = $self[0].style;
                if (opts.destroyOnClose) {
                    $self.add($overlay).remove();
                } else {
                    $self.add($overlay).hide();
                }

                //show the hidden parent lightbox
                if (opts.parentLightbox) {
                    opts.parentLightbox.fadeIn(200);
                }

                $iframe.remove();

				// clean up events.
                $self.undelegate(opts.closeSelector, "click");

                $(window).unbind('reposition', setOverlayHeight);
                $(window).unbind('reposition', setSelfPosition);
                $(window).unbind('scroll', setSelfPosition);
                $(window).unbind('keyup.lightbox_me');
                if (ie6)
                    s.removeExpression('top');
                opts.onClose();
            }

            /* Function to bind to the window to observe the escape/enter key press */
            function observeKeyPress(e) {
                if((e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)) && opts.closeEsc) closeLightbox();
            }

            /* Set the height of the overlay
                    : if the document height is taller than the window, then set the overlay height to the document height.
                    : otherwise, just set overlay height: 100%
            */
            function setOverlayHeight() {
                if ($(window).height() < $(document).height()) {
                    $overlay.css({height: $(document).height() + 'px'});
                     $iframe.css({height: $(document).height() + 'px'});
                } else {
                    $overlay.css({height: '100%'});
                    if (ie6) {
                        $('html,body').css('height','100%');
                        $iframe.css('height', '100%');
                    } // ie6 hack for height: 100%; TODO: handle this in IE7
                }
            }

            /* Set the position of the modal'd window ($self)
                    : if $self is taller than the window, then make it absolutely positioned
                    : otherwise fixed
            */
            function setSelfPosition() {
                var s = $self[0].style;

                // reset CSS so width is re-calculated for margin-left CSS
                $self.css({left: '50%', marginLeft: ($self.outerWidth() / 2) * -1,  zIndex: (opts.zIndex + 3) });

                /* we have to get a little fancy when dealing with height, because lightbox_me
                    is just so fancy.
                 */

                // if the height of $self is bigger than the window and self isn't already position absolute
                if (($self.height() + 80  >= $(window).height()) && ($self.css('position') != 'absolute' || ie6)) {

                    // we are going to make it positioned where the user can see it, but they can still scroll
                    // so the top offset is based on the user's scroll position.
                    var topOffset = $(document).scrollTop() + 40;
                    $self.css({position: 'absolute', top: topOffset + 'px', marginTop: 0})
                    if (ie6) {
                        s.removeExpression('top');
                    }
                } else if ($self.height()+ 80  < $(window).height()) {
                    //if the height is less than the window height, then we're gonna make this thing position: fixed.
                    // in ie6 we're gonna fake it.
                    if (ie6) {
                        s.position = 'absolute';
                        if (opts.centered) {
                            s.setExpression('top', '(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"')
                            s.marginTop = 0;
                        } else {
                            var top = (opts.modalCSS && opts.modalCSS.top) ? parseInt(opts.modalCSS.top) : 0;
                            s.setExpression('top', '((blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"')
                        }
                    } else {
                        if (opts.centered) {
                            $self.css({ position: 'fixed', top: '50%', marginTop: ($self.outerHeight() / 2) * -1})
                        } else {
                            $self.css({ position: 'fixed'}).css(opts.modalCSS);
                        }

                    }
                }
            }

        });

    };

    $.fn.lightbox_me.defaults = {

        // animation
        appearEffect: "fadeIn",
        appearEase: "",
        overlaySpeed: 250,
        lightboxSpeed: 300,

        // close
        closeSelector: ".close",
        closeClick: true,
        closeEsc: true,

        // behavior
        destroyOnClose: false,
        showOverlay: true,
        parentLightbox: false,

        // callbacks
        onLoad: function() {},
        onClose: function() {},

        // style
        classPrefix: 'lb',
        zIndex: 999,
        centered: false,
        modalCSS: {top: '40px'},
        overlayCSS: {background: 'black', opacity: .3}
    }
})(jQuery);<commit_msg>[#7213] ticket:553 Fixed jquery.lightbox_me plugin 'access denied' errors for IE9-11<commit_after>/*
* $ lightbox_me
* By: Buck Wilson
* Version : 2.3
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

(function($) {

    $.fn.lightbox_me = function(options) {

        return this.each(function() {

            var
                opts = $.extend({}, $.fn.lightbox_me.defaults, options),
                $overlay = $(),
                $self = $(this),
                $iframe = $('', {style: 'z-index: ' + (opts.zIndex + 1) + ';border: none; margin: 0; padding: 0; position: absolute; width: 100%; height: 100%; top: 0; left: 0; filter: mask();'});
                ie6 = ($.browser.msie && $.browser.version < 7);

            if (opts.showOverlay) {
                //check if there's an existing overlay, if so, make subequent ones clear
               var $currentOverlays = $(".js_lb_overlay:visible");
                if ($currentOverlays.length > 0){
                    $overlay = $('
', {'class': 'lb_overlay_clear js_lb_overlay'});
                } else {
                    $overlay = $('
', {'class': opts.classPrefix + '_overlay js_lb_overlay'});
                }
            }

            /*----------------------------------------------------
               DOM Building
            ---------------------------------------------------- */
            if (ie6) {
                var src = /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank';
                $iframe.attr('src', src);
                $('body').append($iframe);
            } // iframe shim for ie6, to hide select elements
            $('body').append($self.hide()).append($overlay);

            /*----------------------------------------------------
               Overlay CSS stuffs
            ---------------------------------------------------- */

            // set css of the overlay
            if (opts.showOverlay) {
                setOverlayHeight(); // pulled this into a function because it is called on window resize.
                $overlay.css({ position: 'absolute', width: '100%', top: 0, left: 0, right: 0, bottom: 0, zIndex: (opts.zIndex + 2), display: 'none' });
				if (!$overlay.hasClass('lb_overlay_clear')){
                	$overlay.css(opts.overlayCSS);
                }
            }

            /*----------------------------------------------------
               Animate it in.
            ---------------------------------------------------- */
               //
            if (opts.showOverlay) {
                $overlay.fadeIn(opts.overlaySpeed, function() {
                    setSelfPosition();
                    $self[opts.appearEffect](opts.lightboxSpeed, function() { setOverlayHeight(); setSelfPosition(); opts.onLoad()});
                });
            } else {
                setSelfPosition();
                $self[opts.appearEffect](opts.lightboxSpeed, function() { opts.onLoad()});
            }

            /*----------------------------------------------------
               Hide parent if parent specified (parentLightbox should be jquery reference to any parent lightbox)
            ---------------------------------------------------- */
            if (opts.parentLightbox) {
                opts.parentLightbox.fadeOut(200);
            }

            /*----------------------------------------------------
               Bind Events
            ---------------------------------------------------- */

            $(window).resize(setOverlayHeight)
                     .resize(setSelfPosition)
                     .scroll(setSelfPosition);

            $(window).bind('keyup.lightbox_me', observeKeyPress);

            if (opts.closeClick) {
                $overlay.click(function(e) { closeLightbox(); e.preventDefault; });
            }
            $self.delegate(opts.closeSelector, "click", function(e) {
                closeLightbox(); e.preventDefault();
            });
            $self.bind('close', closeLightbox);
            $self.bind('reposition', setSelfPosition);

            /*--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
              -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- */

            /*----------------------------------------------------
               Private Functions
            ---------------------------------------------------- */

            /* Remove or hide all elements */
            function closeLightbox() {
                var s = $self[0].style;
                if (opts.destroyOnClose) {
                    $self.add($overlay).remove();
                } else {
                    $self.add($overlay).hide();
                }

                //show the hidden parent lightbox
                if (opts.parentLightbox) {
                    opts.parentLightbox.fadeIn(200);
                }

                $iframe.remove();

				// clean up events.
                $self.undelegate(opts.closeSelector, "click");

                $(window).unbind('reposition', setOverlayHeight);
                $(window).unbind('reposition', setSelfPosition);
                $(window).unbind('scroll', setSelfPosition);
                $(window).unbind('keyup.lightbox_me');
                if (ie6)
                    s.removeExpression('top');
                opts.onClose();
            }

            /* Function to bind to the window to observe the escape/enter key press */
            function observeKeyPress(e) {
                if((e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)) && opts.closeEsc) closeLightbox();
            }

            /* Set the height of the overlay
                    : if the document height is taller than the window, then set the overlay height to the document height.
                    : otherwise, just set overlay height: 100%
            */
            function setOverlayHeight() {
                if ($(window).height() < $(document).height()) {
                    $overlay.css({height: $(document).height() + 'px'});
                     $iframe.css({height: $(document).height() + 'px'});
                } else {
                    $overlay.css({height: '100%'});
                    if (ie6) {
                        $('html,body').css('height','100%');
                        $iframe.css('height', '100%');
                    } // ie6 hack for height: 100%; TODO: handle this in IE7
                }
            }

            /* Set the position of the modal'd window ($self)
                    : if $self is taller than the window, then make it absolutely positioned
                    : otherwise fixed
            */
            function setSelfPosition() {
                var s = $self[0].style;

                // reset CSS so width is re-calculated for margin-left CSS
                $self.css({left: '50%', marginLeft: ($self.outerWidth() / 2) * -1,  zIndex: (opts.zIndex + 3) });

                /* we have to get a little fancy when dealing with height, because lightbox_me
                    is just so fancy.
                 */

                // if the height of $self is bigger than the window and self isn't already position absolute
                if (($self.height() + 80  >= $(window).height()) && ($self.css('position') != 'absolute' || ie6)) {

                    // we are going to make it positioned where the user can see it, but they can still scroll
                    // so the top offset is based on the user's scroll position.
                    var topOffset = $(document).scrollTop() + 40;
                    $self.css({position: 'absolute', top: topOffset + 'px', marginTop: 0})
                    if (ie6) {
                        s.removeExpression('top');
                    }
                } else if ($self.height()+ 80  < $(window).height()) {
                    //if the height is less than the window height, then we're gonna make this thing position: fixed.
                    // in ie6 we're gonna fake it.
                    if (ie6) {
                        s.position = 'absolute';
                        if (opts.centered) {
                            s.setExpression('top', '(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"')
                            s.marginTop = 0;
                        } else {
                            var top = (opts.modalCSS && opts.modalCSS.top) ? parseInt(opts.modalCSS.top) : 0;
                            s.setExpression('top', '((blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"')
                        }
                    } else {
                        if (opts.centered) {
                            $self.css({ position: 'fixed', top: '50%', marginTop: ($self.outerHeight() / 2) * -1})
                        } else {
                            $self.css({ position: 'fixed'}).css(opts.modalCSS);
                        }

                    }
                }
            }

        });

    };

    $.fn.lightbox_me.defaults = {

        // animation
        appearEffect: "fadeIn",
        appearEase: "",
        overlaySpeed: 250,
        lightboxSpeed: 300,

        // close
        closeSelector: ".close",
        closeClick: true,
        closeEsc: true,

        // behavior
        destroyOnClose: false,
        showOverlay: true,
        parentLightbox: false,

        // callbacks
        onLoad: function() {},
        onClose: function() {},

        // style
        classPrefix: 'lb',
        zIndex: 999,
        centered: false,
        modalCSS: {top: '40px'},
        overlayCSS: {background: 'black', opacity: .3}
    }
})(jQuery);
<|endoftext|>


<commit_before>(function (window, angular, undefined) {
    'use strict';

    angular
        .module('MainPage')
        .controller('GoodsPage', GoodsPage);

    GoodsPage.$inject = ['auxData', 'goodsSvc'];
    function GoodsPage(auxData, goodsSvc) {
        var vm = this;

        vm.getImgUrl = getImgUrl;

        initController();

        // IMPLEMENTATION

        function initController() {
            vm.list = [];
            vm.currentPage = 1;
            vm.currentItemsPerPage = 24;

            goodsSvc.load()
                .then((goods)=> {
                    vm.list = goods.data;
                });
        }

        function getImgUrl(good) {
            return '/uploads/ready/' + good.urlDir + '/' + good.urlGifLoRes;
        }

    }

})(window, window.angular);
<commit_msg>fix img path<commit_after>(function (window, angular, undefined) {
    'use strict';

    angular
        .module('MainPage')
        .controller('GoodsPage', GoodsPage);

    GoodsPage.$inject = ['auxData', 'goodsSvc'];
    function GoodsPage(auxData, goodsSvc) {
        var vm = this;

        vm.getImgUrl = getImgUrl;

        initController();

        // IMPLEMENTATION

        function initController() {
            vm.list = [];
            vm.currentPage = 1;
            vm.currentItemsPerPage = 24;

            goodsSvc.load()
                .then((goods)=> {
                    vm.list = goods.data;
                });
        }

        function getImgUrl(good) {
            return '/uploads/ready/' + good.urlDir + '/' + good.urlThumb;
        }

    }

})(window, window.angular);
<|endoftext|>


<commit_before>/**
 *
 * @returns {{className: string}}
 * @constructor
 * @memberof svl
 */
function Tracker () {
    var self = this;
    var actions = [];
    var prevActions = [];

    this.init = function () {
        this.trackWindowEvents();
    };

    this.trackWindowEvents = function() {
        var prefix = "LowLevelEvent_";

        // track all mouse related events
        $(document).on('mousedown mouseup mouseover mouseout mousemove click contextmenu dblclick', function(e) {
            self.push(prefix + e.type, {
                cursorX: e.originalEvent.x,
                cursorY: e.originalEvent.y
            });
        });

        // keyboard related events
        $(document).on('keydown keyup', function(e) {
            self.push(prefix + e.type, {
                keyCode: e.keyCode
            });
        });
    };

    this._isCanvasInteraction = function (action) {
        return action.indexOf("LabelingCanvas") >= 0;
    };

    this._isContextMenuAction = function (action) {
        return action.indexOf("ContextMenu") >= 0;
    };

    /** Returns actions */
    this.getActions = function () {
        return actions;
    };

    this._notesToString = function (param) {
        if (!param)
            return "";

        var note = "";
        for (var key in param) {
            if (note.length > 0)
                note += ",";
            note += key + ':' + param[key];
        }

        return note;
    };

    /**
     * This function pushes action type, time stamp, current pov, and current panoId into actions list.
     */

    this.create = function (action, notes, extraData) {
        if (!notes)
            notes = {};

        if (!extraData)
            extraData = {};

        var pov, latlng, panoId, temporaryLabelId;

        var note = this._notesToString(notes);

        if ('temporaryLabelId' in extraData) {
            temporaryLabelId = extraData['temporaryLabelId'];
        }

        // Initialize variables. Note you cannot get pov, panoid, or position
        // before the map and SV load.
        try {
            pov = svl.map.getPov();
        } catch (err) {
            pov = {
                heading: null,
                pitch: null,
                zoom: null
            }
        }

        try {
            latlng = svl.map.getPosition();
        } catch (err) {
            latlng = {
                lat: null,
                lng: null
            };
        }
        if (!latlng) {
            latlng = {
                lat: null,
                lng: null
            };
        }

        try {
            panoId = svl.map.getPanoId();
        } catch (err) {
            panoId = null;
        }

        var now = new Date(),
            timestamp = new Date().getTime();
        // timestamp = now.getUTCFullYear() + "-" + (now.getUTCMonth() + 1) + "-" + now.getUTCDate() + " " + now.getUTCHours() + ":" + now.getUTCMinutes() + ":" + now.getUTCSeconds() + "." + now.getUTCMilliseconds();

        var item = {
            action : action,
            gsv_panorama_id: panoId,
            lat: latlng.lat,
            lng: latlng.lng,
            heading: pov.heading,
            pitch: pov.pitch,
            zoom: pov.zoom,
            note: note,
            temporary_label_id: temporaryLabelId,
            timestamp: timestamp
        };
        return item;
    };

    /**
     * @param action: the action to be stored in the database
     * @param notes: (optional) the notes field in the database
     * @param extraData: (optional) extra data that should not be stored in the notes field in db
     */
    this.push = function (action, notes, extraData) {
        var item = self.create(action, notes, extraData);
        actions.push(item);

        // Submit the data collected thus far if actions is too long.
        if (actions.length > 200 && !self._isCanvasInteraction(action) && !self._isContextMenuAction(action)) {
            var task = svl.taskContainer.getCurrentTask();
            var data = svl.form.compileSubmissionData(task);
            svl.form.submit(data, task);
        }
        return this;
    };

    /**
     * Put the previous labeling actions into prevActions. Then refresh the current actions.
     */
    this.refresh = function () {
        prevActions = prevActions.concat(actions);
        actions = [];
        self.push("RefreshTracker");
    };

    this.init();
}

<commit_msg>Fix small bug that happens in cases where events are artificially triggered<commit_after>/**
 *
 * @returns {{className: string}}
 * @constructor
 * @memberof svl
 */
function Tracker () {
    var self = this;
    var actions = [];
    var prevActions = [];

    this.init = function () {
        this.trackWindowEvents();
    };

    this.trackWindowEvents = function() {
        var prefix = "LowLevelEvent_";

        // track all mouse related events
        $(document).on('mousedown mouseup mouseover mouseout mousemove click contextmenu dblclick', function(e) {
            self.push(prefix + e.type, {
                cursorX: 'pageX' in e ? e.pageX : null,
                cursorY: 'pageY' in e ? e.pageY : null
            });
        });

        // keyboard related events
        $(document).on('keydown keyup', function(e) {
            self.push(prefix + e.type, {
                keyCode: 'keyCode' in e ? e.keyCode : null
            });
        });
    };

    this._isCanvasInteraction = function (action) {
        return action.indexOf("LabelingCanvas") >= 0;
    };

    this._isContextMenuAction = function (action) {
        return action.indexOf("ContextMenu") >= 0;
    };

    /** Returns actions */
    this.getActions = function () {
        return actions;
    };

    this._notesToString = function (param) {
        if (!param)
            return "";

        var note = "";
        for (var key in param) {
            if (note.length > 0)
                note += ",";
            note += key + ':' + param[key];
        }

        return note;
    };

    /**
     * This function pushes action type, time stamp, current pov, and current panoId into actions list.
     */

    this.create = function (action, notes, extraData) {
        if (!notes)
            notes = {};

        if (!extraData)
            extraData = {};

        var pov, latlng, panoId, temporaryLabelId;

        var note = this._notesToString(notes);

        if ('temporaryLabelId' in extraData) {
            temporaryLabelId = extraData['temporaryLabelId'];
        }

        // Initialize variables. Note you cannot get pov, panoid, or position
        // before the map and SV load.
        try {
            pov = svl.map.getPov();
        } catch (err) {
            pov = {
                heading: null,
                pitch: null,
                zoom: null
            }
        }

        try {
            latlng = svl.map.getPosition();
        } catch (err) {
            latlng = {
                lat: null,
                lng: null
            };
        }
        if (!latlng) {
            latlng = {
                lat: null,
                lng: null
            };
        }

        try {
            panoId = svl.map.getPanoId();
        } catch (err) {
            panoId = null;
        }

        var now = new Date(),
            timestamp = new Date().getTime();
        // timestamp = now.getUTCFullYear() + "-" + (now.getUTCMonth() + 1) + "-" + now.getUTCDate() + " " + now.getUTCHours() + ":" + now.getUTCMinutes() + ":" + now.getUTCSeconds() + "." + now.getUTCMilliseconds();

        var item = {
            action : action,
            gsv_panorama_id: panoId,
            lat: latlng.lat,
            lng: latlng.lng,
            heading: pov.heading,
            pitch: pov.pitch,
            zoom: pov.zoom,
            note: note,
            temporary_label_id: temporaryLabelId,
            timestamp: timestamp
        };
        return item;
    };

    /**
     * @param action: the action to be stored in the database
     * @param notes: (optional) the notes field in the database
     * @param extraData: (optional) extra data that should not be stored in the notes field in db
     */
    this.push = function (action, notes, extraData) {
        var item = self.create(action, notes, extraData);
        actions.push(item);

        // Submit the data collected thus far if actions is too long.
        if (actions.length > 200 && !self._isCanvasInteraction(action) && !self._isContextMenuAction(action)) {
            var task = svl.taskContainer.getCurrentTask();
            var data = svl.form.compileSubmissionData(task);
            svl.form.submit(data, task);
        }
        return this;
    };

    /**
     * Put the previous labeling actions into prevActions. Then refresh the current actions.
     */
    this.refresh = function () {
        prevActions = prevActions.concat(actions);
        actions = [];
        self.push("RefreshTracker");
    };

    this.init();
}

<|endoftext|>