<commit_before>/**
 * Created by Mauriel on 2/9/2016.
 */
// Map js
var coverageMap;
var allOverlays = [];
var allShapes = []; // Keeps track of shapes added by text change events
var drawingManager;

$(document).ready(function () {
    // Draw marker on text change
    $("#id_east").bind('input', drawMarkerOnTextChange);
    $("#id_north").bind('input', drawMarkerOnTextChange);

    // Draw rectangle on text change
    $("#id_northlimit").bind('input', drawRectangleOnTextChange);
    $("#id_eastlimit").bind('input', drawRectangleOnTextChange);
    $("#id_southlimit").bind('input', drawRectangleOnTextChange);
    $("#id_westlimit").bind('input', drawRectangleOnTextChange);

    var $radioPoint = $('input[type="radio"][value="point"]'); // id_type_2
    var $radioBox = $('input[type="radio"][value="box"]'); // id_type_1
    // Set initial coverage fields state
    if ($radioBox.is(':checked')) { //box type coverage
        $("#div_id_north").hide();
        $("#div_id_east").hide();
        $("#div_id_elevation").hide();
    }
    if ($radioPoint.is(':checked')) { // point type coverage
        $("#div_id_northlimit").hide();
        $("#div_id_eastlimit").hide();
        $("#div_id_southlimit").hide();
        $("#div_id_westlimit").hide();
        $("#div_id_uplimit").hide();
        $("#div_id_downlimit").hide();
    }

    if ($("#coverageMap").length) {
        google.maps.event.addDomListener(window, "load", initMap);
    }
});

function drawInitialShape() {
    // This field is populated if the page is in view mode
    var shapeType = $("#coverageMap")[0].getAttribute("data-shape-type");

    var resourceType = $("#resource-type").val();
    // Center the map
    if (shapeType || resourceType === "Time Series") {
        deleteAllShapes();
        if (shapeType == "point" || (resourceType === "Time Series" && spatial_coverage_type == "point")) {
            var myLatLng;
            if (shapeType == "point") {
                // resource view mode
                myLatLng = {
                    lat: parseFloat($("#cov_north").text()),
                    lng: parseFloat($("#cov_east").text())
                };
            }
            else {
                // time series resource in edit mode
                myLatLng = {
                    lat: parseFloat($("#id_north").val()),
                    lng: parseFloat($("#id_east").val())
                };
                if ($('#id_north').val()) {
                    $("#id_name").prop('readonly', false);
                }
            }

            if (!myLatLng.lat || !myLatLng.lng) {
                return;
            }
            // Define the rectangle and set its editable property to true.
            var marker = new google.maps.Marker({
                position: myLatLng,
                map: coverageMap
            });
            allShapes.push(marker);
            // Center map at new market
            coverageMap.setCenter(marker.getPosition());
            $("#coverageMap").on("click", "#resetZoomBtn", function () {
                coverageMap.setCenter(marker.getPosition());
            });
        }
        else if (shapeType == "box" || (resourceType === "Time Series" && spatial_coverage_type == "box")) {
            var bounds;
            if (shapeType == "box") {
                //resource view mode
                bounds = {
                    north: parseFloat($("#cov_northlimit").text()),
                    south: parseFloat($("#cov_southlimit").text()),
                    east: parseFloat($("#cov_eastlimit").text()),
                    west: parseFloat($("#cov_westlimit").text())
                };
            }
            else {
                // time series resource edit mode
                bounds = {
                    north: parseFloat($("#id_northlimit").val()),
                    south: parseFloat($("#id_southlimit").val()),
                    east: parseFloat($("#id_eastlimit").val()),
                    west: parseFloat($("#id_westlimit").val())
                };
                if ($('#id_northlimit').val()) {
                    $("#id_name").prop('readonly', false);
                }
            }

            if (bounds.north === null || bounds.south === null || bounds.east === null || bounds.west === null) {
                return;
            }
            // Define the rectangle and set its editable property to true.
            var rectangle = new google.maps.Rectangle({
                bounds: bounds,
                editable: false,
                draggable: false
            });
            rectangle.setMap(coverageMap);
            allShapes.push(rectangle);
            zoomCoverageMap(bounds);
             $("#coverageMap").on("click", "#resetZoomBtn", function () {
                zoomCoverageMap(bounds);
            });
        }
    }
    else {
        var $radioBox = $('input[type="radio"][value="box"]'); // id_type_1
        if ($radioBox.is(":checked")) {
            drawRectangleOnTextChange();
        }
        else {
            drawMarkerOnTextChange();
        }
    }
    $("#id-coverage-spatial input:radio").change(function () {
        if ($(this).val() == "point") {
            $("#div_id_north").show();
            $("#div_id_east").show();
            $("#div_id_elevation").show();
            $("#div_id_northlimit").hide();
            $("#div_id_eastlimit").hide();
            $("#div_id_southlimit").hide();
            $("#div_id_westlimit").hide();
            $("#div_id_uplimit").hide();
            $("#div_id_downlimit").hide();
            drawMarkerOnTextChange();
            drawingManager.setDrawingMode(google.maps.drawing.OverlayType.MARKER);
        }
        else {
            $("#div_id_north").hide();
            $("#div_id_east").hide();
            $("#div_id_elevation").hide();
            $("#div_id_northlimit").show();
            $("#div_id_eastlimit").show();
            $("#div_id_southlimit").show();
            $("#div_id_westlimit").show();
            $("#div_id_uplimit").show();
            $("#div_id_downlimit").show();
            drawRectangleOnTextChange();
            drawingManager.setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE);
        }
        // Show save changes button
        $("#coverage-spatial").find(".btn-primary").show();
    });
    if (sessionStorage.signininfo) {
        $("#sign-in-info").text(sessionStorage.signininfo);
        $("#btn-select-irods-file").show();
    }
}

function initMap() {
    var shapeType;
    var resourceType;
    if ($("#coverageMap")[0]) {
        // data-shape-type is set to have a value only in resource view mode
        shapeType = $("#coverageMap")[0].getAttribute("data-shape-type");
        resourceType = $("#resource-type").val();
        if (resourceType === "Time Series"){
            // set to view mode
            shapeType = " ";
        }
    }

    coverageMap = new google.maps.Map(document.getElementById('coverageMap'), {
        color:"#DDD",
        zoom: 3,
        streetViewControl: false,
        scrollwheel: false,
        center: {lat: 41.850033, lng: -87.6500523}, // Default center
        mapTypeControl: true,
        mapTypeControlOptions: {
            style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
            mapTypeIds: [
                google.maps.MapTypeId.ROADMAP,
                google.maps.MapTypeId.SATELLITE
            ],
            position: google.maps.ControlPosition.TOP_RIGHT
        }
    });

    // Set CSS for the control border.
    var btnRecenter = document.createElement('button');
    btnRecenter.setAttribute("dragable", "false");
    btnRecenter.setAttribute("title", "Recenter");
    btnRecenter.setAttribute("aria-label", "Recenter");
    btnRecenter.setAttribute("type", "button");
    btnRecenter.setAttribute("id", "resetZoomBtn");
    btnRecenter.setAttribute("class", "gm-control-active");
    btnRecenter.style.background = 'none rgb(255, 255, 255)';
    btnRecenter.style.border = '0';
    btnRecenter.style.boxShadow = '0 1px 1px rgba(0,0,0,.3)';
    btnRecenter.style.margin = '10px';
    btnRecenter.style.padding = '0px';
    btnRecenter.style.position = 'absolute';
    btnRecenter.style.borderRadius = "2px";
    btnRecenter.style.boxShadow = "rgba(0, 0, 0, 0.3) 0px 1px 4px -1px";
    btnRecenter.style.width = "40px";
    btnRecenter.style.height = "40px";
    btnRecenter.style.color = "#666666";
    btnRecenter.style.fontSize = "24px";
    btnRecenter.innerHTML = '<i class="fa fa-dot-circle-o"></i>';

    coverageMap.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(btnRecenter);

    drawInitialShape();
    if (!shapeType) {
        drawingManager = new google.maps.drawing.DrawingManager({
            drawingControl: true,
            drawingControlOptions: {
                position: google.maps.ControlPosition.TOP_CENTER,
                drawingModes: [
                    google.maps.drawing.OverlayType.MARKER,
                    google.maps.drawing.OverlayType.RECTANGLE
                ]
            },
            rectangleOptions: {
                editable: true,
                draggable: true
            }
        });
        drawingManager.setMap(coverageMap);
        google.maps.event.addListener(drawingManager, 'rectanglecomplete', function (rectangle) {
            var coordinates = (rectangle.getBounds());
            processDrawing(coordinates, "rectangle");
            rectangle.addListener('bounds_changed', function () {
                var coordinates = (rectangle.getBounds());
                processDrawing(coordinates, "rectangle");
            });
        });
        google.maps.event.addListener(drawingManager, 'markercomplete', function (marker) {
            var coordinates = (marker.getPosition());
            // Set onClick event for recenter button
            processDrawing(coordinates, "marker");
        });
        google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
            allOverlays.push(e);
        });
    }
}

function drawMarkerOnTextChange(){
    var myLatLng = {lat: parseFloat($("#id_north").val()), lng: parseFloat($("#id_east").val())};
    // Delete previous drawings
    deleteAllShapes();
    deleteAllOverlays();
    // Bounds validation
    var badInput = false;
    // Lat
    if (myLatLng.lat > 90 || myLatLng.lat < -90) {
        $("#id_north").addClass("invalid-input");
        badInput = true;
    }
    else {
        $("#id_north").removeClass("invalid-input");
    }
    if (myLatLng.lng > 180 || myLatLng.lng < -180) {
        $("#id_east").addClass("invalid-input");
        badInput = true;
    }
    else {
        $("#id_east").removeClass("invalid-input");
    }
    if (badInput || isNaN(myLatLng.lat) || isNaN(myLatLng.lng)) {
        return;
    }
    // Define the rectangle and set its editable property to true.
    var marker = new google.maps.Marker({
        position: myLatLng,
        map: coverageMap
    });
    // Center map at new market
    coverageMap.setCenter(marker.getPosition());
    // Set onClick event for recenter button
    $("#coverageMap").on("click", "#resetZoomBtn", function () {
        coverageMap.setCenter(marker.getPosition());
    });
    allShapes.push(marker);
}

function drawRectangleOnTextChange(){
    var bounds = {
        north: parseFloat($("#id_northlimit").val()),
        south: parseFloat($("#id_southlimit").val()),
        east: parseFloat($("#id_eastlimit").val()),
        west: parseFloat($("#id_westlimit").val())
    };
    // Delete previous drawings
    deleteAllShapes();
    deleteAllOverlays();
    // Bounds validation
    var badInput = false;
    // North
    if (bounds.north > 90 || bounds.north < -90) {
        $("#id_northlimit").addClass("invalid-input");
        badInput = true;
    }
    else {
        $("#id_northlimit").removeClass("invalid-input");
    }
    // East
    if (bounds.east > 180 || bounds.east < -180) {
        badInput = true;
        $("#id_eastlimit").addClass("invalid-input");
    }
    else {
        $("#id_eastlimit").removeClass("invalid-input");
    }
    // South
    if (bounds.south < -90 || bounds.south > 90) {
        badInput = true;
        $("#id_southlimit").addClass("invalid-input");
    }
    else {
        $("#id_southlimit").removeClass("invalid-input");
    }
    // West
    if (bounds.west < -180 || bounds.west > 180) {
        badInput = true;
        $("#id_westlimit").addClass("invalid-input");
    }
    else {
        $("#id_westlimit").removeClass("invalid-input");
    }
    if (badInput || isNaN(bounds.north) || isNaN(bounds.south) || isNaN(bounds.east) || isNaN(bounds.west)) {
        return;
    }
    // Define the rectangle and set its editable property to true.
    var rectangle = new google.maps.Rectangle({
        bounds: bounds,
        editable: true,
        draggable: true
    });
    rectangle.setMap(coverageMap);
    rectangle.addListener('bounds_changed', function () {
        var coordinates = (rectangle.getBounds());
        processDrawing(coordinates, "rectangle");
    });
    zoomCoverageMap(bounds);
    $("#coverageMap").on("click", "#resetZoomBtn", function () {
        zoomCoverageMap(bounds);
    });
    allShapes.push(rectangle);
}

function processDrawing (coordinates, shape) {
    // Delete previous drawings
    if (allOverlays.length > 1){
        for (var i = 1; i < allOverlays.length; i++){
            allOverlays[i-1].overlay.setMap(null);
        }
        allOverlays.shift();
    }
    if (allOverlays.length > 0) {
        deleteAllShapes();
    }
    // Show save changes button
    $("#coverage-spatial").find(".btn-primary").not('#btn-update-resource-spatial-coverage').show();
    if (shape === "rectangle"){
        var $radioBox = $('input[type="radio"][value="box"]'); // id_type_1
        $radioBox.prop("checked", true);
        $("#div_id_north").hide();
        $("#div_id_east").hide();
        $("#div_id_elevation").hide();
        $("#div_id_northlimit").show();
        $("#div_id_eastlimit").show();
        $("#div_id_southlimit").show();
        $("#div_id_westlimit").show();
        $("#div_id_uplimit").show();
        $("#div_id_downlimit").show();
        var bounds = {
            north: parseFloat(coordinates.getNorthEast().lat()),
            south: parseFloat(coordinates.getSouthWest().lat()),
            east: parseFloat(coordinates.getNorthEast().lng()),
            west: parseFloat(coordinates.getSouthWest().lng())
        };
        // Update fields
        $("#id_northlimit").val(bounds.north.toFixed(4));
        $("#id_eastlimit").val(bounds.east.toFixed(4));
        $("#id_southlimit").val(bounds.south.toFixed(4));
        $("#id_westlimit").val(bounds.west.toFixed(4));
        // Remove red borders
        $("#id_northlimit").removeClass("invalid-input");
        $("#id_eastlimit").removeClass("invalid-input");
        $("#id_southlimit").removeClass("invalid-input");
        $("#id_westlimit").removeClass("invalid-input");
        $("#coverageMap").on("click", "#resetZoomBtn", function () {
            zoomCoverageMap(bounds);
        });
    }
    else {
        var $radioPoint = $('input[type="radio"][value="point"]'); // id_type_2
        $radioPoint.prop("checked", true);
        $("#div_id_north").show();
        $("#div_id_east").show();
        $("#div_id_elevation").show();
        $("#div_id_northlimit").hide();
        $("#div_id_eastlimit").hide();
        $("#div_id_southlimit").hide();
        $("#div_id_westlimit").hide();
        $("#div_id_uplimit").hide();
        $("#div_id_downlimit").hide();
        $("#id_east").val(coordinates.lng().toFixed(4));
        $("#id_north").val(coordinates.lat().toFixed(4));
        // Remove red borders
        $("#id_east").removeClass("invalid-input");
        $("#id_north").removeClass("invalid-input");
        $("#coverageMap").on("click", "#resetZoomBtn", function () {
            coverageMap.setCenter(coordinates);
        });
    }
}

function deleteAllOverlays() {
    for (var i = 0; i < allOverlays.length; i++) {
        allOverlays[i].overlay.setMap(null);
    }
    allOverlays = [];
}

function deleteAllShapes(){
    for (var i = 0; i < allShapes.length; i++) {
        allShapes[i].setMap(null);
    }
    allShapes = [];
}

function zoomCoverageMap(bounds) {
    coverageMap.fitBounds(bounds);
}<commit_msg>[#4220] restrict coverage map latitude<commit_after>/**
 * Created by Mauriel on 2/9/2016.
 */
// Map js
var coverageMap;
var allOverlays = [];
var allShapes = []; // Keeps track of shapes added by text change events
var drawingManager;

$(document).ready(function () {
    // Draw marker on text change
    $("#id_east").bind('input', drawMarkerOnTextChange);
    $("#id_north").bind('input', drawMarkerOnTextChange);

    // Draw rectangle on text change
    $("#id_northlimit").bind('input', drawRectangleOnTextChange);
    $("#id_eastlimit").bind('input', drawRectangleOnTextChange);
    $("#id_southlimit").bind('input', drawRectangleOnTextChange);
    $("#id_westlimit").bind('input', drawRectangleOnTextChange);

    var $radioPoint = $('input[type="radio"][value="point"]'); // id_type_2
    var $radioBox = $('input[type="radio"][value="box"]'); // id_type_1
    // Set initial coverage fields state
    if ($radioBox.is(':checked')) { //box type coverage
        $("#div_id_north").hide();
        $("#div_id_east").hide();
        $("#div_id_elevation").hide();
    }
    if ($radioPoint.is(':checked')) { // point type coverage
        $("#div_id_northlimit").hide();
        $("#div_id_eastlimit").hide();
        $("#div_id_southlimit").hide();
        $("#div_id_westlimit").hide();
        $("#div_id_uplimit").hide();
        $("#div_id_downlimit").hide();
    }

    if ($("#coverageMap").length) {
        google.maps.event.addDomListener(window, "load", initMap);
    }
});

function drawInitialShape() {
    // This field is populated if the page is in view mode
    var shapeType = $("#coverageMap")[0].getAttribute("data-shape-type");

    var resourceType = $("#resource-type").val();
    // Center the map
    if (shapeType || resourceType === "Time Series") {
        deleteAllShapes();
        if (shapeType == "point" || (resourceType === "Time Series" && spatial_coverage_type == "point")) {
            var myLatLng;
            if (shapeType == "point") {
                // resource view mode
                myLatLng = {
                    lat: parseFloat($("#cov_north").text()),
                    lng: parseFloat($("#cov_east").text())
                };
            }
            else {
                // time series resource in edit mode
                myLatLng = {
                    lat: parseFloat($("#id_north").val()),
                    lng: parseFloat($("#id_east").val())
                };
                if ($('#id_north').val()) {
                    $("#id_name").prop('readonly', false);
                }
            }

            if (!myLatLng.lat || !myLatLng.lng) {
                return;
            }
            // Define the rectangle and set its editable property to true.
            var marker = new google.maps.Marker({
                position: myLatLng,
                map: coverageMap
            });
            allShapes.push(marker);
            // Center map at new market
            coverageMap.setCenter(marker.getPosition());
            $("#coverageMap").on("click", "#resetZoomBtn", function () {
                coverageMap.setCenter(marker.getPosition());
            });
        }
        else if (shapeType == "box" || (resourceType === "Time Series" && spatial_coverage_type == "box")) {
            var bounds;
            if (shapeType == "box") {
                //resource view mode
                bounds = {
                    north: parseFloat($("#cov_northlimit").text()),
                    south: parseFloat($("#cov_southlimit").text()),
                    east: parseFloat($("#cov_eastlimit").text()),
                    west: parseFloat($("#cov_westlimit").text())
                };
            }
            else {
                // time series resource edit mode
                bounds = {
                    north: parseFloat($("#id_northlimit").val()),
                    south: parseFloat($("#id_southlimit").val()),
                    east: parseFloat($("#id_eastlimit").val()),
                    west: parseFloat($("#id_westlimit").val())
                };
                if ($('#id_northlimit').val()) {
                    $("#id_name").prop('readonly', false);
                }
            }

            if (bounds.north === null || bounds.south === null || bounds.east === null || bounds.west === null) {
                return;
            }
            // Define the rectangle and set its editable property to true.
            var rectangle = new google.maps.Rectangle({
                bounds: bounds,
                editable: false,
                draggable: false
            });
            rectangle.setMap(coverageMap);
            allShapes.push(rectangle);
            zoomCoverageMap(bounds);
             $("#coverageMap").on("click", "#resetZoomBtn", function () {
                zoomCoverageMap(bounds);
            });
        }
    }
    else {
        var $radioBox = $('input[type="radio"][value="box"]'); // id_type_1
        if ($radioBox.is(":checked")) {
            drawRectangleOnTextChange();
        }
        else {
            drawMarkerOnTextChange();
        }
    }
    $("#id-coverage-spatial input:radio").change(function () {
        if ($(this).val() == "point") {
            $("#div_id_north").show();
            $("#div_id_east").show();
            $("#div_id_elevation").show();
            $("#div_id_northlimit").hide();
            $("#div_id_eastlimit").hide();
            $("#div_id_southlimit").hide();
            $("#div_id_westlimit").hide();
            $("#div_id_uplimit").hide();
            $("#div_id_downlimit").hide();
            drawMarkerOnTextChange();
            drawingManager.setDrawingMode(google.maps.drawing.OverlayType.MARKER);
        }
        else {
            $("#div_id_north").hide();
            $("#div_id_east").hide();
            $("#div_id_elevation").hide();
            $("#div_id_northlimit").show();
            $("#div_id_eastlimit").show();
            $("#div_id_southlimit").show();
            $("#div_id_westlimit").show();
            $("#div_id_uplimit").show();
            $("#div_id_downlimit").show();
            drawRectangleOnTextChange();
            drawingManager.setDrawingMode(google.maps.drawing.OverlayType.RECTANGLE);
        }
        // Show save changes button
        $("#coverage-spatial").find(".btn-primary").show();
    });
    if (sessionStorage.signininfo) {
        $("#sign-in-info").text(sessionStorage.signininfo);
        $("#btn-select-irods-file").show();
    }
}

function initMap() {
    var shapeType;
    var resourceType;
    if ($("#coverageMap")[0]) {
        // data-shape-type is set to have a value only in resource view mode
        shapeType = $("#coverageMap")[0].getAttribute("data-shape-type");
        resourceType = $("#resource-type").val();
        if (resourceType === "Time Series"){
            // set to view mode
            shapeType = " ";
        }
    }

    coverageMap = new google.maps.Map(document.getElementById('coverageMap'), {
        color:"#DDD",
        zoom: 3,
        restriction: {
            latLngBounds: {
              north: 85,
              south: -85,
              east: 179.99999,
              west: -179.99999,
            },
          },
        minZoom: 1,
        streetViewControl: false,
        scrollwheel: false,
        center: {lat: 41.850033, lng: -87.6500523}, // Default center
        mapTypeControl: true,
        mapTypeControlOptions: {
            style: google.maps.MapTypeControlStyle.DROPDOWN_MENU,
            mapTypeIds: [
                google.maps.MapTypeId.ROADMAP,
                google.maps.MapTypeId.SATELLITE
            ],
            position: google.maps.ControlPosition.TOP_RIGHT
        }
    });

    // Set CSS for the control border.
    var btnRecenter = document.createElement('button');
    btnRecenter.setAttribute("dragable", "false");
    btnRecenter.setAttribute("title", "Recenter");
    btnRecenter.setAttribute("aria-label", "Recenter");
    btnRecenter.setAttribute("type", "button");
    btnRecenter.setAttribute("id", "resetZoomBtn");
    btnRecenter.setAttribute("class", "gm-control-active");
    btnRecenter.style.background = 'none rgb(255, 255, 255)';
    btnRecenter.style.border = '0';
    btnRecenter.style.boxShadow = '0 1px 1px rgba(0,0,0,.3)';
    btnRecenter.style.margin = '10px';
    btnRecenter.style.padding = '0px';
    btnRecenter.style.position = 'absolute';
    btnRecenter.style.borderRadius = "2px";
    btnRecenter.style.boxShadow = "rgba(0, 0, 0, 0.3) 0px 1px 4px -1px";
    btnRecenter.style.width = "40px";
    btnRecenter.style.height = "40px";
    btnRecenter.style.color = "#666666";
    btnRecenter.style.fontSize = "24px";
    btnRecenter.innerHTML = '<i class="fa fa-dot-circle-o"></i>';

    coverageMap.controls[google.maps.ControlPosition.RIGHT_BOTTOM].push(btnRecenter);

    drawInitialShape();
    if (!shapeType) {
        drawingManager = new google.maps.drawing.DrawingManager({
            drawingControl: true,
            drawingControlOptions: {
                position: google.maps.ControlPosition.TOP_CENTER,
                drawingModes: [
                    google.maps.drawing.OverlayType.MARKER,
                    google.maps.drawing.OverlayType.RECTANGLE
                ]
            },
            rectangleOptions: {
                editable: true,
                draggable: true
            }
        });
        drawingManager.setMap(coverageMap);
        google.maps.event.addListener(drawingManager, 'rectanglecomplete', function (rectangle) {
            var coordinates = (rectangle.getBounds());
            processDrawing(coordinates, "rectangle");
            rectangle.addListener('bounds_changed', function () {
                var coordinates = (rectangle.getBounds());
                processDrawing(coordinates, "rectangle");
            });
        });
        google.maps.event.addListener(drawingManager, 'markercomplete', function (marker) {
            var coordinates = (marker.getPosition());
            // Set onClick event for recenter button
            processDrawing(coordinates, "marker");
        });
        google.maps.event.addListener(drawingManager, 'overlaycomplete', function (e) {
            allOverlays.push(e);
        });
    }
}

function drawMarkerOnTextChange(){
    var myLatLng = {lat: parseFloat($("#id_north").val()), lng: parseFloat($("#id_east").val())};
    // Delete previous drawings
    deleteAllShapes();
    deleteAllOverlays();
    // Bounds validation
    var badInput = false;
    // Lat
    if (myLatLng.lat > 90 || myLatLng.lat < -90) {
        $("#id_north").addClass("invalid-input");
        badInput = true;
    }
    else {
        $("#id_north").removeClass("invalid-input");
    }
    if (myLatLng.lng > 180 || myLatLng.lng < -180) {
        $("#id_east").addClass("invalid-input");
        badInput = true;
    }
    else {
        $("#id_east").removeClass("invalid-input");
    }
    if (badInput || isNaN(myLatLng.lat) || isNaN(myLatLng.lng)) {
        return;
    }
    // Define the rectangle and set its editable property to true.
    var marker = new google.maps.Marker({
        position: myLatLng,
        map: coverageMap
    });
    // Center map at new market
    coverageMap.setCenter(marker.getPosition());
    // Set onClick event for recenter button
    $("#coverageMap").on("click", "#resetZoomBtn", function () {
        coverageMap.setCenter(marker.getPosition());
    });
    allShapes.push(marker);
}

function drawRectangleOnTextChange(){
    var bounds = {
        north: parseFloat($("#id_northlimit").val()),
        south: parseFloat($("#id_southlimit").val()),
        east: parseFloat($("#id_eastlimit").val()),
        west: parseFloat($("#id_westlimit").val())
    };
    // Delete previous drawings
    deleteAllShapes();
    deleteAllOverlays();
    // Bounds validation
    var badInput = false;
    // North
    if (bounds.north > 90 || bounds.north < -90) {
        $("#id_northlimit").addClass("invalid-input");
        badInput = true;
    }
    else {
        $("#id_northlimit").removeClass("invalid-input");
    }
    // East
    if (bounds.east > 180 || bounds.east < -180) {
        badInput = true;
        $("#id_eastlimit").addClass("invalid-input");
    }
    else {
        $("#id_eastlimit").removeClass("invalid-input");
    }
    // South
    if (bounds.south < -90 || bounds.south > 90) {
        badInput = true;
        $("#id_southlimit").addClass("invalid-input");
    }
    else {
        $("#id_southlimit").removeClass("invalid-input");
    }
    // West
    if (bounds.west < -180 || bounds.west > 180) {
        badInput = true;
        $("#id_westlimit").addClass("invalid-input");
    }
    else {
        $("#id_westlimit").removeClass("invalid-input");
    }
    if (badInput || isNaN(bounds.north) || isNaN(bounds.south) || isNaN(bounds.east) || isNaN(bounds.west)) {
        return;
    }
    // Define the rectangle and set its editable property to true.
    var rectangle = new google.maps.Rectangle({
        bounds: bounds,
        editable: true,
        draggable: true
    });
    rectangle.setMap(coverageMap);
    rectangle.addListener('bounds_changed', function () {
        var coordinates = (rectangle.getBounds());
        processDrawing(coordinates, "rectangle");
    });
    zoomCoverageMap(bounds);
    $("#coverageMap").on("click", "#resetZoomBtn", function () {
        zoomCoverageMap(bounds);
    });
    allShapes.push(rectangle);
}

function processDrawing (coordinates, shape) {
    // Delete previous drawings
    if (allOverlays.length > 1){
        for (var i = 1; i < allOverlays.length; i++){
            allOverlays[i-1].overlay.setMap(null);
        }
        allOverlays.shift();
    }
    if (allOverlays.length > 0) {
        deleteAllShapes();
    }
    // Show save changes button
    $("#coverage-spatial").find(".btn-primary").not('#btn-update-resource-spatial-coverage').show();
    if (shape === "rectangle"){
        var $radioBox = $('input[type="radio"][value="box"]'); // id_type_1
        $radioBox.prop("checked", true);
        $("#div_id_north").hide();
        $("#div_id_east").hide();
        $("#div_id_elevation").hide();
        $("#div_id_northlimit").show();
        $("#div_id_eastlimit").show();
        $("#div_id_southlimit").show();
        $("#div_id_westlimit").show();
        $("#div_id_uplimit").show();
        $("#div_id_downlimit").show();
        var bounds = {
            north: parseFloat(coordinates.getNorthEast().lat()),
            south: parseFloat(coordinates.getSouthWest().lat()),
            east: parseFloat(coordinates.getNorthEast().lng()),
            west: parseFloat(coordinates.getSouthWest().lng())
        };
        // Update fields
        $("#id_northlimit").val(bounds.north.toFixed(4));
        $("#id_eastlimit").val(bounds.east.toFixed(4));
        $("#id_southlimit").val(bounds.south.toFixed(4));
        $("#id_westlimit").val(bounds.west.toFixed(4));
        // Remove red borders
        $("#id_northlimit").removeClass("invalid-input");
        $("#id_eastlimit").removeClass("invalid-input");
        $("#id_southlimit").removeClass("invalid-input");
        $("#id_westlimit").removeClass("invalid-input");
        $("#coverageMap").on("click", "#resetZoomBtn", function () {
            zoomCoverageMap(bounds);
        });
    }
    else {
        var $radioPoint = $('input[type="radio"][value="point"]'); // id_type_2
        $radioPoint.prop("checked", true);
        $("#div_id_north").show();
        $("#div_id_east").show();
        $("#div_id_elevation").show();
        $("#div_id_northlimit").hide();
        $("#div_id_eastlimit").hide();
        $("#div_id_southlimit").hide();
        $("#div_id_westlimit").hide();
        $("#div_id_uplimit").hide();
        $("#div_id_downlimit").hide();
        $("#id_east").val(coordinates.lng().toFixed(4));
        $("#id_north").val(coordinates.lat().toFixed(4));
        // Remove red borders
        $("#id_east").removeClass("invalid-input");
        $("#id_north").removeClass("invalid-input");
        $("#coverageMap").on("click", "#resetZoomBtn", function () {
            coverageMap.setCenter(coordinates);
        });
    }
}

function deleteAllOverlays() {
    for (var i = 0; i < allOverlays.length; i++) {
        allOverlays[i].overlay.setMap(null);
    }
    allOverlays = [];
}

function deleteAllShapes(){
    for (var i = 0; i < allShapes.length; i++) {
        allShapes[i].setMap(null);
    }
    allShapes = [];
}

function zoomCoverageMap(bounds) {
    coverageMap.fitBounds(bounds);
}<|endoftext|>


<commit_before>// ==========================================================================
// Project:   SproutCore Costello - Property Observing Library
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

require('system/object');

// ........................................................................
// CHAIN OBSERVER
//

// This is a private class used by the observable mixin to support chained
// properties.

// ChainObservers are used to automatically monitor a property several
// layers deep.
// org.plan.name = SC._ChainObserver.create({
//    target: this, property: 'org',
//    next: SC._ChainObserver.create({
//      property: 'plan',
//      next: SC._ChainObserver.create({
//        property: 'name', func: myFunc
//      })
//    })
//  })
//
SC._ChainObserver = function(property) {
  this.property = property ;
} ;

// This is the primary entry point.  Configures the chain.
SC._ChainObserver.createChain = function(rootObject, path, target, method, context) {

  // First we create the chain.
  var parts = path.split('.'),
      root  = new SC._ChainObserver(parts[0]),
      tail  = root,
      len   = parts.length;

  for(var idx=1;idx<len;idx++) {
    tail = tail.next = new SC._ChainObserver(parts[idx]) ;
  }

  // Now root has the first observer and tail has the last one.
  // Feed the rootObject into the front to setup the chain...
  // do this BEFORE we set the target/method so they will not be triggered.
  root.objectDidChange(rootObject);

  // Finally, set the target/method on the tail so that future changes will
  // trigger.
  tail.target = target; tail.method = method ; tail.context = context ;

  // and return the root to save
  return root ;
};

SC._ChainObserver.prototype = {
  isChainObserver: true,

  // the object this instance is observing
  object: null,

  // the property on the object this link is observing.
  property: null,

  // if not null, this is the next link in the chain.  Whenever the
  // current property changes, the next observer will be notified.
  next: null,

  // if not null, this is the final target observer.
  target: null,

  // if not null, this is the final target method
  method: null,

  // invoked when the source object changes.  removes observer on old
  // object, sets up new observer, if needed.
  objectDidChange: function(newObject) {
    if (newObject === this.object) return; // nothing to do.

    // if an old object, remove observer on it.
    if (this.object && this.object.removeObserver) {
      this.object.removeObserver(this.property, this, this.propertyDidChange);
    }

    // if a new object, add observer on it...
    this.object = newObject ;
    if (this.object && this.object.addObserver) {
      this.object.addObserver(this.property, this, this.propertyDidChange);
    }

    // now, notify myself that my property value has probably changed.
    this.propertyDidChange() ;
  },

  // the observer method invoked when the observed property changes.
  propertyDidChange: function() {

    // get the new value
    var object = this.object ;
    var property = this.property ;
    var value = (object && object.get) ? object.get(property) : null ;

    // if we have a next object in the chain, notify it that its object
    // did change...
    if (this.next) this.next.objectDidChange(value) ;

    // if we have a target/method, call it.
    var target  = this.target,
        method  = this.method,
        context = this.context ;
    if (target && method) {
      var rev = object.propertyRevision ;
      if (context) {
        method.call(target, object, property, value, context, rev);
      } else {
        method.call(target, object, property, value, rev) ;
      }
    }
  },

  // teardown the chain...
  destroyChain: function() {

    // remove observer
    var obj = this.object ;
    if (obj && obj.removeObserver) {
      obj.removeObserver(this.property, this, this.propertyDidChange) ;
    }

    // destroy next item in chain
    if (this.next) this.next.destroyChain() ;

    // and clear left overs...
    this.next = this.target = this.method = this.object = this.context = null;
    return null ;
  }

} ;
<commit_msg>Fixes bug when chain observers cause a JavaScript error. See http://github.com/martoche/sproutcore-samples/tree/binding-bug for an example.<commit_after>// ==========================================================================
// Project:   SproutCore Costello - Property Observing Library
// Copyright: ©2006-2009 Sprout Systems, Inc. and contributors.
//            Portions ©2008-2009 Apple, Inc. All rights reserved.
// License:   Licened under MIT license (see license.js)
// ==========================================================================

require('system/object');

// ........................................................................
// CHAIN OBSERVER
//

// This is a private class used by the observable mixin to support chained
// properties.

// ChainObservers are used to automatically monitor a property several
// layers deep.
// org.plan.name = SC._ChainObserver.create({
//    target: this, property: 'org',
//    next: SC._ChainObserver.create({
//      property: 'plan',
//      next: SC._ChainObserver.create({
//        property: 'name', func: myFunc
//      })
//    })
//  })
//
SC._ChainObserver = function(property) {
  this.property = property ;
} ;

// This is the primary entry point.  Configures the chain.
SC._ChainObserver.createChain = function(rootObject, path, target, method, context) {

  // First we create the chain.
  var parts = path.split('.'),
      root  = new SC._ChainObserver(parts[0]),
      tail  = root,
      len   = parts.length;

  for(var idx=1;idx<len;idx++) {
    tail = tail.next = new SC._ChainObserver(parts[idx]) ;
  }

  // Now root has the first observer and tail has the last one.
  // Feed the rootObject into the front to setup the chain...
  // do this BEFORE we set the target/method so they will not be triggered.
  root.objectDidChange(rootObject);

  // Finally, set the target/method on the tail so that future changes will
  // trigger.
  tail.target = target; tail.method = method ; tail.context = context ;

  // and return the root to save
  return root ;
};

SC._ChainObserver.prototype = {
  isChainObserver: true,

  // the object this instance is observing
  object: null,

  // the property on the object this link is observing.
  property: null,

  // if not null, this is the next link in the chain.  Whenever the
  // current property changes, the next observer will be notified.
  next: null,

  // if not null, this is the final target observer.
  target: null,

  // if not null, this is the final target method
  method: null,

  // invoked when the source object changes.  removes observer on old
  // object, sets up new observer, if needed.
  objectDidChange: function(newObject) {
    if (newObject === this.object) return; // nothing to do.

    // if an old object, remove observer on it.
    if (this.object && this.object.removeObserver) {
      this.object.removeObserver(this.property, this, this.propertyDidChange);
    }

    // if a new object, add observer on it...
    this.object = newObject ;
    if (this.object && this.object.addObserver) {
      this.object.addObserver(this.property, this, this.propertyDidChange);
    }

    // now, notify myself that my property value has probably changed.
    this.propertyDidChange() ;
  },

  // the observer method invoked when the observed property changes.
  propertyDidChange: function() {

    // get the new value
    var object = this.object ;
    var property = this.property ;
    var value = (object && object.get) ? object.get(property) : null ;

    // if we have a next object in the chain, notify it that its object
    // did change...
    if (this.next) this.next.objectDidChange(value) ;

    // if we have a target/method, call it.
    var target  = this.target,
        method  = this.method,
        context = this.context ;
    if (object && target && method) {
      var rev = object.propertyRevision ;
      if (context) {
        method.call(target, object, property, value, context, rev);
      } else {
        method.call(target, object, property, value, rev) ;
      }
    }
  },

  // teardown the chain...
  destroyChain: function() {

    // remove observer
    var obj = this.object ;
    if (obj && obj.removeObserver) {
      obj.removeObserver(this.property, this, this.propertyDidChange) ;
    }

    // destroy next item in chain
    if (this.next) this.next.destroyChain() ;

    // and clear left overs...
    this.next = this.target = this.method = this.object = this.context = null;
    return null ;
  }

} ;
<|endoftext|>


<commit_before>export default {
  name: "extend-post",

  initialize: function (container) {

    Discourse.View.reopen({
      didInsertElement: function() {
        this._super();
        if(this.element && this.element.id == "topic-closing-info") {
          var topic = $('#topic');
          var topic_id = topic.data('topic-id');
          var group = Discourse.SiteSettings.group;

          $.ajax("/highlight_post", {
            type: 'GET',
            data: { topic_id: topic_id, group: group }
          }).done(function(res){
            if(res.highlight_post){
              $('.top-answer-link').parent().removeClass('hidden');
              $("article.boxed[data-post-id='"+ res.post_id +"']").addClass('most_liked_post');
            }
          });
        }
      }
    });
  }
};

<commit_msg>Fix deprecated Discourse.View<commit_after>export default {
  name: "extend-post",

  initialize: function (container) {

    Ember.View.reopen({
      didInsertElement: function() {
        this._super();
        if(this.element && this.element.id == "topic-closing-info") {
          var topic = $('#topic');
          var topic_id = topic.data('topic-id');
          var group = Discourse.SiteSettings.group;

          $.ajax("/highlight_post", {
            type: 'GET',
            data: { topic_id: topic_id, group: group }
          }).done(function(res){
            if(res.highlight_post){
              $('.top-answer-link').parent().removeClass('hidden');
              $("article.boxed[data-post-id='"+ res.post_id +"']").addClass('most_liked_post');
            }
          });
        }
      }
    });
  }
};

<|endoftext|>


<commit_before>import Flash from 'app/flash'
import rpc from 'app/rpc'
import {set_modal} from 'app/utils/store'
import {i18n} from 'app/utils/i18n'

export function trigger_action(action, service){
  if (action.extra.call){
    const params = service.config

    let missing_params = action.extra.call.params.filter((p) => !(p.name in params))
    if (missing_params.length==0){
      rpc.call("action.trigger",
        [action.id, params]).then(function(){
        })
    }
    else{
      set_modal("service.action",{ action, params, missing_params })
    }
  }
  else if (action.extra.screen){
    this.context.router.push({
      pathname: `/s/${action.id}`,
      state: { service: service }
    })
  }
  else {
    Flash.error(i18n("Dont know how to trigger this action"))
  }
}
<commit_msg>Fix action trigger with no params. Fix #130.<commit_after>import Flash from 'app/flash'
import rpc from 'app/rpc'
import {set_modal} from 'app/utils/store'
import {i18n} from 'app/utils/i18n'

export function trigger_action(action, service){
  if (action.extra.call){
    const params = service.config

    let missing_params = (action.extra.call.params || []).filter((p) => !(p.name in params))
    if (missing_params.length==0){
      rpc.call("action.trigger",
        [action.id, params]).then(function(){
        })
    }
    else{
      set_modal("service.action",{ action, params, missing_params })
    }
  }
  else if (action.extra.screen){
    this.context.router.push({
      pathname: `/s/${action.id}`,
      state: { service: service }
    })
  }
  else {
    Flash.error(i18n("Dont know how to trigger this action"))
  }
}
<|endoftext|>


<commit_before>function calculateFee(n, tx) {
    var estimatedGas = 21000;
    if (tx !== null) {
        estimatedGas = web3.eth.estimateGas(tx);
    }

    var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
    return web3.fromWei(web3.eth.gasPrice * gasMultiplicator * estimatedGas, "ether");
}

function calculateGasPrice(n) {
    var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
    return web3.eth.gasPrice * gasMultiplicator;
}

status.defineSubscription(
    "calculatedFee",
    {value: ["sliderValue"], tx: ["transaction"]},
    function (params) {
        return calculateFee(params.value, params.tx);
    }
);

function getFeeExplanation(n) {
    return I18n.t('send_explanation') + I18n.t('send_explanation_' + (n + 2));
}

status.defineSubscription(
    "feeExplanation",
    {value: ["sliderValue"]},
    function(params) {
        return getFeeExplanation(params.value);
    }
)

function amountParameterBox(params, context) {
    if (!params["bot-db"]) {
        params["bot-db"] = {};
    }

    var contactAddress;
    if (params["bot-db"]["public"] && params["bot-db"]["public"]["recipient"]) {
        contactAddress = params["bot-db"]["public"]["recipient"]["address"];
    } else {
        contactAddress = null;
    }

    var txData;
    var amount;
    try {
        amount = params.args[1];
        txData = {
            to: contactAddress,
            value: web3.toWei(amount) || 0
        };
    } catch (err) {
        amount = null;
        txData = {
            to: contactAddress,
            value: 0
        };
    }

    var sliderValue = params["bot-db"]["sliderValue"] || 0;

    status.setDefaultDb({
        transaction: txData,
        calculatedFee: calculateFee(sliderValue, txData),
        feeExplanation: getFeeExplanation(sliderValue),
        sliderValue: sliderValue
    });

    return {
        title: I18n.t('send_title'),
        showBack: true,
        markup: status.components.scrollView(
            {
                keyboardShouldPersistTaps: "always"
            },
            [status.components.view(
                {
                    flex: 1
                },
                [
                    status.components.text(
                        {
                            style: {
                                fontSize: 14,
                                color: "rgb(147, 155, 161)",
                                paddingTop: 12,
                                paddingLeft: 16,
                                paddingRight: 16,
                                paddingBottom: 20
                            }
                        },
                        I18n.t('send_specify_amount')
                    ),
                    status.components.touchable(
                        {
                            onPress: status.components.dispatch([status.events.FOCUS_INPUT, []])
                        },
                        status.components.view(
                            {
                                flexDirection: "row",
                                alignItems: "center",
                                textAlign: "center",
                                justifyContent: "center"
                            },
                            [
                                status.components.text(
                                    {
                                        font: "light",
                                        numberOfLines: 1,
                                        ellipsizeMode: "tail",
                                        style: {
                                            maxWidth: 250,
                                            fontSize: 38,
                                            marginLeft: 8,
                                            color: "black"
                                        }
                                    },
                                    amount || "0.00"
                                ),
                                status.components.text(
                                    {
                                        font: "light",
                                        style: {
                                            fontSize: 38,
                                            marginLeft: 8,
                                            color: "rgb(147, 155, 161)"
                                        }
                                    },
                                    I18n.t('eth')
                                ),
                            ]
                        )
                    ),
                    status.components.text(
                        {
                            style: {
                                fontSize: 14,
                                color: "rgb(147, 155, 161)",
                                paddingTop: 14,
                                paddingLeft: 16,
                                paddingRight: 16,
                                paddingBottom: 5
                            }
                        },
                        I18n.t('send_fee')
                    ),
                    status.components.view(
                        {
                            flexDirection: "row"
                        },
                        [
                            status.components.text(
                                {
                                    style: {
                                        fontSize: 17,
                                        color: "black",
                                        paddingLeft: 16
                                    }
                                },
                                [status.components.subscribe(["calculatedFee"])]
                            ),
                            status.components.text(
                                {
                                    style: {
                                        fontSize: 17,
                                        color: "rgb(147, 155, 161)",
                                        paddingLeft: 4,
                                        paddingRight: 4
                                    }
                                },
                                I18n.t('eth')
                            )
                        ]
                    ),
                    status.components.slider(
                        {
                            maximumValue: 2,
                            minimumValue: -2,
                            onSlidingComplete: status.components.dispatch(
                                [status.events.UPDATE_DB, "sliderValue"]
                            ),
                            step: 1,
                            style: {
                                marginLeft: 16,
                                marginRight: 16
                            }
                        }
                    ),
                    status.components.view(
                        {
                            flexDirection: "row"
                        },
                        [
                            status.components.text(
                                {
                                    style: {
                                        flex: 1,
                                        fontSize: 14,
                                        color: "rgb(147, 155, 161)",
                                        paddingLeft: 16,
                                        alignSelf: "flex-start"
                                    }
                                },
                                I18n.t('send_cheaper')
                            ),
                            status.components.text(
                                {
                                    style: {
                                        flex: 1,
                                        fontSize: 14,
                                        color: "rgb(147, 155, 161)",
                                        paddingRight: 16,
                                        alignSelf: "flex-end",
                                        textAlign: "right"
                                    }
                                },
                                I18n.t('send_faster')
                            )
                        ]
                    ),
                    status.components.text(
                        {
                            style: {
                                fontSize: 14,
                                color: "black",
                                paddingTop: 16,
                                paddingLeft: 16,
                                paddingRight: 16,
                                paddingBottom: 16,
                                lineHeight: 24
                            }
                        },
                        [status.components.subscribe(["feeExplanation"])]
                    )
                ]
            )]
        )
    };
}

var paramsSend = [
    {
        name: "recipient",
        type: status.types.TEXT,
        suggestions: function (params) {
            return {
                title: I18n.t('send_title'),
                markup: status.components.chooseContact(I18n.t('send_choose_recipient'), "recipient", 0)
            };
        }
    },
    {
        name: "amount",
        type: status.types.NUMBER,
        suggestions: amountParameterBox
    }
];

function validateSend(params, context) {
    if (!params["bot-db"]) {
        params["bot-db"] = {};
    }

    if (!params["bot-db"]["public"] || !params["bot-db"]["public"]["recipient"] || !params["bot-db"]["public"]["recipient"]["address"]) {
        return {
            markup: status.components.validationMessage(
                "Wrong address",
                "Recipient address must be specified"
            )
        };
    }

    if (!params["amount"]) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_amount_specified')
            )
        };
    }

    var amount = params["amount"].replace(",", ".");
    var amountSplitted = amount.split(".");
    if (amountSplitted.length === 2 && amountSplitted[1].length > 18) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_amount_is_too_small')
            )
        };
    }

    if (isNaN(parseFloat(params.amount.replace(",", ".")))) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_invalid_number')
            )
        };
    }

    try {
        var val = web3.toWei(amount, "ether");
        if (val <= 0) {
            throw new Error();
        }
    } catch (err) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_invalid_number')
            )
        };
    }

    var balance = web3.eth.getBalance(context.from);
    var fee = calculateFee(
        params["bot-db"]["sliderValue"],
        {
            to: params["bot-db"]["public"]["recipient"]["address"],
            value: val
        }
    );

    if (bn(val).plus(bn(web3.toWei(fee, "ether"))).greaterThan(bn(balance))) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_insufficient_amount')
                + web3.fromWei(balance, "ether")
                + " ETH)"
            )
        };
    }
}

function handleSend(params, context) {
    var val = web3.toWei(params["amount"].replace(",", "."), "ether");

    var data = {
        from: context.from,
        to: params["bot-db"]["public"]["recipient"]["address"],
        value: val,
        gasPrice: calculateGasPrice(params["bot-db"]["sliderValue"])
    };

    try {
        return web3.eth.sendTransaction(data);
    } catch (err) {
        return {error: err.message};
    }
}

function previewSend(params, context) {
    var amountStyle = {
        fontSize: 36,
        color: "#000000",
        height: 40
    };

    var amount = status.components.view(
        {
            flexDirection: "column",
            alignItems: "flex-end",
            maxWidth: 250
        },
        [status.components.text(
            {
                style: amountStyle,
                numberOfLines: 1,
                ellipsizeMode: "tail",
                font: "light"
            },
            status.localizeNumber(params.amount, context.delimiter, context.separator)
        )]);

    var currency = status.components.view(
        {
            style: {
                flexDirection: "column",
                justifyContent: "flex-end",
                paddingBottom: 0
            }
        },
        [status.components.text(
            {
                style: {
                    color: "#9199a0",
                    fontSize: 16,
                    lineHeight: 18,
                    marginLeft: 7.5
                }
            },
            I18n.t('eth')
        )]
    );

    var firstRow = status.components.view(
        {
            style: {
                flexDirection: "row",
                justifyContent: "space-between",
                marginTop: 8,
                marginBottom: 8
            }
        },
        [amount, currency]
    );

    var markup;
    if (params["bot-db"]
        && params["bot-db"]["public"]
        && params["bot-db"]["public"]["recipient"]
        && context["chat"]["group-chat"] === true) {
        var secondRow = status.components.text(
            {
                style: {
                    color: "#9199a0",
                    fontSize: 14,
                    lineHeight: 18
                }
            },
            I18n.t('send_sending_to') + " " + params["bot-db"]["public"]["recipient"]["name"]
        );
        markup = [firstRow, secondRow];
    } else {
        markup = [firstRow];
    }

    return {
        markup: status.components.view(
            {
                style: {
                    flexDirection: "column"
                }
            },
            markup
        )
    };
}

function shortPreviewSend(params, context) {
    return {
        markup: status.components.text(
            {},
            I18n.t('send_title') + ": "
            + status.localizeNumber(params.amount, context.delimiter, context.separator)
            + " ETH"
        )
    };
}

var send = {
    name: "send",
    icon: "money_white",
    color: "#5fc48d",
    title: I18n.t('send_title'),
    description: I18n.t('send_description'),
    params: paramsSend,
    validator: validateSend,
    handler: handleSend,
    preview: previewSend,
    shortPreview: shortPreviewSend
};

status.command(send);
status.response(send);

var paramsRequest = [
    {
        name: "recipient",
        type: status.types.TEXT,
        suggestions: function (params) {
            return {
                title: I18n.t('request_title'),
                markup: status.components.chooseContact(I18n.t('send_choose_recipient'), "recipient", 0)
            };
        }
    },
    {
        name: "amount",
        type: status.types.NUMBER
    }
];

status.command({
    name: "request",
    color: "#5fc48d",
    title: I18n.t('request_title'),
    description: I18n.t('request_description'),
    params: paramsRequest,
    handler: function (params, context) {
        var val = params["amount"].replace(",", ".");

        return {
            event: "request",
            request: {
                command: "send",
                params: {
                    recipient: context["current-account"]["name"],
                    amount: val
                },
                prefill: [context["current-account"]["name"], val],
                prefillBotDb: {
                    public: {
                        recipient: context["current-account"]
                    }
                }
            }
        };
    },
    preview: function (params, context) {
        var firstRow = status.components.text(
            {},
            I18n.t('request_requesting') + " "
            + status.localizeNumber(params.amount, context.delimiter, context.separator)
            + " ETH"
        );

        var markup;

        if (params["bot-db"]
            && params["bot-db"]["public"]
            && params["bot-db"]["public"]["recipient"]
            && context["chat"]["group-chat"] === true) {

            var secondRow = status.components.text(
                {
                    style: {
                        color: "#9199a0",
                        fontSize: 14,
                        lineHeight: 18
                    }
                },
                I18n.t('request_requesting_from') + " " + params["bot-db"]["public"]["recipient"]["name"]
            );
            markup = [firstRow, secondRow];
        } else {
            markup = [firstRow];
        }

        return {
            markup: status.components.view(
                {
                    style: {
                        flexDirection: "column"
                    }
                },
                markup
            )
        };
    },
    shortPreview: function (params, context) {
        return {
            markup: status.components.text(
                {},
                I18n.t('request_requesting') + " "
                + status.localizeNumber(params.amount, context.delimiter, context.separator)
                + " ETH"
            )
        };
    },
    validator: function (params) {
        if (!params["bot-db"]) {
            params["bot-db"] = {};
        }

        if (!params["bot-db"]["public"] || !params["bot-db"]["public"]["recipient"] || !params["bot-db"]["public"]["recipient"]["address"]) {
            return {
                markup: status.components.validationMessage(
                    "Wrong address",
                    "Recipient address must be specified"
                )
            };
        }
        if (!params["amount"]) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_amount_specified')
                )
            };
        }

        var amount = params.amount.replace(",", ".");
        var amountSplitted = amount.split(".");
        if (amountSplitted.length === 2 && amountSplitted[1].length > 18) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_amount_is_too_small')
                )
            };
        }

        if (isNaN(parseFloat(params.amount.replace(",", ".")))) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_invalid_number')
                )
            };
        }

        try {
            var val = web3.toWei(amount, "ether");
            if (val <= 0) {
                throw new Error();
            }
        } catch (err) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_invalid_number')
                )
            };
        }
    }
});
<commit_msg>wallet: always displays fee with fixed precision<commit_after>function calculateFee(n, tx) {
    var estimatedGas = 21000;
    if (tx !== null) {
        estimatedGas = web3.eth.estimateGas(tx);
    }

    var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
    var weiFee = web3.eth.gasPrice * gasMultiplicator * estimatedGas;
    return parseFloat(web3.fromWei(weiFee, "ether")).toFixed(7);
}

function calculateGasPrice(n) {
    var gasMultiplicator = Math.pow(1.4, n).toFixed(3);
    return web3.eth.gasPrice * gasMultiplicator;
}

status.defineSubscription(
    "calculatedFee",
    {value: ["sliderValue"], tx: ["transaction"]},
    function (params) {
        return calculateFee(params.value, params.tx);
    }
);

function getFeeExplanation(n) {
    return I18n.t('send_explanation') + I18n.t('send_explanation_' + (n + 2));
}

status.defineSubscription(
    "feeExplanation",
    {value: ["sliderValue"]},
    function(params) {
        return getFeeExplanation(params.value);
    }
)

function amountParameterBox(params, context) {
    if (!params["bot-db"]) {
        params["bot-db"] = {};
    }

    var contactAddress;
    if (params["bot-db"]["public"] && params["bot-db"]["public"]["recipient"]) {
        contactAddress = params["bot-db"]["public"]["recipient"]["address"];
    } else {
        contactAddress = null;
    }

    var txData;
    var amount;
    try {
        amount = params.args[1];
        txData = {
            to: contactAddress,
            value: web3.toWei(amount) || 0
        };
    } catch (err) {
        amount = null;
        txData = {
            to: contactAddress,
            value: 0
        };
    }

    var sliderValue = params["bot-db"]["sliderValue"] || 0;

    status.setDefaultDb({
        transaction: txData,
        calculatedFee: calculateFee(sliderValue, txData),
        feeExplanation: getFeeExplanation(sliderValue),
        sliderValue: sliderValue
    });

    return {
        title: I18n.t('send_title'),
        showBack: true,
        markup: status.components.scrollView(
            {
                keyboardShouldPersistTaps: "always"
            },
            [status.components.view(
                {
                    flex: 1
                },
                [
                    status.components.text(
                        {
                            style: {
                                fontSize: 14,
                                color: "rgb(147, 155, 161)",
                                paddingTop: 12,
                                paddingLeft: 16,
                                paddingRight: 16,
                                paddingBottom: 20
                            }
                        },
                        I18n.t('send_specify_amount')
                    ),
                    status.components.touchable(
                        {
                            onPress: status.components.dispatch([status.events.FOCUS_INPUT, []])
                        },
                        status.components.view(
                            {
                                flexDirection: "row",
                                alignItems: "center",
                                textAlign: "center",
                                justifyContent: "center"
                            },
                            [
                                status.components.text(
                                    {
                                        font: "light",
                                        numberOfLines: 1,
                                        ellipsizeMode: "tail",
                                        style: {
                                            maxWidth: 250,
                                            fontSize: 38,
                                            marginLeft: 8,
                                            color: "black"
                                        }
                                    },
                                    amount || "0.00"
                                ),
                                status.components.text(
                                    {
                                        font: "light",
                                        style: {
                                            fontSize: 38,
                                            marginLeft: 8,
                                            color: "rgb(147, 155, 161)"
                                        }
                                    },
                                    I18n.t('eth')
                                ),
                            ]
                        )
                    ),
                    status.components.text(
                        {
                            style: {
                                fontSize: 14,
                                color: "rgb(147, 155, 161)",
                                paddingTop: 14,
                                paddingLeft: 16,
                                paddingRight: 16,
                                paddingBottom: 5
                            }
                        },
                        I18n.t('send_fee')
                    ),
                    status.components.view(
                        {
                            flexDirection: "row"
                        },
                        [
                            status.components.text(
                                {
                                    style: {
                                        fontSize: 17,
                                        color: "black",
                                        paddingLeft: 16
                                    }
                                },
                                [status.components.subscribe(["calculatedFee"])]
                            ),
                            status.components.text(
                                {
                                    style: {
                                        fontSize: 17,
                                        color: "rgb(147, 155, 161)",
                                        paddingLeft: 4,
                                        paddingRight: 4
                                    }
                                },
                                I18n.t('eth')
                            )
                        ]
                    ),
                    status.components.slider(
                        {
                            maximumValue: 2,
                            minimumValue: -2,
                            onSlidingComplete: status.components.dispatch(
                                [status.events.UPDATE_DB, "sliderValue"]
                            ),
                            step: 1,
                            style: {
                                marginLeft: 16,
                                marginRight: 16
                            }
                        }
                    ),
                    status.components.view(
                        {
                            flexDirection: "row"
                        },
                        [
                            status.components.text(
                                {
                                    style: {
                                        flex: 1,
                                        fontSize: 14,
                                        color: "rgb(147, 155, 161)",
                                        paddingLeft: 16,
                                        alignSelf: "flex-start"
                                    }
                                },
                                I18n.t('send_cheaper')
                            ),
                            status.components.text(
                                {
                                    style: {
                                        flex: 1,
                                        fontSize: 14,
                                        color: "rgb(147, 155, 161)",
                                        paddingRight: 16,
                                        alignSelf: "flex-end",
                                        textAlign: "right"
                                    }
                                },
                                I18n.t('send_faster')
                            )
                        ]
                    ),
                    status.components.text(
                        {
                            style: {
                                fontSize: 14,
                                color: "black",
                                paddingTop: 16,
                                paddingLeft: 16,
                                paddingRight: 16,
                                paddingBottom: 16,
                                lineHeight: 24
                            }
                        },
                        [status.components.subscribe(["feeExplanation"])]
                    )
                ]
            )]
        )
    };
}

var paramsSend = [
    {
        name: "recipient",
        type: status.types.TEXT,
        suggestions: function (params) {
            return {
                title: I18n.t('send_title'),
                markup: status.components.chooseContact(I18n.t('send_choose_recipient'), "recipient", 0)
            };
        }
    },
    {
        name: "amount",
        type: status.types.NUMBER,
        suggestions: amountParameterBox
    }
];

function validateSend(params, context) {
    if (!params["bot-db"]) {
        params["bot-db"] = {};
    }

    if (!params["bot-db"]["public"] || !params["bot-db"]["public"]["recipient"] || !params["bot-db"]["public"]["recipient"]["address"]) {
        return {
            markup: status.components.validationMessage(
                "Wrong address",
                "Recipient address must be specified"
            )
        };
    }

    if (!params["amount"]) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_amount_specified')
            )
        };
    }

    var amount = params["amount"].replace(",", ".");
    var amountSplitted = amount.split(".");
    if (amountSplitted.length === 2 && amountSplitted[1].length > 18) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_amount_is_too_small')
            )
        };
    }

    if (isNaN(parseFloat(params.amount.replace(",", ".")))) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_invalid_number')
            )
        };
    }

    try {
        var val = web3.toWei(amount, "ether");
        if (val <= 0) {
            throw new Error();
        }
    } catch (err) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_invalid_number')
            )
        };
    }

    var balance = web3.eth.getBalance(context.from);
    var fee = calculateFee(
        params["bot-db"]["sliderValue"],
        {
            to: params["bot-db"]["public"]["recipient"]["address"],
            value: val
        }
    );

    if (bn(val).plus(bn(web3.toWei(fee, "ether"))).greaterThan(bn(balance))) {
        return {
            markup: status.components.validationMessage(
                I18n.t('validation_title'),
                I18n.t('validation_insufficient_amount')
                + web3.fromWei(balance, "ether")
                + " ETH)"
            )
        };
    }
}

function handleSend(params, context) {
    var val = web3.toWei(params["amount"].replace(",", "."), "ether");

    var data = {
        from: context.from,
        to: params["bot-db"]["public"]["recipient"]["address"],
        value: val,
        gasPrice: calculateGasPrice(params["bot-db"]["sliderValue"])
    };

    try {
        return web3.eth.sendTransaction(data);
    } catch (err) {
        return {error: err.message};
    }
}

function previewSend(params, context) {
    var amountStyle = {
        fontSize: 36,
        color: "#000000",
        height: 40
    };

    var amount = status.components.view(
        {
            flexDirection: "column",
            alignItems: "flex-end",
            maxWidth: 250
        },
        [status.components.text(
            {
                style: amountStyle,
                numberOfLines: 1,
                ellipsizeMode: "tail",
                font: "light"
            },
            status.localizeNumber(params.amount, context.delimiter, context.separator)
        )]);

    var currency = status.components.view(
        {
            style: {
                flexDirection: "column",
                justifyContent: "flex-end",
                paddingBottom: 0
            }
        },
        [status.components.text(
            {
                style: {
                    color: "#9199a0",
                    fontSize: 16,
                    lineHeight: 18,
                    marginLeft: 7.5
                }
            },
            I18n.t('eth')
        )]
    );

    var firstRow = status.components.view(
        {
            style: {
                flexDirection: "row",
                justifyContent: "space-between",
                marginTop: 8,
                marginBottom: 8
            }
        },
        [amount, currency]
    );

    var markup;
    if (params["bot-db"]
        && params["bot-db"]["public"]
        && params["bot-db"]["public"]["recipient"]
        && context["chat"]["group-chat"] === true) {
        var secondRow = status.components.text(
            {
                style: {
                    color: "#9199a0",
                    fontSize: 14,
                    lineHeight: 18
                }
            },
            I18n.t('send_sending_to') + " " + params["bot-db"]["public"]["recipient"]["name"]
        );
        markup = [firstRow, secondRow];
    } else {
        markup = [firstRow];
    }

    return {
        markup: status.components.view(
            {
                style: {
                    flexDirection: "column"
                }
            },
            markup
        )
    };
}

function shortPreviewSend(params, context) {
    return {
        markup: status.components.text(
            {},
            I18n.t('send_title') + ": "
            + status.localizeNumber(params.amount, context.delimiter, context.separator)
            + " ETH"
        )
    };
}

var send = {
    name: "send",
    icon: "money_white",
    color: "#5fc48d",
    title: I18n.t('send_title'),
    description: I18n.t('send_description'),
    params: paramsSend,
    validator: validateSend,
    handler: handleSend,
    preview: previewSend,
    shortPreview: shortPreviewSend
};

status.command(send);
status.response(send);

var paramsRequest = [
    {
        name: "recipient",
        type: status.types.TEXT,
        suggestions: function (params) {
            return {
                title: I18n.t('request_title'),
                markup: status.components.chooseContact(I18n.t('send_choose_recipient'), "recipient", 0)
            };
        }
    },
    {
        name: "amount",
        type: status.types.NUMBER
    }
];

status.command({
    name: "request",
    color: "#5fc48d",
    title: I18n.t('request_title'),
    description: I18n.t('request_description'),
    params: paramsRequest,
    handler: function (params, context) {
        var val = params["amount"].replace(",", ".");

        return {
            event: "request",
            request: {
                command: "send",
                params: {
                    recipient: context["current-account"]["name"],
                    amount: val
                },
                prefill: [context["current-account"]["name"], val],
                prefillBotDb: {
                    public: {
                        recipient: context["current-account"]
                    }
                }
            }
        };
    },
    preview: function (params, context) {
        var firstRow = status.components.text(
            {},
            I18n.t('request_requesting') + " "
            + status.localizeNumber(params.amount, context.delimiter, context.separator)
            + " ETH"
        );

        var markup;

        if (params["bot-db"]
            && params["bot-db"]["public"]
            && params["bot-db"]["public"]["recipient"]
            && context["chat"]["group-chat"] === true) {

            var secondRow = status.components.text(
                {
                    style: {
                        color: "#9199a0",
                        fontSize: 14,
                        lineHeight: 18
                    }
                },
                I18n.t('request_requesting_from') + " " + params["bot-db"]["public"]["recipient"]["name"]
            );
            markup = [firstRow, secondRow];
        } else {
            markup = [firstRow];
        }

        return {
            markup: status.components.view(
                {
                    style: {
                        flexDirection: "column"
                    }
                },
                markup
            )
        };
    },
    shortPreview: function (params, context) {
        return {
            markup: status.components.text(
                {},
                I18n.t('request_requesting') + " "
                + status.localizeNumber(params.amount, context.delimiter, context.separator)
                + " ETH"
            )
        };
    },
    validator: function (params) {
        if (!params["bot-db"]) {
            params["bot-db"] = {};
        }

        if (!params["bot-db"]["public"] || !params["bot-db"]["public"]["recipient"] || !params["bot-db"]["public"]["recipient"]["address"]) {
            return {
                markup: status.components.validationMessage(
                    "Wrong address",
                    "Recipient address must be specified"
                )
            };
        }
        if (!params["amount"]) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_amount_specified')
                )
            };
        }

        var amount = params.amount.replace(",", ".");
        var amountSplitted = amount.split(".");
        if (amountSplitted.length === 2 && amountSplitted[1].length > 18) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_amount_is_too_small')
                )
            };
        }

        if (isNaN(parseFloat(params.amount.replace(",", ".")))) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_invalid_number')
                )
            };
        }

        try {
            var val = web3.toWei(amount, "ether");
            if (val <= 0) {
                throw new Error();
            }
        } catch (err) {
            return {
                markup: status.components.validationMessage(
                    I18n.t('validation_title'),
                    I18n.t('validation_invalid_number')
                )
            };
        }
    }
});
<|endoftext|>


<commit_before>import Ember from 'ember';
import assign from './utils/assign';
import descriptor from './utils/descriptor';

export default Ember.Helper.extend({

  compute(params, options) {
    this.options = options;
    // collect all of the actions from here up the prototype chain
    let actions = ancestorsOf(this).reduce(function(actions, ancestor) {
      return ancestor.actions ? assign({}, ancestor.actions, actions) : actions;
    }, {});

    if (!this._update) {
      this.value = this.initialValueFor(params, options);
    }
    delete this._update;

    return this.handlebarsValueFor(decorate(this, actions, this.prototypeFor(this.value), [this.value]), this.value);

    function decorate(microstate, actions, object, context) {
      return Object.create(object, Object.keys(actions).reduce((values, key)=> {
        return assign(values, {
          [key]: descriptor(valueFor(actions, key))
        });
      }, {}));

      function valueFor(actions, key) {
        let action = actions[key];
        if (typeof action === 'function') {
          return function(...args) {
            return microstate.transition(key, ()=> action.call(null, ...context, ...args));
          };
        } else {
          let next = object[key];
          if (next.map && next.length >=0) {
            return next.map(val => decorate(microstate, action, val, context.concat(val)));
          } else {
            return decorate(microstate, action, next, context.concat(next));
          }
        }
      }
    }
  },

  initialValueFor([value]) {
    return value;
  },

  handlebarsValueFor(value) {
    return value;
  },

  prototypeFor(value) {
    return value;
  },

  transition(eventName, updateFn = (current)=> current) {
    let nextState = updateFn.call(this, this.value);
    if (nextState !== this.value) {
      this.value = nextState;
      this._update = true;
      this.recompute();
      this.sendAction('state', nextState);
      this.sendAction(eventName, nextState);
    }
    return nextState;
  },

  sendAction(name, state) {
    let actionCallback = this.options[Ember.String.dasherize(`on-${name}`)];
    if (actionCallback && actionCallback.call) {
      actionCallback.call(null, state);
    }
  },

  actions: {
    set(current, value) {
      return value;
    }
  }
});

function ancestorsOf(object, ancestors = [object]) {
  let proto = Object.getPrototypeOf(object);
  if (proto == null) {
    return ancestors;
  } else {
    return ancestorsOf(proto, ancestors.concat(proto));
  }
}
<commit_msg>Added inline docs for value hooks<commit_after>import Ember from 'ember';
import assign from './utils/assign';
import descriptor from './utils/descriptor';

export default Ember.Helper.extend({

  compute(params, options) {
    this.options = options;
    // collect all of the actions from here up the prototype chain
    let actions = ancestorsOf(this).reduce(function(actions, ancestor) {
      return ancestor.actions ? assign({}, ancestor.actions, actions) : actions;
    }, {});

    if (!this._update) {
      this.value = this.initialValueFor(params, options);
    }
    delete this._update;

    return this.handlebarsValueFor(decorate(this, actions, this.prototypeFor(this.value), [this.value]), this.value);

    function decorate(microstate, actions, object, context) {
      return Object.create(object, Object.keys(actions).reduce((values, key)=> {
        return assign(values, {
          [key]: descriptor(valueFor(actions, key))
        });
      }, {}));

      function valueFor(actions, key) {
        let action = actions[key];
        if (typeof action === 'function') {
          return function(...args) {
            return microstate.transition(key, ()=> action.call(null, ...context, ...args));
          };
        } else {
          let next = object[key];
          if (next.map && next.length >=0) {
            return next.map(val => decorate(microstate, action, val, context.concat(val)));
          } else {
            return decorate(microstate, action, next, context.concat(next));
          }
        }
      }
    }
  },

  /**
   * initialValueFor hook is used to coerce params value into
   * type that the microstate expects. It's called when the helper
   * is computed the first time or consequently when incoming parameters
   * or arguments change.
   */
  initialValueFor([value]) {
    return value;
  },

  /**
   * prototypeFor hook provides the prototype for the object that
   * will be exposed to the template.
   */
  prototypeFor(value) {
    return value;
  },

  /**
   * handlebarsValueFor hooks is used to provide value that is formatted
   * to accomidate specificies of Handlebars.
   */
  handlebarsValueFor(value) {
    return value;
  },

  transition(eventName, updateFn = (current)=> current) {
    let nextState = updateFn.call(this, this.value);
    if (nextState !== this.value) {
      this.value = nextState;
      this._update = true;
      this.recompute();
      this.sendAction('state', nextState);
      this.sendAction(eventName, nextState);
    }
    return nextState;
  },

  sendAction(name, state) {
    let actionCallback = this.options[Ember.String.dasherize(`on-${name}`)];
    if (actionCallback && actionCallback.call) {
      actionCallback.call(null, state);
    }
  },

  actions: {
    set(current, value) {
      return value;
    }
  }
});

function ancestorsOf(object, ancestors = [object]) {
  let proto = Object.getPrototypeOf(object);
  if (proto == null) {
    return ancestors;
  } else {
    return ancestorsOf(proto, ancestors.concat(proto));
  }
}
<|endoftext|>


<commit_before>/**
 * @author Ayman Habib
 */
THREE.SkinnedMuscle = function(geom, points, material) {
    // Create bones for uuids in geometryPath
    this.pathpoints = points;
    this.pathpointObjects = [];
    geom.bones = [];

    for (var i=0; i< 2*points.length-2; i++) {
        var bone = new THREE.Bone();
        bone.pos = [0, 0, 0];
        bone.rotq = [0, 0, 0, 1];
        //bone.rotq = [0.70711, 0, 0, 0.70711]; //[0, 0, 0, 1];
        bone.ppt = this.pathpoints[Math.floor(i/2)];
        geom.bones.push(bone);
    }

    var numVerticesPerLevel = geom.vertices.length / (2*points.length-2);

    for ( var i = 0; i < geom.vertices.length; i++ ) {
        var skinIndex = Math.floor(i / numVerticesPerLevel);
        geom.skinIndices.push(new THREE.Vector4(skinIndex, 0, 0, 0));
        geom.skinWeights.push( new THREE.Vector4( 1, 0, 0, 0 ) );
    }
    geom.dynamic = true;
    THREE.SkinnedMesh.call( this, geom );
    this.material = material;
    this.material.skinning = true;
};

THREE.SkinnedMuscle.prototype = Object.create( THREE.SkinnedMesh.prototype );
THREE.SkinnedMuscle.prototype.constructor = THREE.SkinnedMuscle;

THREE.SkinnedMuscle.prototype.updateMatrixWorld = function( force ) {
// if has pathpoints attribute then it's a muscle
// Cycle through pathpoints, update their matrixworld
// then set the position of the Bones from that
    if (this.skeleton === undefined)
        return;
    var bones = this.skeleton.bones;
    //console.warn("Num bones in updateMatrixWorld: " + bones.length);

    if (this.pathpointObjects.length != this.pathpoints.length){
        var b = 0;
        for ( var p=0; p < this.pathpoints.length; p++) {
            var pptObject1 = editor.objectByUuid(this.pathpoints[p]);
            var pptObject2 = editor.objectByUuid(this.pathpoints[p+1]);

            if (pptObject1 !== undefined) {
               // add every pathpoint to the list of PathPoint objects
                this.pathpointObjects.push(pptObject1);

                if (pptObject2 !== undefined) {
                // define the two bones of a segement of the path together
                    bones[b].geometry = pptObject1.geometry;
                    bones[++b].geometry = pptObject2.geometry;
                }
                b++;
            }
        }
    }
    // Compute reverse transform from Ground to Scene (usually this's inverse translation)
    // This is necessary since the blending to compute vertices adds offset twice
    var mat = new THREE.Matrix4().getInverse(this.parent.matrixWorld);
    var vec = new THREE.Vector3().setFromMatrixPosition(mat);

    // Variables for the two points of a given path segement, the axis to
    // be rotated (from) and the vector between them (to)
    var pt1 = new THREE.Vector3();
    var pt2 = new THREE.Vector3();
    var vFrom = new THREE.Vector3(0, -1, 0);
    var vTo = new THREE.Vector3();

    // cycle through each segement defined by two PathPoints, pt1 and pt2
    // and alignge the bones (caps of each segment) to be alinged with
    // the vector connecting them.
    var b = 0; // bone (of SkinnedMuscle) index
    for (var p = 0; p < this.pathpoints.length-1; p++) {
        var thisPathpointObject = this.pathpointObjects[p];
        var nextPathpointObject = this.pathpointObjects[p+1];

        if(thisPathpointObject !== undefined) {
            pt1.setFromMatrixPosition(thisPathpointObject.matrixWorld);
            pt2.setFromMatrixPosition(nextPathpointObject.matrixWorld);

            vTo = pt2.clone();
            vTo.sub(pt1).normalize();

            // bones are positioned on the pathpoints
            bones[b].position.setFromMatrixPosition(thisPathpointObject.matrixWorld);
            bones[b].position.add(vec);
            // the orientation of the bone is updated to have its Y-axis pointed
            // back along the vector from pt1 to pt2
            bones[b].quaternion.setFromUnitVectors(vFrom, vTo);

            bones[++b].position.setFromMatrixPosition(nextPathpointObject.matrixWorld);
            // the orientation of the bone is updated to have its Y-axis pointed
            // back along the vector from pt1 to pt2
            bones[b].position.add(vec);
            bones[b++].quaternion.setFromUnitVectors(vFrom, vTo);
        }
    }
    this.skeleton.update();
    THREE.SkinnedMesh.prototype.updateMatrixWorld.call( this, true );
};
<commit_msg>Use skinWeightes on bone vertices to smoothen muscle paths.<commit_after>/**
 * @author Ayman Habib
 */
THREE.SkinnedMuscle = function(geom, points, material) {
    // Create bones for uuids in geometryPath
    this.pathpoints = points;
    this.pathpointObjects = [];
    geom.bones = [];

    for (var i=0; i< 2*points.length-2; i++) {
        var bone = new THREE.Bone();
        bone.pos = [0, 0, 0];
        bone.rotq = [0, 0, 0, 1];
        bone.ppt = this.pathpoints[Math.floor(i/2)];
        geom.bones.push(bone);
    }

    var numVerticesPerLevel = geom.vertices.length / (2*points.length-2);

    for ( var i = 0; i < geom.vertices.length; i++ ) {
        var skinIndex = Math.floor(i / numVerticesPerLevel);
        geom.skinIndices.push(new THREE.Vector4(skinIndex, skinIndex+1, skinIndex-1, 0));
        geom.skinWeights.push( new THREE.Vector4( 0.8, 0.1, 0.1, 0 ) );
    }
    geom.dynamic = true;
    THREE.SkinnedMesh.call( this, geom );
    this.material = material;
    this.material.skinning = true;
};

THREE.SkinnedMuscle.prototype = Object.create( THREE.SkinnedMesh.prototype );
THREE.SkinnedMuscle.prototype.constructor = THREE.SkinnedMuscle;

THREE.SkinnedMuscle.prototype.updateMatrixWorld = function( force ) {
// if has pathpoints attribute then it's a muscle
// Cycle through pathpoints, update their matrixworld
// then set the position of the Bones from that
    if (this.skeleton === undefined)
        return;
    var bones = this.skeleton.bones;
    //console.warn("Num bones in updateMatrixWorld: " + bones.length);

    if (this.pathpointObjects.length != this.pathpoints.length){
        var b = 0;
        for ( var p=0; p < this.pathpoints.length; p++) {
            var pptObject1 = editor.objectByUuid(this.pathpoints[p]);
            var pptObject2 = editor.objectByUuid(this.pathpoints[p+1]);

            if (pptObject1 !== undefined) {
               // add every pathpoint to the list of PathPoint objects
                this.pathpointObjects.push(pptObject1);

                if (pptObject2 !== undefined) {
                // define the two bones of a segement of the path together
                    bones[b].geometry = pptObject1.geometry;
                    bones[++b].geometry = pptObject2.geometry;
                }
                b++;
            }
        }
    }
    // Compute reverse transform from Ground to Scene (usually this's inverse translation)
    // This is necessary since the blending to compute vertices adds offset twice
    var mat = new THREE.Matrix4().getInverse(this.parent.matrixWorld);
    var vec = new THREE.Vector3().setFromMatrixPosition(mat);

    // Variables for the two points of a given path segement, the axis to
    // be rotated (from) and the vector between them (to)
    var pt1 = new THREE.Vector3();
    var pt2 = new THREE.Vector3();
    var vFrom = new THREE.Vector3(0, -1, 0);
    var vTo = new THREE.Vector3();

    // cycle through each segement defined by two PathPoints, pt1 and pt2
    // and alignge the bones (caps of each segment) to be alinged with
    // the vector connecting them.
    var b = 0; // bone (of SkinnedMuscle) index
    for (var p = 0; p < this.pathpoints.length-1; p++) {
        var thisPathpointObject = this.pathpointObjects[p];
        var nextPathpointObject = this.pathpointObjects[p+1];

        if(thisPathpointObject !== undefined) {
            pt1.setFromMatrixPosition(thisPathpointObject.matrixWorld);
            pt2.setFromMatrixPosition(nextPathpointObject.matrixWorld);

            vTo = pt2.clone();
            vTo.sub(pt1).normalize();

            // bones are positioned on the pathpoints
            bones[b].position.setFromMatrixPosition(thisPathpointObject.matrixWorld);
            bones[b].position.add(vec);
            // the orientation of the bone is updated to have its Y-axis pointed
            // back along the vector from pt1 to pt2
            bones[b].quaternion.setFromUnitVectors(vFrom, vTo);

            bones[++b].position.setFromMatrixPosition(nextPathpointObject.matrixWorld);
            // the orientation of the bone is updated to have its Y-axis pointed
            // back along the vector from pt1 to pt2
            bones[b].position.add(vec);
            bones[b++].quaternion.setFromUnitVectors(vFrom, vTo);
        }
    }
    this.skeleton.update();
    THREE.SkinnedMesh.prototype.updateMatrixWorld.call( this, true );
};
<|endoftext|>


<commit_before>// not yet ported. feel like it?<commit_msg>Remove register_shutdown_function as in experimental<commit_after><|endoftext|>