<commit_before>
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var activeBtnState = {};

// when one of the section buttons gets clicked
function setButtonActive(elem) {
    deactivateButtons();
    $(elem).addClass('active');
    removeTable();
    prepareTable();
}

// before activating a new button, we have to deactivate the last one
function deactivateButtons() {
    var buttons  = $('.sectionsButton');
    var id;
    $.each(buttons, function(){
        if($(this).hasClass("active")) {
            $(this).removeClass("active");
            return;
        }
    });
}

// this gets called as soon as the page loads
// and it populates the cars select box
function getUserCars() {
    var carNum = 0;
    $.ajax({type:"GET", url: "/api/v1/cars/"})
        .fail(function(resp){
            console.log('bad credentials.');
        })
        .done(function(resp){
            carNum = resp.count;
            var options = $('#userCars');
            $.each(resp.results, function() {
                options.append($("<option />").val(this.pk).text(this.manufacturer_name+" "+this.model_name));
            });
            if(carNum>1) {
                $('#carNum').text('My cars');
            } else {
                $('#carNum').text('My car');
            }

            $('#refuellingsButton').addClass('active');
            prepareTable();
        });
}

// check what section button has the active class
// generate table for that section
function prepareTable() {
    $('#tableRecords').css({'display': 'none'});
    var sectionBtn = $('.sectionsButton.active');
    var sectionID = $(sectionBtn).attr('id');
    var index = sectionID.indexOf("Button");
    var sectionName = sectionID.substring(0, index);
    sectionName = sectionName.toLowerCase();
    getTableColumns(sectionName);
}

// gets the records for the selected section
function getTableRecords(sectionName) {
     $.ajax({type:"GET", url: "/api/v1/"+sectionName+"/"})
        .fail(function(resp){
            console.log('bad credentials.');
        })
        .done(function(resp){
            activeBtnState.sectionName = sectionName;
            activeBtnState.numOfRecords = resp.count;
            activeBtnState.results = resp.results;
            addTableBody();
     });
}

// gets the columns for the selected section
function getTableColumns(sectionName) {
    var sectionSingular;
    if(sectionName === "taxes") {
        sectionSingular = sectionName.substring(0, sectionName.length-2);
    }
    else {
        sectionSingular = sectionName.substring(0, sectionName.length-1);
    }
    $.ajax({type:"GET", url: "/api/v1/columns/"+sectionSingular+"/"})
        .fail(function(resp){
            console.log('bad credentials.');
        })
        .done(function(resp){
            activeBtnState.numOfColumns = resp.length-2;
            activeBtnState.columns = resp;
            addTableHead();
            getTableRecords(sectionName);
    });
}

// this builds the table's head
function addTableHead() {
    $('#tableRecords thead').append($('<tr>'));
    var tableHead = $('#tableRecords thead').find('tr');
    for(column in activeBtnState.columns) {
        var columnName = getTableHeadName(activeBtnState.columns[column]);
        if(columnName === "Id" || columnName === "Car") {
            continue;
        }
        tableHead.append($('<th>').text(columnName));
    }
    tableHead.append($('<th>').css({'width': '70px'}));
}

// this builds the table's body
function addTableBody() {
    enableTheAddNewRecordBtn();
    if(activeBtnState.numOfRecords > 0){ // there are records for this section
         var noRecordsMsg = "";
         var carHasRecords = false;
         $.each(activeBtnState.results, function(index, elem) {
             var pk;
             var tableContainerWidth = $('#garageContent').width();
             $('#contentBody').append($('<tr>'));
             var newRow = $('#contentBody').find('tr').last();
             for (var key in elem) {
                 if(key==="pk") {
                    pk = elem[key];
                    //$(newRow).append($('<input>').attr("name", key).attr("id", "hiddenValue").val(pk));
                    continue;
                 }
                 if(key==="car") {
                      if(!isSelectedCar(elem[key])) { // this record belongs to another car
                          if(!carHasRecords && index === (activeBtnState.results.length-1)) {
                              // this combination of section and car doesn't have any records
                              noRecordsMsg = 'You have no '+activeBtnState.sectionName+' added for this car!';
                              setNoRecordsBody(newRow, noRecordsMsg);
                          }
                          return; // skip to the next record
                      }
                      carHasRecords = true;
                      continue;
                 }
                 $(newRow).append($('<td>')
                          .append($('<input>')
                               .prop('type', 'text')
                               .attr('name', key)
                               .val(elem[key])
                          ));
             }
             $(newRow).append($('<span>').attr('id', "hiddenValue").text(pk));
             addOperationsButtons(newRow);

             var tableCell = $('td');
             var leftPadding = tableCell.css('padding-left');
             var rightPadding = tableCell.css('padding-right');
             var widthPadding = parseInt(leftPadding.charAt(0)) + parseInt(rightPadding.charAt(0));
             var cellWidth = (tableContainerWidth-70)/activeBtnState.numOfColumns-(widthPadding+1);
             $(newRow).find('input').css({"width": cellWidth});
         });

        // here we remove the table row/rows added and not populated
        // because the corresponding record/records belong/s to another car (the user has more than one car)
        $('#contentBody tr').each(function(index, elem){
                if($(elem).children().length == 0) {
                    $(elem).remove();
                }
        });

         if(!noRecordsMsg) { // we have records for the selected section and car
            arrangeTableForMinHeight();
         }
    }
    else { // there are no records for this section
         $('#tableRecords thead tr').children().last().remove();
         $('#contentBody').append($('<tr>'));
         var newRow = $('#contentBody').find('tr').last();
         var message = 'You have no '+activeBtnState.sectionName+' added yet!';
         setNoRecordsBody(newRow, message);
    }

    var rows = $('#contentBody tr');
    rows.on('click', markSelectedRecord);

    $('#tableRecords').show();
    setAddNewRecordBtn();
    $("#addNewRecordBtn").prop('disabled', false);
}

// here we add an extra tbody that holds an 'empty space'
// the purpose is to make up for the difference between the min height of the table body (150px) and
// the height that the table would have with only 2-3 rows
function arrangeTableForMinHeight() {
    var contentBodyHeight = $("#contentBody tr").length * $('#contentBody tr').height();
    if(contentBodyHeight < 150){
        var emptySpace = 150 - contentBodyHeight;
        $("#emptyBody").append($('<tr>').attr('id', 'emptySpaceRow')
                            .append($('<td>').attr('colspan', activeBtnState.numOfColumns+1)
                                .append($('<div>')
                                    .attr('id', 'emptyDiv'))));
        $("#emptyDiv").height(emptySpace);
    }
}

// adds the button responsible for adding a new row
function setAddNewRecordBtn() {
    var tableHeight = $('#tableRecords').height();
    var marginTop = 15 + tableHeight + "px";
    $('#addNewRecordBtn').css({'top': marginTop, });
    $('#addNewRecordBtn').css({'visibility': 'visible'});
    $("#coverDiv").css({'top': marginTop});

}

function removeTheAddNewRecordBtn() {
    $('#addNewRecordBtn').css({'visibility': 'hidden'});
}

// this function is called when the user wants to add a new record to the table
function addNewRecord() {
    disableTheAddNewRecordBtn();

    $("#emptyBody").find("#emptySpaceRow").remove();
    $('#contentBody').append($('<tr>'));
    arrangeTableForMinHeight();
    removeTheAddNewRecordBtn();
    for (column in activeBtnState.columns) {
        if(activeBtnState.columns[column] == "id" || activeBtnState.columns[column] =="car") {continue;}
        $('#contentBody').find("tr").last().append($("<td>")
                                                .append($('<input>')
                                                .prop('type', 'text')
                                                .attr('name', activeBtnState.columns[column])));
    }

    $('#contentBody').find("tr").last().addClass("temporaryRow");
    addOperationsButtons($('.temporaryRow'));
    $('#contentBody').find("tr").last().on('click', markSelectedRecord);
    setAddNewRecordBtn();
}

function appendSaveRowImg(parentElement){
    $(parentElement).append($('<img>')
                    .attr("src", saveImgSrc)
                    .addClass("saveRowImg")
                    .click(saveRecord));
}

function replaceSaveRowImg(parentElement) {
    $(parentElement).find(".saveRowImg").remove();
    $( ".temporaryRow .deleteRowImg" ).before( $('<img>')
                        .attr("src", updateImgSrc)
                        .addClass("updateRowImg")
                        .click(updateRecord));
}

function appendUpdateRowImg(parentElement) {
    $(parentElement).append($('<img>')
                        .attr("src", updateImgSrc)
                        .addClass("updateRowImg")
                        .click(updateRecord));
}

function appendDeleteRowImg(parentElement) {
    $(parentElement).append($('<img>')
                    .attr("src", deleteImgSrc)
                    .addClass("deleteRowImg")
                    .click(deleteRecord));
}

// adds an additional column that holds the save and delete row buttons
function addOperationsButtons(newRow ) {
     $(newRow).append($('<td>')
            .css({'width': '70px'})
            .append($('<div>')
                .addClass('rowButtons')));
     if($(newRow).hasClass( "temporaryRow" )){
        appendSaveRowImg($(".rowButtons").last());
     } else {
        appendUpdateRowImg($(".rowButtons").last());
     }
     appendDeleteRowImg($(".rowButtons").last());
}

// set the csrf token before making ajax call
function ajaxSetup() {
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    });
}

// mark the record clicked by the user
function markSelectedRecord() {
    if($('#selectedRecord').length !== 0) {
        unmarkSelectedRecord();
    }

    $(this).attr('id', 'selectedRecord');
}

function unmarkSelectedRecord() {
     $("#selectedRecord").removeAttr("id");
}

function getSelectedRecordPk() {
    var pk = $("#selectedRecord").find("#hiddenValue").text();
    return pk;
}

// returns the input data contained by the selected row
function getSelectedRecordData() {
    var columns = $("#selectedRecord").find("td");
    var data = {};
    for ( var i = 0; i<columns.length-1; i++){
        var cell = columns[i];
        var cellChildren = $(cell).children();
        //console.log(cellChildren);
        var name = cellChildren.attr("name");
        var value = cellChildren.val();
        data[name] = value;
    }
    //console.log(data);
    return data;
}

function getSelectedCarURL() {
    var selectedCar = $('#userCars').val();
    var carUrl = "/api/v1/cars/" + selectedCar + "/";
    return carUrl;
}

function makeRowPermanent(objSaved) {
    $(".temporaryRow").append($('<span>').attr("id", "hiddenValue").text(objSaved.pk))
    replaceSaveRowImg($(".temporaryRow").last());
    $(".temporaryRow").removeClass("temporaryRow");
}

function disableTheAddNewRecordBtn() {
    $("#addNewRecordBtn").prop('disabled', true);
    $("#coverDiv").show();
}

function enableTheAddNewRecordBtn() {
     $("#coverDiv").hide();
     $("#addNewRecordBtn").prop('disabled', false);
}

function saveRecord() {
    console.log("in save record");
    $(this).closest("tr").attr("id", "selectedRecord"); // mark this row as selected
    var dataObj = getSelectedRecordData();
    var selectedCarUrl = getSelectedCarURL();
    dataObj["car"] = selectedCarUrl;

    ajaxSetup();
    $.ajax({
              method: "POST", // or PUT when updating
              url: "/api/v1/refuellings/",
              contentType : 'application/json',
              data: JSON.stringify(dataObj),
          })
          .done(function( objSaved ) {
              console.log( "Data Saved: " + JSON.stringify(objSaved));
              unmarkSelectedRecord();
              makeRowPermanent(objSaved);
              enableTheAddNewRecordBtn();
          });
}

function updateRecord() {
    console.log("in update");
    $(this).closest("tr").attr("id", "selectedRecord"); // mark this row as selected
    var pk = getSelectedRecordPk();
    var dataObj = getSelectedRecordData();
    var selectedCarUrl = getSelectedCarURL();
    dataObj["car"] = selectedCarUrl;
    console.log(dataObj);

    ajaxSetup();
    $.ajax({
              method: "PUT",
              contentType : 'application/json',
              url: "/api/v1/refuellings/"+pk+"/",
              data: JSON.stringify(dataObj),
          })
          .done(function( msg ) {
              console.log( "Data Saved: " + msg );
              unmarkSelectedRecord();
          });
}

function rearrangeTableAfterDeletingRecord() {
      $("#selectedRecord").remove();
      removeTheAddNewRecordBtn();
      $("#emptyBody").find("#emptySpaceRow").remove();
      arrangeTableForMinHeight();
      setAddNewRecordBtn();
}

function deleteRecord() {
    $(this).closest("tr").attr("id", "selectedRecord"); // mark this row as selected
    var pk = getSelectedRecordPk();
    ajaxSetup();
    $.ajax({
              method: "DELETE",
              url: "/api/v1/refuellings/"+pk+"/"
           })
          .done(function( msg ) {
              console.log( "Data Saved: " + msg );
              rearrangeTableAfterDeletingRecord();
          });
}

// this is called when the user has no records for a certain section and/or car
function setNoRecordsBody(newRow, message) {
    $(newRow).append($('<td>')
        .attr('colspan', activeBtnState.numOfColumns+1)
        .append($('<div>')
            .attr('id', 'noRecordsMessage')
            .text(message)));
}

// get table head columns names from the DB column names
function getTableHeadName(key) {
    var res = key.split("_");
    var name ="";
    for (var i=0; i<res.length; i++) {
        if(i === 0) {
            name += res[i].charAt(0).toUpperCase() + res[i].slice(1);
        } else {
            name += " " + res[i];
        }
    }
    return name;
}

// this checks if the car selected by the user matches the current record's car
function isSelectedCar(carUrl) {
    carUrl = String(carUrl);
    var carID = carUrl.charAt(carUrl.length-2);
    var selectedCar = $('#userCars').val();
    if(carID == selectedCar) {
        return true;
    }
    return false;
}

// this is called when the user selects a different car
function selectCarHandler() {
    removeTableRows();
    addTableBody();
}

function removeTableRows() {
    $('#tableRecords tbody').find('tr').remove();
}

function removeTable() {
    $('#tableRecords tbody').find('tr').remove();
    $('#tableRecords thead').find('tr').remove();
}<commit_msg>make ajax call when selecting the car too<commit_after>
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length + 1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}

var activeBtnState = {};

// when one of the section buttons gets clicked
function setButtonActive(elem) {
    deactivateButtons();
    $(elem).addClass('active');
    removeTable();
    prepareTable();
}

// before activating a new button, we have to deactivate the last one
function deactivateButtons() {
    var buttons  = $('.sectionsButton');
    var id;
    $.each(buttons, function(){
        if($(this).hasClass("active")) {
            $(this).removeClass("active");
            return;
        }
    });
}

// this gets called as soon as the page loads
// and it populates the cars select box
function getUserCars() {
    var carNum = 0;
    $.ajax({type:"GET", url: "/api/v1/cars/"})
        .fail(function(resp){
            console.log('bad credentials.');
        })
        .done(function(resp){
            carNum = resp.count;
            var options = $('#userCars');
            $.each(resp.results, function() {
                options.append($("<option />").val(this.pk).text(this.manufacturer_name+" "+this.model_name));
            });
            if(carNum>1) {
                $('#carNum').text('My cars');
            } else {
                $('#carNum').text('My car');
            }

            $('#refuellingsButton').addClass('active');
            prepareTable();
        });
}

// check what section button has the active class
// generate table for that section
function prepareTable() {
    $('#tableRecords').css({'display': 'none'});
    var sectionBtn = $('.sectionsButton.active');
    var sectionID = $(sectionBtn).attr('id');
    var index = sectionID.indexOf("Button");
    var sectionName = sectionID.substring(0, index);
    sectionName = sectionName.toLowerCase();
    getTableColumns(sectionName);
}

// gets the records for the selected section
function getTableRecords(sectionName) {
     $.ajax({type:"GET", url: "/api/v1/"+sectionName+"/"})
        .fail(function(resp){
            console.log('bad credentials.');
        })
        .done(function(resp){
            activeBtnState.sectionName = sectionName;
            activeBtnState.numOfRecords = resp.count;
            activeBtnState.results = resp.results;
            addTableBody();
     });
}

// gets the columns for the selected section
function getTableColumns(sectionName) {
    var sectionSingular;
    if(sectionName === "taxes") {
        sectionSingular = sectionName.substring(0, sectionName.length-2);
    }
    else {
        sectionSingular = sectionName.substring(0, sectionName.length-1);
    }
    $.ajax({type:"GET", url: "/api/v1/columns/"+sectionSingular+"/"})
        .fail(function(resp){
            console.log('bad credentials.');
        })
        .done(function(resp){
            activeBtnState.numOfColumns = resp.length-2;
            activeBtnState.columns = resp;
            addTableHead();
            getTableRecords(sectionName);
    });
}

// this builds the table's head
function addTableHead() {
    $('#tableRecords thead').append($('<tr>'));
    var tableHead = $('#tableRecords thead').find('tr');
    for(column in activeBtnState.columns) {
        var columnName = getTableHeadName(activeBtnState.columns[column]);
        if(columnName === "Id" || columnName === "Car") {
            continue;
        }
        $(tableHead).append($('<th>').text(columnName));
    }
    $(tableHead).append($('<th>').css({'width': '70px'})); // this is the column that holds the operations buttons
}

// this builds the table's body
function addTableBody() {
    enableTheAddNewRecordBtn();
    if(activeBtnState.numOfRecords > 0){ // there are records for this section
         var noRecordsMsg = "";
         var carHasRecords = false;
         $.each(activeBtnState.results, function(index, elem) {
             var pk;
             var tableContainerWidth = $('#garageContent').width();
             $('#contentBody').append($('<tr>'));
             var newRow = $('#contentBody').find('tr').last();
             for (var key in elem) {
                 if(key==="pk") {
                    pk = elem[key];
                    //$(newRow).append($('<input>').attr("name", key).attr("id", "hiddenValue").val(pk));
                    continue;
                 }
                 if(key==="car") {
                      if(!isSelectedCar(elem[key])) { // this record belongs to another car
                          if(!carHasRecords && index === (activeBtnState.results.length-1)) {
                              // this combination of section and car doesn't have any records
                              noRecordsMsg = 'You have no '+activeBtnState.sectionName+' added for this car!';
                              setNoRecordsBody(newRow, noRecordsMsg);
                          }
                          return; // skip to the next record
                      }
                      carHasRecords = true;
                      continue;
                 }
                 $(newRow).append($('<td>')
                          .append($('<input>')
                               .prop('type', 'text')
                               .attr('name', key)
                               .val(elem[key])
                          ));
             }
             $(newRow).append($('<span>').attr('id', "hiddenValue").text(pk));
             addOperationsButtons(newRow);

             var tableCell = $('td');
             var leftPadding = tableCell.css('padding-left');
             var rightPadding = tableCell.css('padding-right');
             var widthPadding = parseInt(leftPadding.charAt(0)) + parseInt(rightPadding.charAt(0));
             var cellWidth = (tableContainerWidth-70)/activeBtnState.numOfColumns-(widthPadding+1);
             $(newRow).find('input').css({"width": cellWidth});
         });

        // here we remove the table row/rows added and not populated
        // because the corresponding record/records belong/s to another car (the user has more than one car)
        $('#contentBody tr').each(function(index, elem){
                if($(elem).children().length == 0) {
                    $(elem).remove();
                }
        });

         if(!noRecordsMsg) { // we have records for the selected section and car
            arrangeTableForMinHeight();
         }
    }
    else { // there are no records for this section
         $('#contentBody').append($('<tr>'));
         var newRow = $('#contentBody').find('tr').last();
         var message = 'You have no '+activeBtnState.sectionName+' added yet!';
         setNoRecordsBody(newRow, message);
    }

    var rows = $('#contentBody tr');
    rows.on('click', markSelectedRecord);

    $('#tableRecords').show();
    setAddNewRecordBtn();
    $("#addNewRecordBtn").prop('disabled', false);
}

// here we add an extra tbody that holds an 'empty space'
// the purpose is to make up for the difference between the min height of the table body (150px) and
// the height that the table would have with only 2-3 rows
function arrangeTableForMinHeight() {
    var contentBodyHeight = $("#contentBody tr").length * $('#contentBody tr').height();
    if(contentBodyHeight < 150){
        var emptySpace = 150 - contentBodyHeight;
        $("#emptyBody").append($('<tr>').attr('id', 'emptySpaceRow')
                            .append($('<td>').attr('colspan', activeBtnState.numOfColumns+1)
                                .append($('<div>')
                                    .attr('id', 'emptyDiv'))));
        $("#emptyDiv").height(emptySpace);
    }
}

// adds the button responsible for adding a new row
function setAddNewRecordBtn() {
    var tableHeight = $('#tableRecords').height();
    var marginTop = 15 + tableHeight + "px";
    $('#addNewRecordBtn').css({'top': marginTop, });
    $('#addNewRecordBtn').css({'visibility': 'visible'});
    $("#coverDiv").css({'top': marginTop});

}

function removeTheAddNewRecordBtn() {
    $('#addNewRecordBtn').css({'visibility': 'hidden'});
}

// this function is called when the user wants to add a new record to the table
function addNewRecord() {
    disableTheAddNewRecordBtn();
    removeNoRecordsTD();

    $("#emptyBody").find("#emptySpaceRow").remove();
    $('#contentBody').append($('<tr>'));
    arrangeTableForMinHeight();
    removeTheAddNewRecordBtn();
    for (column in activeBtnState.columns) {
        if(activeBtnState.columns[column] == "id" || activeBtnState.columns[column] =="car") {continue;}
        $('#contentBody').find("tr").last().append($("<td>")
                                                .append($('<input>')
                                                .prop('type', 'text')
                                                .attr('name', activeBtnState.columns[column])));
    }

    $('#contentBody').find("tr").last().addClass("temporaryRow");
    addOperationsButtons($('.temporaryRow'));
    $('#contentBody').find("tr").last().on('click', markSelectedRecord);
    setAddNewRecordBtn();
}

function appendSaveRowImg(parentElement){
    $(parentElement).append($('<img>')
                    .attr("src", saveImgSrc)
                    .addClass("saveRowImg")
                    .click(saveRecord));
}

function replaceSaveRowImg(parentElement) {
    $(parentElement).find(".saveRowImg").remove();
    $( ".temporaryRow .deleteRowImg" ).before( $('<img>')
                        .attr("src", updateImgSrc)
                        .addClass("updateRowImg")
                        .click(updateRecord));
}

function appendUpdateRowImg(parentElement) {
    $(parentElement).append($('<img>')
                        .attr("src", updateImgSrc)
                        .addClass("updateRowImg")
                        .click(updateRecord));
}

function appendDeleteRowImg(parentElement) {
    $(parentElement).append($('<img>')
                    .attr("src", deleteImgSrc)
                    .addClass("deleteRowImg")
                    .click(deleteRecord));
}

// adds an additional column that holds the save and delete row buttons
function addOperationsButtons(newRow ) {
     $(newRow).append($('<td>')
            .css({'width': '70px'})
            .append($('<div>')
                .addClass('rowButtons')));
     if($(newRow).hasClass( "temporaryRow" )){
        appendSaveRowImg($(".rowButtons").last());
     } else {
        appendUpdateRowImg($(".rowButtons").last());
     }
     appendDeleteRowImg($(".rowButtons").last());
}

// set the csrf token before making ajax call
function ajaxSetup() {
    var csrftoken = getCookie('csrftoken');
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    });
}

// mark the record clicked by the user
function markSelectedRecord() {
    if($('#selectedRecord').length !== 0) {
        unmarkSelectedRecord();
    }

    $(this).attr('id', 'selectedRecord');
}

function unmarkSelectedRecord() {
     $("#selectedRecord").removeAttr("id");
}

function getSelectedRecordPk() {
    var pk = $("#selectedRecord").find("#hiddenValue").text();
    return pk;
}

// returns the input data contained by the selected row
function getSelectedRecordData() {
    var columns = $("#selectedRecord").find("td");
    var data = {};
    for ( var i = 0; i<columns.length-1; i++){
        var cell = columns[i];
        var cellChildren = $(cell).children();
        //console.log(cellChildren);
        var name = cellChildren.attr("name");
        var value = cellChildren.val();
        data[name] = value;
    }
    //console.log(data);
    return data;
}

function getSelectedCarURL() {
    var selectedCar = $('#userCars').val();
    var carUrl = "/api/v1/cars/" + selectedCar + "/";
    return carUrl;
}

function makeRowPermanent(objSaved) {
    $(".temporaryRow").append($('<span>').attr("id", "hiddenValue").text(objSaved.pk))
    replaceSaveRowImg($(".temporaryRow").last());
    $(".temporaryRow").removeClass("temporaryRow");
}

function disableTheAddNewRecordBtn() {
    $("#addNewRecordBtn").prop('disabled', true);
    $("#coverDiv").show();
}

function enableTheAddNewRecordBtn() {
     $("#coverDiv").hide();
     $("#addNewRecordBtn").prop('disabled', false);
}

function saveRecord() {
    console.log("in save record");
    $(this).closest("tr").attr("id", "selectedRecord"); // mark this row as selected
    var section = activeBtnState.sectionName;
    var dataObj = getSelectedRecordData();
    var selectedCarUrl = getSelectedCarURL();
    dataObj["car"] = selectedCarUrl;

    ajaxSetup();
    $.ajax({
              method: "POST", // or PUT when updating
              url: "/api/v1/" + section + "/",
              contentType : 'application/json',
              data: JSON.stringify(dataObj),
          })
          .done(function( objSaved ) {
              console.log( "Data Saved: " + JSON.stringify(objSaved));
              unmarkSelectedRecord();
              makeRowPermanent(objSaved);
              enableTheAddNewRecordBtn();
          });
}

function updateRecord() {
    console.log("in update");
    $(this).closest("tr").attr("id", "selectedRecord"); // mark this row as selected
    var section = activeBtnState.sectionName;
    var pk = getSelectedRecordPk();
    var dataObj = getSelectedRecordData();
    var selectedCarUrl = getSelectedCarURL();
    dataObj["car"] = selectedCarUrl;
    console.log(dataObj);

    ajaxSetup();
    $.ajax({
              method: "PUT",
              contentType : 'application/json',
              url: "/api/v1/" + section + "/" + pk + "/",
              data: JSON.stringify(dataObj),
          })
          .done(function( msg ) {
              console.log( "Data Saved: " + msg );
              unmarkSelectedRecord();
          });
}

function rearrangeTableAfterDeletingRecord() {
      $("#selectedRecord").remove();
      removeTheAddNewRecordBtn();
      $("#emptyBody").find("#emptySpaceRow").remove();
      arrangeTableForMinHeight();
      setAddNewRecordBtn();
}

function deleteRecord() {
    $(this).closest("tr").attr("id", "selectedRecord"); // mark this row as selected
    var section = activeBtnState.sectionName;
    var pk = getSelectedRecordPk();
    ajaxSetup();
    $.ajax({
              method: "DELETE",
              url: "/api/v1/" + section + "/" + pk + "/"
           })
          .done(function( msg ) {
              console.log( "Data Saved: " + msg );
              rearrangeTableAfterDeletingRecord();
          });
}

// this is called when the user has no records for a certain section and/or car
function setNoRecordsBody(newRow, message) {
    $(newRow).append($('<td>')
        .attr("id", "noRecordsTD")
        .attr('colspan', activeBtnState.numOfColumns+1)
        .append($('<div>')
            .attr('id', 'noRecordsMessage')
            .text(message)));
}

function removeNoRecordsTD() {
    $("#noRecordsTD").closest("tr").remove();
}

// get table head columns names from the DB column names
function getTableHeadName(key) {
    var res = key.split("_");
    var name ="";
    for (var i=0; i<res.length; i++) {
        if(i === 0) {
            name += res[i].charAt(0).toUpperCase() + res[i].slice(1);
        } else {
            name += " " + res[i];
        }
    }
    return name;
}

// this checks if the car selected by the user matches the current record's car
function isSelectedCar(carUrl) {
    carUrl = String(carUrl);
    var carID = carUrl.charAt(carUrl.length-2);
    var selectedCar = $('#userCars').val();
    if(carID == selectedCar) {
        return true;
    }
    return false;
}

// this is called when the user selects a different car
function selectCarHandler() {
    removeTable();
    prepareTable();
}

function removeTableRows() {
    $('#tableRecords tbody').find('tr').remove();
}

function removeTable() {
    $('#tableRecords tbody').find('tr').remove();
    $('#tableRecords thead').find('tr').remove();
}<|endoftext|>


<commit_before>/**
 * Ninja Blocks arduino controller
 */

var
	serialport = require('serialport')
	, stream = require('stream')
	, util = require('util')
	, path = require('path')
	, net = require('net')
	, fs = require('fs')
	, metaEvents = require('./lib/meta-events.js')
	, deviceStream = require('./lib/device-stream.js')
	, platformDevice = require('./lib/platform-device.js')
	, deviceHandlers = require('./lib/handlers.js')
	, configHandlers = require('./lib/config')
;

const kUtilBinPath = "/opt/utilities/bin/";
const kArduinoFlashScript = "ninja_upate_arduino";

const kArduinoParamsFile = "/etc/opt/ninja/.arduino_update_params";
const kArduinoUpdatedFile = "/etc/opt/ninja/.has_updated_arduino";

/**
 * platform.device = serial / net stream to device data (JSON stream)
 *
 */
function platform(opts, app, version) {

	var
		str = undefined
		, mod = this
	;

	//version to flash. Set by config.
	this.arduinoVersionToDownload = "V12"; //default to most common hardware

	this.retry = {

		delay : 3000
		, timer : null
		, count : 0
		, max : 3
	};

	stream.call(this);
	this.app = app;
	this.log = app.log;
	this.opts = opts || { };
	this.queue = [ ];
	this.device = undefined;
	this.channel = undefined;
	this.debounce = [ ];

	this.registeredDevices = [ ];

	this.statusLights = [

		{
			state : "client::up"
			, color : "00FF00"
		}
		, {
			state : "client::down"
			, color : "FFFF00"
		}
		, {
			state : "client::authed"
			, color : "00FFFF"
		}
		, {
			state : "client::activation"
			, color : "FF00FF"
		}
		, {
			state : "client::invalidToken"
			, color : "0000FF"
		}
		, {
			state : "client::reconnecting"
			, color : "00FFFF"
		}
		, {
			state : "client::updating"
			, color : "FFFFFF"
		}
	];

	if((!opts.devicePath) && opts.env == "production") {

		this.opts.devicePath = "/dev/ttyO1";
	}
	// don't bother if neither are specified
	if(!opts.devicePath && !opts.deviceHost) {

		return this.log.info("platform: No device specified");
	}
	else {

		if(!this.createStream()) {

			this.log.error("platform: Error creating device stream");
		}
	}

	/**
	 * Bind listeners for app state
	 * make the status LED do its thing
	 */
	this.statusLights.forEach(function(state) {

		app.on(state.state, function() {

			if(!mod.device) { return; }
			mod.device.write(JSON.stringify({
				DEVICE : [
					{
						G : "0"
						, V : 0
						, D : 999
						, DA : state.color
					}
				]
			}));
		});
	});

	/**
	 * Get version from arduino
	 */
	function getVersion() {

		if(!mod.device) { return; }
		mod.device.write('{"DEVICE":[{"G":"0","V":0,"D":1003,"DA":"VNO"}]}');
	};

	this.once('open', function() {

		var versionSpam = setInterval(function() {

			getVersion();
		}, 500);

		mod.once('version', function(ver) {

			version(ver);
			clearTimeout(versionSpam);
		});

		setTimeout(function() {

			clearTimeout(versionSpam);
		}, 2000);
	});

	//app.on('device::command', mod.onCommand.bind(mod));
	app.on('client::up', mod.restorePersistantDevices.bind(mod));

};

util.inherits(platform, stream);

deviceHandlers(platform);
deviceStream(platform);
metaEvents(platform);

platform.prototype.config = function(rpc,cb) {

  var self = this;

  if (!rpc) {
    return configHandlers.probe.call(this,cb);
  }

  switch (rpc.method) {
    case 'manual_board_version':   return configHandlers.manual_board_version.call(this,rpc.params,cb); break;
    case 'confirm_flash_arduino':  return configHandlers.confirm_flash_arduino.call(this,rpc.params,cb); break;
    case 'flashduino_begin':  return configHandlers.flashduino_begin.call(this,rpc.params,cb); break;
    default:               return cb(true);                                              break;
  }
};

platform.prototype.restorePersistantDevices = function() {
	var persistantDevices = this.opts.persistantDevices;

	if (!persistantDevices) {
		return;
	}

	var persistantGuid;
	persistantDevices.forEach(function(persistantGuid){

		deviceAttributes = persistantGuid.split('_');
		if (deviceAttributes.length < 3) {
			return;
		}

		this.registerDevice(deviceAttributes[0]
			, deviceAttributes[1]
			, deviceAttributes[2]
		);

	}.bind(this));

}

platform.prototype.setArduinoVersionToDownload = function(version) {
	this.arduinoVersionToDownload = version;
}

platform.prototype.flashArduino = function() {
	var params = '-f ' + this.arduinoVersionToDownload;
	this.log.info('platform: setting params, \'' + params + '\'');
	self = this;
    fs.writeFile(kArduinoParamsFile, params, function(err) { //write params to file
   		if(err) {
        	console.log(err);
    	} else {
			fs.unlink(kArduinoUpdatedFile, function (err) { //delete file to trigger update on next run
  			if (err) {
			} else {
				self.log.info('platform: flashing arduino...' + this.arduinoVersionToDownload);
				process.exit(); //restart so /etc/init/ninjablock.conf can run
			}
			});
		}
	});
}

function guid(device) {
	return [device.G,device.V,device.D].join('_');
}

platform.prototype.registerDevice = function(deviceG, deviceV, deviceD) {
	var device = new platformDevice(deviceG, deviceV, deviceD);
	device.write = function(DA) {
		this.onCommand.call(this,{
			G:device.G,
			V:device.V,
			D:device.D,
			DA:DA
		});
	}.bind(this);
	this.emit('register', device);
	this.registeredDevices[guid(device)] = device;
	return device;
}

platform.prototype.sendData = function(dat) {
	if(!dat) { return; }
	var device = this.registeredDevices[guid(dat)];
	if (!device) {
		device = this.registerDevice(dat.G, dat.V, dat.D);
	}
	device.emit('data',deviceObj.DA);
};

platform.prototype.sendConfig = function(type, dat) {

	if(!dat) { return; }
	dat.type = type;
	this.emit('config', dat);
};

platform.prototype.getJSON = function getJSON(data) {

	try {

		return JSON.parse(data);
	}
	catch(e) { }
	return null;
};

module.exports = platform;
<commit_msg>Fix reference to deviceObj<commit_after>/**
 * Ninja Blocks arduino controller
 */

var
	serialport = require('serialport')
	, stream = require('stream')
	, util = require('util')
	, path = require('path')
	, net = require('net')
	, fs = require('fs')
	, metaEvents = require('./lib/meta-events.js')
	, deviceStream = require('./lib/device-stream.js')
	, platformDevice = require('./lib/platform-device.js')
	, deviceHandlers = require('./lib/handlers.js')
	, configHandlers = require('./lib/config')
;

const kUtilBinPath = "/opt/utilities/bin/";
const kArduinoFlashScript = "ninja_upate_arduino";

const kArduinoParamsFile = "/etc/opt/ninja/.arduino_update_params";
const kArduinoUpdatedFile = "/etc/opt/ninja/.has_updated_arduino";

/**
 * platform.device = serial / net stream to device data (JSON stream)
 *
 */
function platform(opts, app, version) {

	var
		str = undefined
		, mod = this
	;

	//version to flash. Set by config.
	this.arduinoVersionToDownload = "V12"; //default to most common hardware

	this.retry = {

		delay : 3000
		, timer : null
		, count : 0
		, max : 3
	};

	stream.call(this);
	this.app = app;
	this.log = app.log;
	this.opts = opts || { };
	this.queue = [ ];
	this.device = undefined;
	this.channel = undefined;
	this.debounce = [ ];

	this.registeredDevices = [ ];

	this.statusLights = [

		{
			state : "client::up"
			, color : "00FF00"
		}
		, {
			state : "client::down"
			, color : "FFFF00"
		}
		, {
			state : "client::authed"
			, color : "00FFFF"
		}
		, {
			state : "client::activation"
			, color : "FF00FF"
		}
		, {
			state : "client::invalidToken"
			, color : "0000FF"
		}
		, {
			state : "client::reconnecting"
			, color : "00FFFF"
		}
		, {
			state : "client::updating"
			, color : "FFFFFF"
		}
	];

	if((!opts.devicePath) && opts.env == "production") {

		this.opts.devicePath = "/dev/ttyO1";
	}
	// don't bother if neither are specified
	if(!opts.devicePath && !opts.deviceHost) {

		return this.log.info("platform: No device specified");
	}
	else {

		if(!this.createStream()) {

			this.log.error("platform: Error creating device stream");
		}
	}

	/**
	 * Bind listeners for app state
	 * make the status LED do its thing
	 */
	this.statusLights.forEach(function(state) {

		app.on(state.state, function() {

			if(!mod.device) { return; }
			mod.device.write(JSON.stringify({
				DEVICE : [
					{
						G : "0"
						, V : 0
						, D : 999
						, DA : state.color
					}
				]
			}));
		});
	});

	/**
	 * Get version from arduino
	 */
	function getVersion() {

		if(!mod.device) { return; }
		mod.device.write('{"DEVICE":[{"G":"0","V":0,"D":1003,"DA":"VNO"}]}');
	};

	this.once('open', function() {

		var versionSpam = setInterval(function() {

			getVersion();
		}, 500);

		mod.once('version', function(ver) {

			version(ver);
			clearTimeout(versionSpam);
		});

		setTimeout(function() {

			clearTimeout(versionSpam);
		}, 2000);
	});

	//app.on('device::command', mod.onCommand.bind(mod));
	app.on('client::up', mod.restorePersistantDevices.bind(mod));

};

util.inherits(platform, stream);

deviceHandlers(platform);
deviceStream(platform);
metaEvents(platform);

platform.prototype.config = function(rpc,cb) {

  var self = this;

  if (!rpc) {
    return configHandlers.probe.call(this,cb);
  }

  switch (rpc.method) {
    case 'manual_board_version':   return configHandlers.manual_board_version.call(this,rpc.params,cb); break;
    case 'confirm_flash_arduino':  return configHandlers.confirm_flash_arduino.call(this,rpc.params,cb); break;
    case 'flashduino_begin':  return configHandlers.flashduino_begin.call(this,rpc.params,cb); break;
    default:               return cb(true);                                              break;
  }
};

platform.prototype.restorePersistantDevices = function() {
	var persistantDevices = this.opts.persistantDevices;

	if (!persistantDevices) {
		return;
	}

	var persistantGuid;
	persistantDevices.forEach(function(persistantGuid){

		deviceAttributes = persistantGuid.split('_');
		if (deviceAttributes.length < 3) {
			return;
		}

		this.registerDevice(deviceAttributes[0]
			, deviceAttributes[1]
			, deviceAttributes[2]
		);

	}.bind(this));

}

platform.prototype.setArduinoVersionToDownload = function(version) {
	this.arduinoVersionToDownload = version;
}

platform.prototype.flashArduino = function() {
	var params = '-f ' + this.arduinoVersionToDownload;
	this.log.info('platform: setting params, \'' + params + '\'');
	self = this;
    fs.writeFile(kArduinoParamsFile, params, function(err) { //write params to file
   		if(err) {
        	console.log(err);
    	} else {
			fs.unlink(kArduinoUpdatedFile, function (err) { //delete file to trigger update on next run
  			if (err) {
			} else {
				self.log.info('platform: flashing arduino...' + this.arduinoVersionToDownload);
				process.exit(); //restart so /etc/init/ninjablock.conf can run
			}
			});
		}
	});
}

function guid(device) {
	return [device.G,device.V,device.D].join('_');
}

platform.prototype.registerDevice = function(deviceG, deviceV, deviceD) {
	var device = new platformDevice(deviceG, deviceV, deviceD);
	device.write = function(DA) {
		this.onCommand.call(this,{
			G:device.G,
			V:device.V,
			D:device.D,
			DA:DA
		});
	}.bind(this);
	this.emit('register', device);
	this.registeredDevices[guid(device)] = device;
	return device;
}

platform.prototype.sendData = function(deviceObj) {
	if(!deviceObj) { return; }
	var device = this.registeredDevices[guid(deviceObj)];
	if (!device) {
		device = this.registerDevice(deviceObj.G, deviceObj.V, deviceObj.D);
	}
	device.emit('data',deviceObj.DA);
};

platform.prototype.sendConfig = function(type, dat) {

	if(!dat) { return; }
	dat.type = type;
	this.emit('config', dat);
};

platform.prototype.getJSON = function getJSON(data) {

	try {

		return JSON.parse(data);
	}
	catch(e) { }
	return null;
};

module.exports = platform;
<|endoftext|>


<commit_before>var _noCaptchaFields=_noCaptchaFields || [];

function noCaptchaFieldRender() {
    for(var i=0;i<_noCaptchaFields.length;i++) {
        var field=document.getElementById('Nocaptcha-'+_noCaptchaFields[i]);
        var options={'sitekey': field.getAttribute('data-sitekey'), 'theme': field.getAttribute('data-theme'), 'type': field.getAttribute('data-type')};

        grecaptcha.render(field, options);
    }
}<commit_msg>Added Callbck Function Check<commit_after>var _noCaptchaFields=_noCaptchaFields || [];

function noCaptchaFieldRender() {
    for(var i=0;i<_noCaptchaFields.length;i++) {
        var field=document.getElementById('Nocaptcha-'+_noCaptchaFields[i]);
        var options={
            'sitekey': field.getAttribute('data-sitekey'),
            'theme': field.getAttribute('data-theme'),
            'type': field.getAttribute('data-type'),
            'callback': (field.getAttribute('data-callback') ? verifyCallback : undefined )
        };

        grecaptcha.render(field, options);
    }
}
<|endoftext|>


<commit_before>window.serviceHub = 'http://labs.icesoft.com/bridgeit-services/service';

bridgeit.launchFailed = function(){
    document.getElementById('appStoreLink').href = bridgeit.appStoreLink();
    var popup = document.getElementById('getBridgeItPopup');
    popup.style.opacity = 0.95;
    popup.style.display = 'block';
    var vwidth = document.body.clientWidth;
    //center popup with 30px padding of body
    //popup.style.marginLeft = '' + ((vwidth*0.4)/2)-30 + 'px';
}
function closeGetBridgeItPopup(){
    var popup = document.getElementById('getBridgeItPopup');
    popup.style.opacity = 0;
    popup.style.display = 'none';
}
function ajaxGet(url,cb){
    var request;
    if( window.XMLHttpRequest){
        request = new XMLHttpRequest();
    }
    else if( window.ActiveXObject ){
        try{
            request = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch(e){
            try{
                request = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(e){}
        }
    }
    if( !request ){
        console.log("Unable to invoke XMLHttpRequest for " + url);
        return false;
    }
    request.onreadystatechange = function(){
        if (request.readyState === 4) {
            if (request.status === 200) {
                cb(request.responseText);
            } else {
                console.log("XMLHttpRequest for " + url +
                        " with status " + request.status);
            }
          }
    }
    request.open('GET', url);
    request.send();
}
function adjustContentHeight(){
    var vheight = document.body.clientHeight;
    if( navigator.userAgent.indexOf('iPhone') > -1 ){
        vheight += 62;
    }
    document.querySelectorAll('.ui-content')[0].style.minHeight = ''+( vheight - 120 ) + 'px';
}
window.addEventListener('resize', adjustContentHeight, false);
window.addEventListener('orientationchange', adjustContentHeight, false);

function simpleTime()  {
    var now = new Date();
    return now.getHours() + ":" +
    ("00" + now.getMinutes()).slice(-2) + ":" +
    ("00" + now.getSeconds()).slice(-2);
}
<commit_msg>add more markety blurb for notSupported condition<commit_after>window.serviceHub = 'http://labs.icesoft.com/bridgeit-services/service';

bridgeit.launchFailed = function(){
    document.getElementById('appStoreLink').href = bridgeit.appStoreLink();
    var popup = document.getElementById('getBridgeItPopup');
    popup.style.opacity = 0.95;
    popup.style.display = 'block';
    var vwidth = document.body.clientWidth;
    //center popup with 30px padding of body
    //popup.style.marginLeft = '' + ((vwidth*0.4)/2)-30 + 'px';
}
bridgeit.notSupported = function(id, command){
    if( !bridgeit.isIOS() && !bridgeit.isAndroid() && !bridgeit.isWindowsPhone8()){
        alert('Would you like to try the full mobile experience with BridgeIt? Grab an iOS, Android, or Windows 8 Phone device and try it out!');
    }
    else{
        alert('Sorry, the command ' + command + ' for BridgeIt is not supported on this platform');
    }
}
function closeGetBridgeItPopup(){
    var popup = document.getElementById('getBridgeItPopup');
    popup.style.opacity = 0;
    popup.style.display = 'none';
}
function ajaxGet(url,cb){
    var request;
    if( window.XMLHttpRequest){
        request = new XMLHttpRequest();
    }
    else if( window.ActiveXObject ){
        try{
            request = new ActiveXObject("Msxml2.XMLHTTP");
        }
        catch(e){
            try{
                request = new ActiveXObject("Microsoft.XMLHTTP");
            }
            catch(e){}
        }
    }
    if( !request ){
        console.log("Unable to invoke XMLHttpRequest for " + url);
        return false;
    }
    request.onreadystatechange = function(){
        if (request.readyState === 4) {
            if (request.status === 200) {
                cb(request.responseText);
            } else {
                console.log("XMLHttpRequest for " + url +
                        " with status " + request.status);
            }
          }
    }
    request.open('GET', url);
    request.send();
}
function adjustContentHeight(){
    var vheight = document.body.clientHeight;
    if( navigator.userAgent.indexOf('iPhone') > -1 ){
        vheight += 62;
    }
    document.querySelectorAll('.ui-content')[0].style.minHeight = ''+( vheight - 120 ) + 'px';
}
window.addEventListener('resize', adjustContentHeight, false);
window.addEventListener('orientationchange', adjustContentHeight, false);

function simpleTime()  {
    var now = new Date();
    return now.getHours() + ":" +
    ("00" + now.getMinutes()).slice(-2) + ":" +
    ("00" + now.getSeconds()).slice(-2);
}
<|endoftext|>


<commit_before>backupApp.controller("ExportController", function($scope, $routeParams, AppService, DialogService, gettextCatalog) {
    $scope.ExportType = "file";
    $scope.ExportPasswords = true;
    $scope.Connecting = false;
    $scope.BackupID = $routeParams.backupid;
    $scope.Completed = false;

    $scope.doExport = function() {
        // Helper function(s)
        function warnUnencryptedPasswords(continuation) {
            if ($scope.ExportType == "file" && $scope.ExportPasswords && !$scope.fileEncrypted) {
                DialogService.dialog(gettextCatalog.getString("Not using encryption"), gettextCatalog.getString("The configuration should be kept safe. Are you sure you want to save an unencrypted file containing your passwords?"), [gettextCatalog.getString("Cancel"), gettextCatalog.getString("Yes, I understand the risk")], function(ix) {
                    if (ix == 0) {
                        $scope.CurrentStep = 0;
                    } else {
                        continuation();
                    }
                });
            } else {
                continuation();
            }

        }

        // The actual export function to call after all checks pass
        function getExport() {
            if ($scope.ExportType == "commandline") {
                $scope.Connecting = true;
                AppService.get("/backup/" + $scope.BackupID + "/export?cmdline=true&export-passwords=" + encodeURIComponent($scope.ExportPasswords)).then(
                    function(resp) {
                        $scope.Connecting = false;
                        $scope.Completed = true;
                        $scope.CommandLine = resp.data.Command;
                    },
                    function(resp) {
                        $scope.Connecting = false;
                        var message = resp.statusText;
                        if (resp.data != null && resp.data.Message != null) {
                            message = resp.data.Message;
                        }

                        DialogService.dialog(gettextCatalog.getString("Error"), gettextCatalog.getString("Failed to connect: {{message}}", { message: message }));
                    }
                );
            } else {
                $scope.DownloadURL = AppService.get_export_url($scope.BackupID, $scope.UseEncryption ? $scope.Passphrase : null, $scope.ExportPasswords);
                $scope.Completed = true;
            }

        }

        // Make checks that do not require user input
        $scope.fileEncrypted = false;
        if ($scope.UseEncryption && $scope.ExportType == "file") {
            if (($scope.Passphrase || "").trim().length == 0) {
                DialogService.dialog(gettextCatalog.getString("No passphrase entered"), gettextCatalog.getString("To export without a passphrase, uncheck the \"Encrypt file\" box"));
                return;
            } else {
               $scope.fileEncrypted = true;
            }
        }

        // Chain various checks that require user input
        warnUnencryptedPasswords(function() { getExport(); });
    };
});
<commit_msg>update to use type-safe comparison<commit_after>backupApp.controller("ExportController", function($scope, $routeParams, AppService, DialogService, gettextCatalog) {
    $scope.ExportType = "file";
    $scope.ExportPasswords = true;
    $scope.Connecting = false;
    $scope.BackupID = $routeParams.backupid;
    $scope.Completed = false;

    $scope.doExport = function() {
        // Helper function(s)
        function warnUnencryptedPasswords(continuation) {
            if ($scope.ExportType === "file" && $scope.ExportPasswords && !$scope.fileEncrypted) {
                DialogService.dialog(gettextCatalog.getString("Not using encryption"), gettextCatalog.getString("The configuration should be kept safe. Are you sure you want to save an unencrypted file containing your passwords?"), [gettextCatalog.getString("Cancel"), gettextCatalog.getString("Yes, I understand the risk")], function(ix) {
                    if (ix === 0) {
                        $scope.CurrentStep = 0;
                    } else {
                        continuation();
                    }
                });
            } else {
                continuation();
            }

        }

        // The actual export function to call after all checks pass
        function getExport() {
            if ($scope.ExportType === "commandline") {
                $scope.Connecting = true;
                AppService.get("/backup/" + $scope.BackupID + "/export?cmdline=true&export-passwords=" + encodeURIComponent($scope.ExportPasswords)).then(
                    function(resp) {
                        $scope.Connecting = false;
                        $scope.Completed = true;
                        $scope.CommandLine = resp.data.Command;
                    },
                    function(resp) {
                        $scope.Connecting = false;
                        var message = resp.statusText;
                        if (resp.data != null && resp.data.Message != null) {
                            message = resp.data.Message;
                        }

                        DialogService.dialog(gettextCatalog.getString("Error"), gettextCatalog.getString("Failed to connect: {{message}}", { message: message }));
                    }
                );
            } else {
                $scope.DownloadURL = AppService.get_export_url($scope.BackupID, $scope.UseEncryption ? $scope.Passphrase : null, $scope.ExportPasswords);
                $scope.Completed = true;
            }

        }

        // Make checks that do not require user input
        $scope.fileEncrypted = false;
        if ($scope.UseEncryption && $scope.ExportType === "file") {
            if (($scope.Passphrase || "").trim().length === 0) {
                DialogService.dialog(gettextCatalog.getString("No passphrase entered"), gettextCatalog.getString("To export without a passphrase, uncheck the \"Encrypt file\" box"));
                return;
            } else {
               $scope.fileEncrypted = true;
            }
        }

        // Chain various checks that require user input
        warnUnencryptedPasswords(function() { getExport(); });
    };
});
<|endoftext|>


<commit_before>
// global object to store our functionality
ad = {}

// repo: record all ajax responses in a dictionary mapping paths to
// json objects.  the recording happens in the transformer; the
// recorded information is used when saving changes on the server.
ad.repo = {};

// lookup path in the meta data of obj.  lookup obj in ad.repo.  if
// obj has changed, add it to output list.  collect all attributes
// recursively that occur anywhere inside obj, lookup strings in
// ad.repo, and recurse wherever lookup yields a hit.  (this is not a
// very clean approach.  urls should have their own type.  strings
// that are not meant to be urls, but happen to are identical to a key
// in ad.repo should not yield hits.  but we haven't found a better
// way to do this yet.)
function collectChangedNodes(obj) {
    // FIXME: nyi

    return [];
}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
};
<commit_msg>frontend: added utils.deepCopy<commit_after>
// global object to store our functionality
ad = {}

// repo: record all ajax responses in a dictionary mapping paths to
// json objects.  the recording happens in the transformer; the
// recorded information is used when saving changes on the server.
ad.repo = {};

// lookup path in the meta data of obj.  lookup obj in ad.repo.  if
// obj has changed, add it to output list.  collect all attributes
// recursively that occur anywhere inside obj, lookup strings in
// ad.repo, and recurse wherever lookup yields a hit.  (this is not a
// very clean approach.  urls should have their own type.  strings
// that are not meant to be urls, but happen to are identical to a key
// in ad.repo should not yield hits.  but we haven't found a better
// way to do this yet.)
function collectChangedNodes(obj) {
    // FIXME: nyi

    return [];
}

function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
};

function deepCopy(obj) {
    return jQuery.extend(true, {}, obj);
};
<|endoftext|>


<commit_before>angular.module('private.logout', [
])
.config(function ($stateProvider) {
    $stateProvider
        .state('wegas.private.logout', {
            url: 'logout',
        	views: {
    		 	'workspace': {
                    controller: 'LogoutController as logoutCtrl',
                }
            }
        })
    ;
})
.controller('LogoutController', function LogoutController($state, Auth, SessionsModel, ScenariosModel, Flash) {
    console.log("Logout!");
    Auth.logout().then(function(response){
    	SessionsModel.clearCache();
    	ScenariosModel.clearCache();
    	Flash(response.level, response.message);
        $state.go("wegas.public.login");
    });

});<commit_msg>Edit flash message for logout<commit_after>angular.module('private.logout', [
])
.config(function ($stateProvider) {
    $stateProvider
        .state('wegas.private.logout', {
            url: 'logout',
        	views: {
    		 	'workspace': {
                    controller: 'LogoutController as logoutCtrl',
                }
            }
        })
    ;
})
.controller('LogoutController', function LogoutController($state, Auth, SessionsModel, ScenariosModel, Flash) {
    console.log("Logout!");
    Auth.logout().then(function(response){
    	SessionsModel.clearCache();
    	ScenariosModel.clearCache();
    	response.flash();
        $state.go("wegas.public.login");
    });

});<|endoftext|>


<commit_before>const fs = require('fs-extra')
const path = require('path')
const shortid = require('shortid')
const sharp = require('sharp')

const {
  getDesigners,
  updateDesignerFile,
  getProjects,
  updateProjectFile,
  getInfo,
  updateInfoFile,
} = require('./data')

module.exports = {
  processImages: main,
  getImageData
}

const imageDir = path.join(__dirname, '../static/images')

async function main () {
  await Promise.all([
    processDesigners(),
    processProjects(),
    processInfo(),
  ])
}

async function processDesigners () {
  const designers = await getDesigners()

  // process works of all designers
  const processedWorks = await Promise.all(designers.map(designer =>
    processDesignerWorks(designer)
  ))

  // update yaml files of all designers
  await Promise.all(designers.map((designer, index) =>
    updateDesigner(designer, processedWorks[index])
  ))
}

async function processProjects () {
  const projects = await getProjects()

  // process project images
  const processedProjectImages = await Promise.all(projects.map(project =>
    processProjectImages(project)
  ))

  // update yaml files of all designers
  await Promise.all(projects.map((project, index) =>
    updateProject(project, processedProjectImages[index])
  ))
}

async function processInfo () {
  const info = await getInfo()
  const processedImages = await processInfoImages(info)
  await updateInfo(info, processedImages)
}

async function processDesignerWorks (data) {
  const { works } = data
  return Promise.all(works.map(async (work) => {
    // find images that need processing
    const images = work.images || []
    const imagesToProcess = images.filter(image =>
      image.file && !image.file.includes('static/images/works')
    )

    // process them
    const processedImages = await Promise.all(imagesToProcess.map(image => {
      const newFilename = workImageFilename(data, work, image)
      return processImage(image, newFilename)
    }))

    // return relevant info
    return processedImages
      .filter(item => !!item)
      .map((item, i) => Object.assign({}, item, {
      originalImageIndex: images.indexOf(imagesToProcess[i])
    }))
  }))
}

async function processProjectImages (project) {
  // find images that need processing
  const images = project.images || []
  const imagesToProcess = images.filter(image =>
    image.file && !image.file.includes('static/images/projects')
  )

  // process them
  const processedImages = await Promise.all(imagesToProcess.map(image => {
    const newFilename = projectImageFilename(project, image)
    return processImage(image, newFilename)
  }))

  // return relevant info
  return processedImages
    .filter(item => !!item)
    .map((item, i) => Object.assign({}, item, {
    originalImageIndex: images.indexOf(imagesToProcess[i])
  }))
}

async function processInfoImages (info) {
  // find images that need processing
  const images = info.images || []
  const imagesToProcess = images.filter(image =>
    image.file && !image.file.includes('static/images/info/')
  )

  // process them
  const processedImages = await Promise.all(imagesToProcess.map(image => {
    const newFilename = infoImageFilename(image)
    return processImage(image, newFilename)
  }))

  // return relevant info
  return processedImages.map((item, i) => Object.assign({}, item, {
    originalImageIndex: images.indexOf(imagesToProcess[i])
  }))
}

async function updateDesigner (data, processedWorks) {
  const dataWorks = data.works || []
  const works = await Promise.all(dataWorks.map(async (work, index) => {
    const processedWorkItems = processedWorks[index]
    const workImages = work.images || []
    const images = await Promise.all(workImages.map(async (image, index) => {
      const processedWorkItem = processedWorkItems.find(item => item.originalImageIndex === index)
      return processedWorkItem ? getProcessedImageData(image, processedWorkItem) : image
    }))

    return Object.assign({}, work, { images })
  }))

  // write updated designer to same file
  const updatedDesigner = Object.assign({}, data, { works })
  await updateDesignerFile(updatedDesigner)
}

async function updateProject (project, processedImages) {
  if (!project.images || processedImages.length === 0) {
    return
  }

  const images = await Promise.all(project.images.map(async (image, index) => {
    const processedItem = processedImages.find(item => item.originalImageIndex === index)
    return processedItem ? getProcessedImageData(image, processedItem) : image
  }))

  // write updated project to same file
  const updatedProject = Object.assign({}, project, { images })
  await updateProjectFile(updatedProject)
}

async function updateInfo (info, processedImages) {
  if (processedImages.length === 0) {
    return
  }

  const images = await Promise.all(info.images.map(async (image, index) => {
    const processedItem = processedImages.find(item => item.originalImageIndex === index)
    return processedItem ? getProcessedImageData(image, processedItem) : image
  }))

  // write updated project to same file
  const updatedInfo = Object.assign({}, info, { images })
  await updateInfoFile(updatedInfo)
}

async function getProcessedImageData (image, processedImageItem) {
  const { newImage } = processedImageItem
  return Object.assign({}, image, {
    file: imageDataFilename(newImage)
  })
}

async function getImageData (filename) {
  const { width, height } = await sharp(filename).metadata()
  return {
    file: imageDataFilename(filename),
    width,
    height
  }
}

function imageDataFilename (filename) {
  const prefix = '/images/';
  return filename.substr(filename.indexOf(prefix))
}

function workImageFilename (designer, work, image) {
  const newDirectory = path.resolve(imageDir, 'works', designer.slug,  `${work.slug}-${shortid.generate()}`)
  const newFilename = path.join(newDirectory, `large${path.extname(image.file)}`)
  return newFilename
}

function projectImageFilename (project, image) {
  const newDirectory = path.resolve(imageDir, 'projects', project.slug, shortid.generate())
  const newFilename = path.join(newDirectory, `large${path.extname(image.file)}`)
  return newFilename
}

function infoImageFilename (image) {
  const newDirectory = path.resolve(imageDir, 'info', shortid.generate())
  const newFilename = path.join(newDirectory, `large${path.extname(image.file)}`)
  return newFilename
}

async function processImage (image, newFilename) {
  try {
    // move from root static folder to correct subdirectory, and rename
    const movedImage = await moveImage(image, newFilename)

    // convert other image formats to jpg
    const jpegImage = await makeImageJpeg(movedImage)

    // create necessary sizes
    const resizedImages = await resizeImage(jpegImage)

    // return relevant info
    return {
      newImage: jpegImage,
      resizedImages
    }
  } catch (err) {
    console.error('err processing file:', image.file)
    console.error(err)

    return null
  }
}

function moveImage (image, newFilename) {
  // move image to correct directory
  const oldFilename = path.join(imageDir, path.basename(image.file))
  return fs.move(oldFilename, newFilename)
    .then(() => newFilename)
}

function makeImageJpeg (imageFilename) {
  const ext = path.extname(imageFilename)
  if (ext === '.jpg') {
    return Promise.resolve(imageFilename)
  }

  let jpegFilename = path.basename(imageFilename, ext) + '.jpg'
  jpegFilename = path.join(path.dirname(imageFilename), jpegFilename)
  return sharp(imageFilename)
    .jpeg({ quality: 90 })
    .toFile(jpegFilename)
    .then(() =>
      fs.remove(imageFilename) // remove old non-jpeg file
        .catch() // ignore non-critical error
    )
    .then(() => jpegFilename)
}

async function resizeImage (jpegFilename) {
  let info
  try {
    info = await sharp(jpegFilename).metadata()
  } catch (err) {
    console.error('err reading image metadata: ', jpegFilename)
    console.error(err)
    return []
  }

  const { width, height } = info

  const sizes = []
  if (width >= height) {
    sizes.push({ width: 200 })
    sizes.push({ width: 400 })
    if (width > 800) sizes.push({ width: 800 })
    if (width > 1440) sizes.push({ width: 1440 })
  } else {
    sizes.push({ height: 300 })
    if (height > 600) sizes.push({ height: 600 })
    if (height > 900) sizes.push({ height: 900 })
    if (height > 1600) sizes.push({ height: 1600 })
  }

  return Promise.all(sizes.map(size => {
    const ext = path.extname(jpegFilename)
    let resizedFilename = `resized-${size.width ? `w${size.width}` : `h${size.height}`}${ext}`
    resizedFilename = path.join(path.dirname(jpegFilename), resizedFilename)

    return sharp(jpegFilename)
      .resize(size.width, size.height)
      .toFile(resizedFilename)
      .then(() => resizedFilename)
  }))
}
<commit_msg>bugfix: fixes bug where images are always resized<commit_after>const fs = require('fs-extra')
const path = require('path')
const shortid = require('shortid')
const sharp = require('sharp')

const {
  getDesigners,
  updateDesignerFile,
  getProjects,
  updateProjectFile,
  getInfo,
  updateInfoFile,
} = require('./data')

module.exports = {
  processImages: main,
  getImageData
}

const imageDir = path.join(__dirname, '../static/images')

async function main () {
  await Promise.all([
    processDesigners(),
    processProjects(),
    processInfo(),
  ])
}

async function processDesigners () {
  const designers = await getDesigners()

  // process works of all designers
  const processedWorks = await Promise.all(designers.map(designer =>
    processDesignerWorks(designer)
  ))

  // update yaml files of all designers
  await Promise.all(designers.map((designer, index) =>
    updateDesigner(designer, processedWorks[index])
  ))
}

async function processProjects () {
  const projects = await getProjects()

  // process project images
  const processedProjectImages = await Promise.all(projects.map(project =>
    processProjectImages(project)
  ))

  // update yaml files of all designers
  await Promise.all(projects.map((project, index) =>
    updateProject(project, processedProjectImages[index])
  ))
}

async function processInfo () {
  const info = await getInfo()
  const processedImages = await processInfoImages(info)
  await updateInfo(info, processedImages)
}

async function processDesignerWorks (data) {
  const { works } = data
  return Promise.all(works.map(async (work) => {
    // find images that need processing
    const images = work.images || []
    const imagesToProcess = images.filter(image =>
      image.file && !image.file.includes('images/works')
    )

    // process them
    const processedImages = await Promise.all(imagesToProcess.map(image => {
      const newFilename = workImageFilename(data, work, image)
      return processImage(image, newFilename)
    }))

    // return relevant info
    return processedImages
      .filter(item => !!item)
      .map((item, i) => Object.assign({}, item, {
      originalImageIndex: images.indexOf(imagesToProcess[i])
    }))
  }))
}

async function processProjectImages (project) {
  // find images that need processing
  const images = project.images || []
  const imagesToProcess = images.filter(image =>
    image.file && !image.file.includes('images/projects')
  )

  // process them
  const processedImages = await Promise.all(imagesToProcess.map(image => {
    const newFilename = projectImageFilename(project, image)
    return processImage(image, newFilename)
  }))

  // return relevant info
  return processedImages
    .filter(item => !!item)
    .map((item, i) => Object.assign({}, item, {
    originalImageIndex: images.indexOf(imagesToProcess[i])
  }))
}

async function processInfoImages (info) {
  // find images that need processing
  const images = info.images || []
  const imagesToProcess = images.filter(image =>
    image.file && !image.file.includes('images/info/')
  )

  // process them
  const processedImages = await Promise.all(imagesToProcess.map(image => {
    const newFilename = infoImageFilename(image)
    return processImage(image, newFilename)
  }))

  // return relevant info
  return processedImages.map((item, i) => Object.assign({}, item, {
    originalImageIndex: images.indexOf(imagesToProcess[i])
  }))
}

async function updateDesigner (data, processedWorks) {
  const dataWorks = data.works || []
  const works = await Promise.all(dataWorks.map(async (work, index) => {
    const processedWorkItems = processedWorks[index]
    const workImages = work.images || []
    const images = await Promise.all(workImages.map(async (image, index) => {
      const processedWorkItem = processedWorkItems.find(item => item.originalImageIndex === index)
      return processedWorkItem ? getProcessedImageData(image, processedWorkItem) : image
    }))

    return Object.assign({}, work, { images })
  }))

  // write updated designer to same file
  const updatedDesigner = Object.assign({}, data, { works })
  await updateDesignerFile(updatedDesigner)
}

async function updateProject (project, processedImages) {
  if (!project.images || processedImages.length === 0) {
    return
  }

  const images = await Promise.all(project.images.map(async (image, index) => {
    const processedItem = processedImages.find(item => item.originalImageIndex === index)
    return processedItem ? getProcessedImageData(image, processedItem) : image
  }))

  // write updated project to same file
  const updatedProject = Object.assign({}, project, { images })
  await updateProjectFile(updatedProject)
}

async function updateInfo (info, processedImages) {
  if (processedImages.length === 0) {
    return
  }

  const images = await Promise.all(info.images.map(async (image, index) => {
    const processedItem = processedImages.find(item => item.originalImageIndex === index)
    return processedItem ? getProcessedImageData(image, processedItem) : image
  }))

  // write updated project to same file
  const updatedInfo = Object.assign({}, info, { images })
  await updateInfoFile(updatedInfo)
}

async function getProcessedImageData (image, processedImageItem) {
  const { newImage } = processedImageItem
  return Object.assign({}, image, {
    file: imageDataFilename(newImage)
  })
}

async function getImageData (filename) {
  const { width, height } = await sharp(filename).metadata()
  return {
    file: imageDataFilename(filename),
    width,
    height
  }
}

function imageDataFilename (filename) {
  const prefix = '/images/';
  return filename.substr(filename.indexOf(prefix))
}

function workImageFilename (designer, work, image) {
  const newDirectory = path.resolve(imageDir, 'works', designer.slug,  `${work.slug}-${shortid.generate()}`)
  const newFilename = path.join(newDirectory, `large${path.extname(image.file)}`)
  return newFilename
}

function projectImageFilename (project, image) {
  const newDirectory = path.resolve(imageDir, 'projects', project.slug, shortid.generate())
  const newFilename = path.join(newDirectory, `large${path.extname(image.file)}`)
  return newFilename
}

function infoImageFilename (image) {
  const newDirectory = path.resolve(imageDir, 'info', shortid.generate())
  const newFilename = path.join(newDirectory, `large${path.extname(image.file)}`)
  return newFilename
}

async function processImage (image, newFilename) {
  try {
    // move from root static folder to correct subdirectory, and rename
    const movedImage = await moveImage(image, newFilename)

    // convert other image formats to jpg
    const jpegImage = await makeImageJpeg(movedImage)

    // create necessary sizes
    const resizedImages = await resizeImage(jpegImage)

    // return relevant info
    return {
      newImage: jpegImage,
      resizedImages
    }
  } catch (err) {
    console.error('err processing file:', image.file)
    console.error(err)

    return null
  }
}

function moveImage (image, newFilename) {
  // move image to correct directory
  const oldFilename = path.join(imageDir, path.basename(image.file))
  return fs.move(oldFilename, newFilename)
    .then(() => newFilename)
}

function makeImageJpeg (imageFilename) {
  const ext = path.extname(imageFilename)
  if (ext === '.jpg') {
    return Promise.resolve(imageFilename)
  }

  let jpegFilename = path.basename(imageFilename, ext) + '.jpg'
  jpegFilename = path.join(path.dirname(imageFilename), jpegFilename)
  return sharp(imageFilename)
    .jpeg({ quality: 90 })
    .toFile(jpegFilename)
    .then(() =>
      fs.remove(imageFilename) // remove old non-jpeg file
        .catch() // ignore non-critical error
    )
    .then(() => jpegFilename)
}

async function resizeImage (jpegFilename) {
  let info
  try {
    info = await sharp(jpegFilename).metadata()
  } catch (err) {
    console.error('err reading image metadata: ', jpegFilename)
    console.error(err)
    return []
  }

  const { width, height } = info

  const sizes = []
  if (width >= height) {
    sizes.push({ width: 200 })
    sizes.push({ width: 400 })
    if (width > 800) sizes.push({ width: 800 })
    if (width > 1440) sizes.push({ width: 1440 })
  } else {
    sizes.push({ height: 300 })
    if (height > 600) sizes.push({ height: 600 })
    if (height > 900) sizes.push({ height: 900 })
    if (height > 1600) sizes.push({ height: 1600 })
  }

  return Promise.all(sizes.map(size => {
    const ext = path.extname(jpegFilename)
    let resizedFilename = `resized-${size.width ? `w${size.width}` : `h${size.height}`}${ext}`
    resizedFilename = path.join(path.dirname(jpegFilename), resizedFilename)

    return sharp(jpegFilename)
      .resize(size.width, size.height)
      .toFile(resizedFilename)
      .then(() => resizedFilename)
  }))
}
<|endoftext|>


<commit_before>dojo.provide("sketchSpaceDesigner.designer.modes.Mode");

dojo.require("dojox.gfx.matrix");
dojo.require("dijit.layout.ContentPane");
dojo.require("sketchSpaceDesigner.designer.widgets");
dojo.require("sketchSpaceDesigner.designer.outline");

dojo.declare("sketchSpaceDesigner.designer.modes.Mode", [], {
  constructor: function () {
    this.inputState = {};
    this.inputState.keyboard = {};
    this.inputState.mouse = {};
    this.outlines = {};
  },
  enable: function () {
    var mode = this;
    this.designer.forEachObjectShape(function (shape) { mode.enableShape(shape); });
    this.onMouseWheelHandle = dojo.connect(this.designer.container, (!dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll"), function(e){
      var scroll = e[(!dojo.isMozilla ? "wheelDelta" : "detail")] * (!dojo.isMozilla ? 1 : -1);
      mode.onMouseWheel(e, scroll);
    });
    this.onKeyDownHandle = dojo.connect(document, "onkeydown", this, function (event) { mode.onKeyDown(event); });
    this.onKeyUpHandle = dojo.connect(document, "onkeyup", this, function (event) { mode.onKeyUp(event); });
    this.onMouseDownHandle = dojo.connect(this.designer.container, "onmousedown", this, this.onMouseDown);
    this.onMouseUpHandle = dojo.connect(this.designer.container, "onmouseup", this, this.onMouseUp);
    this.onMouseMoveHandle = dojo.connect(this.designer.container, "onmousemove", this, this.onMouseMove);
    this.onActivateHandle = dojo.connect(this.designer.container, "activate", this, this.onActivate);
    this.onContextMenuHandle = dojo.connect(this.designer.container, "contextmenu", this, this.onContextMenu);
    this.setOptionsHandle = dojo.connect(this.designer, "setOptions", this, this.onSetOptions);

/*
    this.shareCurrentImageOption = new sketchSpaceDesigner.designer.widgets.OptionCheckBox({title:"Shared image selection:", optionsPath:"shareCurrentImage", designer:this.designer});
    this.designer.ui.options.addChild(this.shareCurrentImageOption);

    this.showAuthorshipColorOption = new sketchSpaceDesigner.designer.widgets.OptionCheckBox({title:"Show authorship:", optionsPath:"showAuthorshipColors", designer:this.designer});
    this.designer.ui.options.addChild(this.showAuthorshipColorOption);
*/

    this.designer.ui.options.layout();
  },
  disable: function () {
    var mode = this;
    dojo.disconnect(this.setOptionsHandle);
    dojo.disconnect(this.onContextMenuHandle);
    dojo.disconnect(this.onActivateHandle);
    dojo.disconnect(this.onMouseDownHandle);
    dojo.disconnect(this.onMouseUpHandle);
    dojo.disconnect(this.onMouseMoveHandle);
    dojo.disconnect(this.onKeyUpHandle);
    dojo.disconnect(this.onKeyDownHandle);
    dojo.disconnect(this.onMouseWheelHandle);
    this.designer.forEachObjectShape(function (shape) { mode.disableShape(shape); });
/*
    this.shareCurrentImageOption.destroyRecursive();
    this.showAuthorshipColorOption.destroyRecursive();
*/
    this.designer.ui.options.layout();
    for (var name in this.outlines)
      this.disableOutline(name);
  },
  enableShape: function (shape) {
    var mode = this;
    shape.onMouseUpHandle = shape.connect("onmouseup", shape, function (event) { mode.onShapeMouseUp(shape, event); });
    shape.onMouseDownHandle = shape.connect("onmousedown", shape, function (event) { mode.onShapeMouseDown(shape, event); });
  },
  disableShape: function (shape) {
    dojo.disconnect(shape.onMouseUpHandle);
    dojo.disconnect(shape.onMouseDownHandle);
  },
  onActivate: function  (event) { dojo.stopEvent(event); },
  onContextMenu: function  (event) { dojo.stopEvent(event); },
  onSetOptions: function () {},
  onMouseWheel: function (event, scroll) {
  },
  onKeyDown: function (event) {
    this.inputState.keyboard[event.keyCode] = event;
  },
  onKeyUp: function (event) {
    console.log([event.keyCode, event]);
    if (event.keyCode == dojo.keys.CHAR_S) {
      this.designer.setOptions({doStroke: !this.designer.options.doStroke});
    } else if (event.keyCode == dojo.keys.CHAR_F) {
      this.designer.setOptions({doFill: !this.designer.options.doFill});
    } else if (event.keyCode == dojo.keys.ESCAPE) {
     // FIXME: Kind of ugly, but needed to set the corect mode icon in
     // the UI. Maybe change UI code to use dojo.connect for that?
     this.designer.ui._onSelect();
    }
    delete this.inputState.keyboard[event.keyCode];
  },
  onMouseDown: function(event) {
    this.inputState.mouse[event.button] = event;
  },
  onMouseUp: function(event) {
    delete this.inputState.mouse[event.button];
  },
  onMouseMove: function(event) {
    this.mouse = this.getCurrentGlobalMouse(event);
    this.updateCursorBboxOutline();
  },
  onShapeMouseDown: function (shape, event) {
  },
  onShapeMouseUp: function (shape, event) {
  },

  screenToLocalCoord: function (p, container) {
    if (container === undefined) container = this.getContainerShape();
    var screenToObjMatrix = dojox.gfx.matrix.invert(container._getRealMatrix());
    return dojox.gfx.matrix.multiplyPoint(screenToObjMatrix, p.x, p.y);
  },

  localCoordToScreen: function (p, container) {
    if (container === undefined) container = this.getContainerShape();
    var objToScreenMatrix = container._getRealMatrix();
    return dojox.gfx.matrix.multiplyPoint(objToScreenMatrix, p.x, p.y);
  },

  getCurrentGlobalMouse: function (event) {
    if (event.layerX !== undefined) {
      return {x:event.layerX, y:event.layerY};
    } else {
      /* All of this is for Opera, that Dojo does not seem to handle
       * correctly... Hopefully it doesn't break other browsers... */
      var cpos = $(this.designer.container).offset();
      var tpos = $(event.target).offset();
      console.log( event.offsetX );
      console.log( tpos.left );
      console.log( cpos.left );

      return {x: event.offsetX + tpos.left - cpos.left, y: event.offsetY + tpos.top - cpos.top};
    }
  },

  getCurrentMouse: function (event, container) {
   return this.screenToLocalCoord(this.getCurrentGlobalMouse(event), container);
  },

  getCurrentMove: function (event, container, orig) {
    var mouse = this.getCurrentMouse(event, container);
    if (orig === undefined) orig = this.orig;
    return dojox.gfx.matrix.translate(mouse.x - orig.x, mouse.y - orig.y);
  },

  getCurrentCursorBbox: function() {
    return sketchSpaceDesigner.designer.bbox.Bbox().addPoints([this.orig, this.mouse]);
  },

  addOutline: function(name, bbox, lineDefinitions) {
    if (this.outlines[name] !== undefined) return;
    this.outlines[name] = sketchSpaceDesigner.designer.outline.createOutline(this.designer, bbox, lineDefinitions);
    return this.outlines[name];
  },

  removeOutline: function (name) {
    if (this.outlines[name] !== undefined) {
      this.outlines[name].removeShape();
      delete this.outlines[name];
    }
  },

  addCursorBboxOutline: function(name) {
    this.addOutline("cursorBboxOutline", this.getCurrentCursorBbox(), this.cursorBboxOutlineDefinitions[name]);
  },

  updateCursorBboxOutline: function () {
    if (this.outlines.cursorBboxOutline == undefined) return;
    this.outlines.cursorBboxOutline.bbox = this.getCurrentCursorBbox();
    var n = this.outlines.cursorBboxOutline.bbox;
    this.outlines.cursorBboxOutline.update();
  },

  removeCursorBboxOutline: function () {
    this.removeOutline("cursorBboxOutline");
  }

});
<commit_msg>Bugfix for msie mouse hopping<commit_after>dojo.provide("sketchSpaceDesigner.designer.modes.Mode");

dojo.require("dojox.gfx.matrix");
dojo.require("dijit.layout.ContentPane");
dojo.require("sketchSpaceDesigner.designer.widgets");
dojo.require("sketchSpaceDesigner.designer.outline");

dojo.declare("sketchSpaceDesigner.designer.modes.Mode", [], {
  constructor: function () {
    this.inputState = {};
    this.inputState.keyboard = {};
    this.inputState.mouse = {};
    this.outlines = {};
  },
  enable: function () {
    var mode = this;
    this.designer.forEachObjectShape(function (shape) { mode.enableShape(shape); });
    this.onMouseWheelHandle = dojo.connect(this.designer.container, (!dojo.isMozilla ? "onmousewheel" : "DOMMouseScroll"), function(e){
      var scroll = e[(!dojo.isMozilla ? "wheelDelta" : "detail")] * (!dojo.isMozilla ? 1 : -1);
      mode.onMouseWheel(e, scroll);
    });
    this.onKeyDownHandle = dojo.connect(document, "onkeydown", this, function (event) { mode.onKeyDown(event); });
    this.onKeyUpHandle = dojo.connect(document, "onkeyup", this, function (event) { mode.onKeyUp(event); });
    this.onMouseDownHandle = dojo.connect(this.designer.container, "onmousedown", this, this.onMouseDown);
    this.onMouseUpHandle = dojo.connect(this.designer.container, "onmouseup", this, this.onMouseUp);
    this.onMouseMoveHandle = dojo.connect(this.designer.container, "onmousemove", this, this.onMouseMove);
    this.onActivateHandle = dojo.connect(this.designer.container, "activate", this, this.onActivate);
    this.onContextMenuHandle = dojo.connect(this.designer.container, "contextmenu", this, this.onContextMenu);
    this.setOptionsHandle = dojo.connect(this.designer, "setOptions", this, this.onSetOptions);

/*
    this.shareCurrentImageOption = new sketchSpaceDesigner.designer.widgets.OptionCheckBox({title:"Shared image selection:", optionsPath:"shareCurrentImage", designer:this.designer});
    this.designer.ui.options.addChild(this.shareCurrentImageOption);

    this.showAuthorshipColorOption = new sketchSpaceDesigner.designer.widgets.OptionCheckBox({title:"Show authorship:", optionsPath:"showAuthorshipColors", designer:this.designer});
    this.designer.ui.options.addChild(this.showAuthorshipColorOption);
*/

    this.designer.ui.options.layout();
  },
  disable: function () {
    var mode = this;
    dojo.disconnect(this.setOptionsHandle);
    dojo.disconnect(this.onContextMenuHandle);
    dojo.disconnect(this.onActivateHandle);
    dojo.disconnect(this.onMouseDownHandle);
    dojo.disconnect(this.onMouseUpHandle);
    dojo.disconnect(this.onMouseMoveHandle);
    dojo.disconnect(this.onKeyUpHandle);
    dojo.disconnect(this.onKeyDownHandle);
    dojo.disconnect(this.onMouseWheelHandle);
    this.designer.forEachObjectShape(function (shape) { mode.disableShape(shape); });
/*
    this.shareCurrentImageOption.destroyRecursive();
    this.showAuthorshipColorOption.destroyRecursive();
*/
    this.designer.ui.options.layout();
    for (var name in this.outlines)
      this.disableOutline(name);
  },
  enableShape: function (shape) {
    var mode = this;
    shape.onMouseUpHandle = shape.connect("onmouseup", shape, function (event) { mode.onShapeMouseUp(shape, event); });
    shape.onMouseDownHandle = shape.connect("onmousedown", shape, function (event) { mode.onShapeMouseDown(shape, event); });
  },
  disableShape: function (shape) {
    dojo.disconnect(shape.onMouseUpHandle);
    dojo.disconnect(shape.onMouseDownHandle);
  },
  onActivate: function  (event) { dojo.stopEvent(event); },
  onContextMenu: function  (event) { dojo.stopEvent(event); },
  onSetOptions: function () {},
  onMouseWheel: function (event, scroll) {
  },
  onKeyDown: function (event) {
    this.inputState.keyboard[event.keyCode] = event;
  },
  onKeyUp: function (event) {
    console.log([event.keyCode, event]);
    if (event.keyCode == dojo.keys.CHAR_S) {
      this.designer.setOptions({doStroke: !this.designer.options.doStroke});
    } else if (event.keyCode == dojo.keys.CHAR_F) {
      this.designer.setOptions({doFill: !this.designer.options.doFill});
    } else if (event.keyCode == dojo.keys.ESCAPE) {
     // FIXME: Kind of ugly, but needed to set the corect mode icon in
     // the UI. Maybe change UI code to use dojo.connect for that?
     this.designer.ui._onSelect();
    }
    delete this.inputState.keyboard[event.keyCode];
  },
  onMouseDown: function(event) {
    this.inputState.mouse[event.button] = event;
  },
  onMouseUp: function(event) {
    delete this.inputState.mouse[event.button];
  },
  onMouseMove: function(event) {
    this.mouse = this.getCurrentGlobalMouse(event);
    this.updateCursorBboxOutline();
  },
  onShapeMouseDown: function (shape, event) {
  },
  onShapeMouseUp: function (shape, event) {
  },

  screenToLocalCoord: function (p, container) {
    if (container === undefined) container = this.getContainerShape();
    var screenToObjMatrix = dojox.gfx.matrix.invert(container._getRealMatrix());
    return dojox.gfx.matrix.multiplyPoint(screenToObjMatrix, p.x, p.y);
  },

  localCoordToScreen: function (p, container) {
    if (container === undefined) container = this.getContainerShape();
    var objToScreenMatrix = container._getRealMatrix();
    return dojox.gfx.matrix.multiplyPoint(objToScreenMatrix, p.x, p.y);
  },

  getCurrentGlobalMouse: function (event) {
    if (event.layerX !== undefined && !$.browser.msie) {
      return {x:event.layerX, y:event.layerY};
    } else {
      /* All of this is for Opera, that Dojo does not seem to handle
       * correctly... Hopefully it doesn't break other browsers... */
      var cpos = $(this.designer.container).offset();
      var tpos = $(event.target).offset();
      return {x: event.offsetX + tpos.left - cpos.left, y: event.offsetY + tpos.top - cpos.top};
    }
  },

  getCurrentMouse: function (event, container) {
   return this.screenToLocalCoord(this.getCurrentGlobalMouse(event), container);
  },

  getCurrentMove: function (event, container, orig) {
    var mouse = this.getCurrentMouse(event, container);
    if (orig === undefined) orig = this.orig;
    return dojox.gfx.matrix.translate(mouse.x - orig.x, mouse.y - orig.y);
  },

  getCurrentCursorBbox: function() {
    return sketchSpaceDesigner.designer.bbox.Bbox().addPoints([this.orig, this.mouse]);
  },

  addOutline: function(name, bbox, lineDefinitions) {
    if (this.outlines[name] !== undefined) return;
    this.outlines[name] = sketchSpaceDesigner.designer.outline.createOutline(this.designer, bbox, lineDefinitions);
    return this.outlines[name];
  },

  removeOutline: function (name) {
    if (this.outlines[name] !== undefined) {
      this.outlines[name].removeShape();
      delete this.outlines[name];
    }
  },

  addCursorBboxOutline: function(name) {
    this.addOutline("cursorBboxOutline", this.getCurrentCursorBbox(), this.cursorBboxOutlineDefinitions[name]);
  },

  updateCursorBboxOutline: function () {
    if (this.outlines.cursorBboxOutline == undefined) return;
    this.outlines.cursorBboxOutline.bbox = this.getCurrentCursorBbox();
    var n = this.outlines.cursorBboxOutline.bbox;
    this.outlines.cursorBboxOutline.update();
  },

  removeCursorBboxOutline: function () {
    this.removeOutline("cursorBboxOutline");
  }

});
<|endoftext|>