<commit_before>// Toolbar Item: 'Edit'

WulinMaster.actions.Edit = $.extend({}, WulinMaster.actions.BaseAction, {
  name: 'edit',

  handler: function() {
    var grid = this.getGrid();

    // Batch update action
    batchUpdateByAjax(grid);
    return false;
  }
});

var batchUpdateByAjax = function(grid, version) {
  var ids, name, scope, width, height, selectedIndexes, url;
  selectedIndexes = grid.getSelectedRows();
  name = grid.name;
  scope = $( '#' + name + '_form');

  if (!selectedIndexes || selectedIndexes.length === 0) {
    displayErrorMessage('Please select a record');
  } else {
    ids = grid.getSelectedIds();
    if (ids.length > 350) {
      displayErrorMessage('You select too many rows, please select less than 350 rows.');
      return;
    }
    url = grid.path + '/wulin_master_edit_form' + grid.query;
    if (version)
      url = url + "&update_version=" + version;
    $.get(url, function(data){
      Ui.createModelModal(grid, data, {
        ready: function(modal, trigger) {
          Ui.setupForm(grid, true, selectedIndexes);
          Ui.setupComponents(grid);
          showFlagCheckBox(modal, ids);
          checkTheBox(name);
          submitForm(grid, ids, selectedIndexes);
        }
      });
    });
  }
};

var fillValues = function(scope, grid, selectedIndexes) {
  var data, inputBox, dataArr, comm = {};
  if (selectedIndexes.length == 1) {
    data = grid.loader.data[selectedIndexes[0]];
    loadValue(scope, data);
  } else {
    dataArr = $.map(selectedIndexes, function(n, i){
      return grid.loader.data[n];
    });
    $.each(dataArr, function(index, n){
      for (var k in n) {
        if (index === 0) {
          if (k != 'id' && k != 'slick_index') comm[k] = n[k];
        } else {
          if ($.type(n[k]) != 'object' && comm[k] !== n[k]) {
            delete comm[k];
          } else if ($.type(n[k]) === 'object' && $.type(comm[k]) === 'object' && !compareArray(comm[k]['id'], n[k]['id'])) {
            delete comm[k];
          }
        }
      }
    });
    loadValue(scope, comm);
  }

  // Avoid label overlapping input to MD textfield
  $('#' + grid.name + '_form .field').filter(function () {
    return !!$(this).find('input').val();
  }).find('label').addClass('active');
};

var loadValue = function(scope, data) {
  for ( var i in data) {
    if ($('input:text[data-field="' + i + '"]', scope).size() > 0) {
      $('input[data-field="' + i + '"]', scope).val(data[i]);
    } else if ($('textarea[data-field="' + i + '"]', scope).size() > 0) {
      $('textarea[data-field="' + i + '"]', scope).val(data[i]);
    } else if ($('input:checkbox[data-field="' + i + '"]', scope).size() > 0) {
      if (data[i]) {
        $('input:checkbox[data-field="' + i + '"]', scope).prop('checked', true);
      } else {
        $('input:checkbox[data-field="' + i + '"]', scope).removeAttr('checked');
      }
    } else if ($('select[data-field="' + i + '"]', scope).size() > 0) {
      inputBox = $('select[data-field="' + i + '"]', scope);
      if ($.type(data[i]) === 'string') {
        inputBox.val(data[i]);
      } else if ($.type(data[i]) === 'object') {
        if ($.type(data[i]['id']) === 'array') {
          inputBox.val(data[i]['id']);
        } else {
          inputBox.val(data[i]['id']);
        }
      } else if ($.type(data[i]) === 'array') {
        inputBox.val(data[i]);
      }

      inputBox.trigger("chosen:updated");
      distinctInput(inputBox);
    }
  }
};

var distinctInput = function(inputBox) {
  inputBox.chosen().change(function(){
    $('#' + inputBox.attr('id') + '_chosen').off('click').on('click', 'li:contains("Add new Option")', function() {
      Ui.createAddOptionModal(inputBox);
    });
    $('#' + inputBox.attr('id') + '_chosen .chosen-single .search-choice-close').trigger('mouseup');
  });
};

var showFlagCheckBox = function(scope, ids) {
  if (ids.length > 1) {
    $('.target_flag_container', scope).show();
  } else {
    $('.target_flag_container', scope).hide();
  }
};

var checkTheBox = function(name) {
  var scope = $( '#' + name + '_form');
  // Check flag when change value of the box
  scope.off('keyup', 'input:text, input:password, textarea').on('keyup', 'input:text, input:password, textarea', function(e) {
    $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]').prop('checked', true);
  });
  scope.off('change', 'input:checkbox, input:file').on('change', 'input:checkbox:not(.target_flag), input:file', function(e) {
    $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]').prop('checked', true);
  });

  // Date picker \ datetime picker \ time picker
  scope.off('change', 'input.flatpickr-input').on('change', 'input.flatpickr-input', function(e) {
    $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]').prop('checked', true);
  });

  // Empty input box when flag change to unchecked
  scope.off('change', 'input.target_flag:visible').on('change', 'input.target_flag:visible', function(){
    if ($(this).prop('checked') == false) {
      $('input[data-target="' + $(this).attr('data-target') + '"]').not(':button, :submit, :reset, :hidden, .target_flag').val('').removeAttr('checked').removeAttr('selected');
      $('select[data-target="' + $(this).attr('data-target') + '"]').val('').trigger("chosen:updated");
    }
  });
};

var grepValues = function(formData, jqForm, options) {
  var flagDom;
  for(var i = formData.length - 1; i >= 0; i--) {
    flagDom = $('input.target_flag:checkbox[data-target="' + $('[name="' + formData[i].name + '"]').not('[type="hidden"]').attr('data-target') + '"]', jqForm);
    if(flagDom.not(':checked').size() > 0) {
      formData.splice(i, 1);
    }
  }
};

var submitForm = function(grid, ids, selectedIndexes) {
  var name = grid.name,
  $scope = $( '#' + name + '_form'),
  $form = $('form', $scope);
  $scope.off('click', '.update_btn').on('click', '.update_btn', function() {
    var options = {
      dateType: 'json',
      url: grid.path + "/" + ids + ".json"+grid.query,
      data: {_method: 'PUT'},
      beforeSubmit: grepValues,
      success: function(msg) {
        if(msg.success) {
          Ui.resetForm(grid.name);
          grid.loader.reloadData();
          if (selectedIndexes.length > 1) {
            displayNewNotification(selectedIndexes.length + ' ' + grid.model.toLowerCase() + 's updated');
          } else {
            displayNewNotification('1 ' + grid.model.toLowerCase() + ' updated');
          }

        } else {
          displayErrorMessage(msg.error_message);
          saveMessage('Error updating ' + grid.model.toLowerCase(), 'error');
          grid.loader.reloadData();
        }
        $scope.closest('.modal').modal('close');
      }
    };
    $form.ajaxSubmit(options);
    return false;
  });
};

var compareArray = function(x, y) {
  if ($.type(x) == 'array' && $.type(y) == 'array') {
    if (x.length != y.length) {
      return false;
    }

    for (var k in x) {
      if (x[k] != y[k]) {//!== So that the the values are not converted while comparison
        return false;
      }
    }
    return true;
  } else {
    return x === y;
  }
};

WulinMaster.ActionManager.register(WulinMaster.actions.Edit);
<commit_msg>BUG #32 - Fix the option select feature fir dropdown in edit form<commit_after>// Toolbar Item: 'Edit'

WulinMaster.actions.Edit = $.extend({}, WulinMaster.actions.BaseAction, {
  name: 'edit',

  handler: function() {
    var grid = this.getGrid();

    // Batch update action
    batchUpdateByAjax(grid);
    return false;
  }
});

var batchUpdateByAjax = function(grid, version) {
  var ids, name, scope, width, height, selectedIndexes, url;
  selectedIndexes = grid.getSelectedRows();
  name = grid.name;
  scope = $( '#' + name + '_form');

  if (!selectedIndexes || selectedIndexes.length === 0) {
    displayErrorMessage('Please select a record');
  } else {
    ids = grid.getSelectedIds();
    if (ids.length > 350) {
      displayErrorMessage('You select too many rows, please select less than 350 rows.');
      return;
    }
    url = grid.path + '/wulin_master_edit_form' + grid.query;
    if (version)
      url = url + "&update_version=" + version;
    $.get(url, function(data){
      Ui.createModelModal(grid, data, {
        ready: function(modal, trigger) {
          Ui.setupForm(grid, true, selectedIndexes);
          Ui.setupComponents(grid);
          showFlagCheckBox(modal, ids);
          checkTheBox(name);
          submitForm(grid, ids, selectedIndexes);
        }
      });
    });
  }
};

var fillValues = function(scope, grid, selectedIndexes) {
  var data, inputBox, dataArr, comm = {};
  if (selectedIndexes.length == 1) {
    data = grid.loader.data[selectedIndexes[0]];
    loadValue(scope, data);
  } else {
    dataArr = $.map(selectedIndexes, function(n, i){
      return grid.loader.data[n];
    });
    $.each(dataArr, function(index, n){
      for (var k in n) {
        if (index === 0) {
          if (k != 'id' && k != 'slick_index') comm[k] = n[k];
        } else {
          if ($.type(n[k]) != 'object' && comm[k] !== n[k]) {
            delete comm[k];
          } else if ($.type(n[k]) === 'object' && $.type(comm[k]) === 'object' && !compareArray(comm[k]['id'], n[k]['id'])) {
            delete comm[k];
          }
        }
      }
    });
    loadValue(scope, comm);
  }

  // Avoid label overlapping input to MD textfield
  $('#' + grid.name + '_form .field').filter(function () {
    return !!$(this).find('input').val();
  }).find('label').addClass('active');
};

var loadValue = function(scope, data) {
  for ( var i in data) {
    if ($('input:text[data-field="' + i + '"]', scope).size() > 0) {
      $('input[data-field="' + i + '"]', scope).val(data[i]);
    } else if ($('textarea[data-field="' + i + '"]', scope).size() > 0) {
      $('textarea[data-field="' + i + '"]', scope).val(data[i]);
    } else if ($('input:checkbox[data-field="' + i + '"]', scope).size() > 0) {
      if (data[i]) {
        $('input:checkbox[data-field="' + i + '"]', scope).prop('checked', true);
      } else {
        $('input:checkbox[data-field="' + i + '"]', scope).removeAttr('checked');
      }
    } else if ($('select[data-field="' + i + '"]', scope).size() > 0) {
      inputBox = $('select[data-field="' + i + '"]', scope);
      if ($.type(data[i]) === 'string') {
        inputBox.val(data[i]);
      } else if ($.type(data[i]) === 'object') {
        if ($.type(data[i]['id']) === 'array') {
          inputBox.val(data[i]['id']);
        } else {
          inputBox.val(data[i]['id']);
        }
      } else if ($.type(data[i]) === 'array') {
        inputBox.val(data[i]);
      }
      distinctInput(inputBox);
    }
  }
};

var distinctInput = function(inputBox) {
  inputBox.change(function(){
    $('#' + inputBox.attr('id') + '_chosen').off('click').on('click', 'li:contains("Add new Option")', function() {
      $('#' + inputBox.attr('id') + '_chosen .chosen-single .search-choice-close').trigger('mouseup');
      Ui.createAddOptionModal(inputBox);
    });
  });
};

var showFlagCheckBox = function(scope, ids) {
  if (ids.length > 1) {
    $('.target_flag_container', scope).show();
  } else {
    $('.target_flag_container', scope).hide();
  }
};

var checkTheBox = function(name) {
  var scope = $( '#' + name + '_form');
  // Check flag when change value of the box
  scope.off('keyup', 'input:text, input:password, textarea').on('keyup', 'input:text, input:password, textarea', function(e) {
    $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]').prop('checked', true);
  });
  scope.off('change', 'input:checkbox, input:file').on('change', 'input:checkbox:not(.target_flag), input:file', function(e) {
    $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]').prop('checked', true);
  });

  // Date picker \ datetime picker \ time picker
  scope.off('change', 'input.flatpickr-input').on('change', 'input.flatpickr-input', function(e) {
    $('input.target_flag:checkbox[data-target="' + $(e.currentTarget).attr('data-target') + '"]').prop('checked', true);
  });

  // Empty input box when flag change to unchecked
  scope.off('change', 'input.target_flag:visible').on('change', 'input.target_flag:visible', function(){
    if ($(this).prop('checked') == false) {
      $('input[data-target="' + $(this).attr('data-target') + '"]').not(':button, :submit, :reset, :hidden, .target_flag').val('').removeAttr('checked').removeAttr('selected');
      $('select[data-target="' + $(this).attr('data-target') + '"]').val('').trigger("chosen:updated");
    }
  });
};

var grepValues = function(formData, jqForm, options) {
  var flagDom;
  for(var i = formData.length - 1; i >= 0; i--) {
    flagDom = $('input.target_flag:checkbox[data-target="' + $('[name="' + formData[i].name + '"]').not('[type="hidden"]').attr('data-target') + '"]', jqForm);
    if(flagDom.not(':checked').size() > 0) {
      formData.splice(i, 1);
    }
  }
};

var submitForm = function(grid, ids, selectedIndexes) {
  var name = grid.name,
  $scope = $( '#' + name + '_form'),
  $form = $('form', $scope);
  $scope.off('click', '.update_btn').on('click', '.update_btn', function() {
    var options = {
      dateType: 'json',
      url: grid.path + "/" + ids + ".json"+grid.query,
      data: {_method: 'PUT'},
      beforeSubmit: grepValues,
      success: function(msg) {
        if(msg.success) {
          Ui.resetForm(grid.name);
          grid.loader.reloadData();
          if (selectedIndexes.length > 1) {
            displayNewNotification(selectedIndexes.length + ' ' + grid.model.toLowerCase() + 's updated');
          } else {
            displayNewNotification('1 ' + grid.model.toLowerCase() + ' updated');
          }

        } else {
          displayErrorMessage(msg.error_message);
          saveMessage('Error updating ' + grid.model.toLowerCase(), 'error');
          grid.loader.reloadData();
        }
        $scope.closest('.modal').modal('close');
      }
    };
    $form.ajaxSubmit(options);
    return false;
  });
};

var compareArray = function(x, y) {
  if ($.type(x) == 'array' && $.type(y) == 'array') {
    if (x.length != y.length) {
      return false;
    }

    for (var k in x) {
      if (x[k] != y[k]) {//!== So that the the values are not converted while comparison
        return false;
      }
    }
    return true;
  } else {
    return x === y;
  }
};

WulinMaster.ActionManager.register(WulinMaster.actions.Edit);
<|endoftext|>


<commit_before>/* useful vars */
var selectedMP = null;
var supportsSecret = null;
var emailTxt = ""; // global for preview and then send
// literally whatever data we want to store along with the email
var emailData = {
  allowPublicListing: true // It's an open letter in this campaign
};
var submissionDeferred;

if (!getParameterByName("embedded")) {
  $('#title').show();
  $('#intro').show();
}

$(window).on('load', function() {
  // load the data into the dropdown
  var mps = {};

  var data = persons.map(function(p) {
    mps[p.id] = p;

    return {
      id: p.id,
      text: p.name + (p.party ? (' - ' + p.party.abbr) : ''),
    };
  });

  data.sort(function(a, b) {
    return a.text.localeCompare(b.text);
  });

  $(".choose-mp .single-mp").click(function() {
    var selectedId = parseInt($(this).data('id'));
    chooseMP(mps[selectedId]);
  });

  $(".neglected-mps .single-mp").first().click();

  $('#select-dropdown').on("change", function(e) {
    var selectedId = parseInt($(this).val());
    chooseMP(mps[selectedId]);
  });

  $('select.use-select2').select2({
    data: data,
    placeholder: 'Choose an MP',
  });

  $('body').append($(""));
});

$("#previewEmail").prop("disabled", false);

$(".multiple-choice .option").click(function() {
  var $this = $(this);

  $this.siblings(".multiple-choice .option").removeClass("selected");
  $this.addClass("selected");
  $("#previewEmail").prop("disabled", false);
});

$(".checkbox .option").click(function() {
  var $this = $(this);

  $this.toggleClass("selected");
});

$(".choose-one li").on('click', function(e) {
  var $this = $(this);
  $this.siblings().removeClass('active');
  $this.addClass('active');
});

/* follow-up questions */
var senderSecret = null,
    emailId = null;

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}

function emailSent() {
  ga('send', 'event', 'representation-email', 'sent');

  // prep follow up questions
  var questions = [
    {
      q: "Would you like to get updates about tools such as this?",
      a: ["Yes, please", "No, thanks"],
    },
  ];
  var q = questions[getRandomInt(0, questions.length)];

  ga('send', 'event', 'follow-up', 'asked', q.q);
  $('.follow-up-question p').text(q.q);
  $('#follow-up-answer-1').text(q.a[0]);
  $('#follow-up-answer-2').text(q.a[1]);

  // prep sharing
  $('.twitter-share').data('message', 'I wrote an open letter to ' + selectedMP.name + ' about being heard in Parliament. Join me.');
  $('.fb-share').data('message', 'I wrote an open letter to ' + selectedMP.name + ' about being heard in Parliament, you should too.');

  $("#preview-message").hide();
  $("#message-sent").show();
  pymChild.scrollParentTo('contactmps-embed-parent');
  pymChild.sendHeight();
}

$(".follow-up-question-box .toggle-select-follow-up").click(function() {
  var $this = $(this);

  $(".follow-up-question-box .toggle-select-follow-up").removeClass("selected");
  $this.addClass("selected");

  var q = $('.follow-up-question').text().trim();
  var a = $this.text().trim();

  $('.follow-up-question p').text('Thanks!');
  $('.follow-up-answer-box').hide();

  // submit to server
  submissionDeferred.done(function() {
    jQuery.ajax('/api/v1/email/' + emailId + '/qa/', {
      type: 'POST',
      data: {
        question: q,
        answer: a,
        sender_secret: senderSecret,
      },
    });
  });

  ga('send', 'event', 'follow-up', 'answered', q);
});

$("#preview-message").hide();
$("#message-sent").hide();
pymChild.sendHeight();

$("#previewEmail").click(function(e) {
  e.preventDefault();
  var senderName = $(".name-input").val();
  var senderEmail = $(".email-input").val();
  var emailSubject = "Representation of the public in the national assembly";
  var sufficientlyRepresentedOptions = $('#sufficiently-represented .option.selected')
  var howShouldVoiceHeard = $('#how-should-voice-heard textarea').val();
  var concerns = $('#concerns textarea').val();
  var province = $('select[name=province]').val();

  /** VALIDATION **/

  if (senderName === '') {
    alert('Please enter your name');
    $('.name-input').focus();
    return;
  }

  if (senderEmail === '') {
    alert('Please enter your email');
    $('.email-input').focus();
    return;
  }

  if (province === null) {
    alert('Please select your province');
    $('select[name=province]').focus();
    return;
  }

  if (sufficientlyRepresentedOptions.length !== 1) {
    alert('Please indicate whether you feel sufficiently-represented');
    pymChild.scrollParentToChildEl('sufficiently-represented');
    return;
  }

  if (howShouldVoiceHeard === '') {
    alert('Please indicate how you\'d like to make your voice heard');
    $('#how-should-voice-heard').focus();
    return;
  }

  emailTxt = composeMessage();
  emailHtml = emailTxt.replace(/\n/g, '
');

  $("#name").text(senderName);
  $("#email").text(senderEmail);
  $("#email-title").text(emailSubject);
  $("#letter-preview").html(emailHtml);

  $("#build-message").hide();
  $("#preview-message").show();
  location.hash = "#preview-message";

  pymChild.scrollParentTo('contactmps-embed-parent');
  pymChild.sendHeight();
});

$("#editMessage").click(function(e) {
  e.preventDefault();
  $("#build-message").show();
  $("#preview-message").hide();
  location.hash = "#email-secret";
  pymChild.scrollParentTo('contactmps-embed-parent');
  pymChild.sendHeight();
});

var reCaptchaValid = false;
var gReCaptchaValidated = function() {
  $("input[type=submit]").removeAttr('disabled');
  reCaptchaValid = true;
};

var gReCaptchaExpired = function() {
  $("input[type=submit]").attr('disabled','disabled');
  reCaptchaValid = false;
};

var recaptchaLoaded = function() {
  grecaptcha.render('recaptcha', {
    'sitekey': recaptchaKey,
    'callback': gReCaptchaValidated,
    'expired-callback': gReCaptchaExpired
  });
  if (typeof pymChild !== undefined) {
    pymChild.sendHeight();
  }
};

$('#representation').on('submit', submitForm);

function triggerSubmit() {
  $('#representation').submit();
}

function submitForm(e) {
  e.preventDefault();

  if (!reCaptchaValid) {
    alert("Please prove you are human first");
    pymChild.scrollParentToChildEl('recaptcha');
    return;
  }

  var senderName = $(".name-input").val();
  var senderEmail = $(".email-input").val();
  var emailSubject = $("#email-title").text();

  submissionDeferred = jQuery.ajax('/api/v1/email/', {
    type: 'POST',
    data: {
      person: selectedMP.id,
      name: senderName,
      email: senderEmail,
      body: emailTxt,
      subject: emailSubject,
      anyData: JSON.stringify(emailData),
      gRecaptchaResponse: grecaptcha.getResponse(),
      campaign_slug: 'natrepresentation',
    },
    success: function(data) {
      console.info("success", data);

      senderSecret = data.sender_secret;
      emailId = data.secure_id;
      $('.twitter-share').data('url', 'http://noconfidencevote.openup.org.za/email/' + emailId + '/');
      $('.facebook-share').data('url', 'http://noconfidencevote.openup.org.za/email/' + emailId + '/');
      $("#view-letter-link").attr("href", "/email/" + emailId);
    },
    error: function(jqXHR, textStatus, errorThrown) {
      console.error(jqXHR, textStatus, errorThrown, jqXHR.responseText);
    }
  });

  emailSent();
}

// https://stackoverflow.com/a/901144/1305080
function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[\[\]]/g, "\\$&");
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
}

function getParentUrl() {
  return getParameterByName('parentUrl');
}

$(function() {
  ga('send', {
    hitType: 'event',
    eventCategory: 'environment',
    eventAction: 'loaded-by-parent-url',
    eventLabel: getParentUrl()
  });
});

var template = "Hon. {{{ recipient_name }}},\n\nAs a democratically elected member of Parliament, you represent me, my concerns and my hopes for the future of South Africa.\n\nI {{{ sufficiently_represented }}}that members of Parliament represent me sufficiently as a citizen, in the National Assembly.\n\n{{{ how_voice_heard }}}.\n\nI would like to be able to raise my concerns about issues of national government with you by {{{ how_should_voice_heard }}}.\n\n{{{ concerns }}}I believe it is important that I and other South Africans can have our concerns heard by national Government. I hope that we can work together to find effective ways of being heard.\n\nSincerely,\n{{{ sender_name }}}\n{{{ province }}}\n";

function composeMessage() {
  var sufficientlyRepresentedOption = $('#sufficiently-represented .option.selected').data('value');
  var howVoiceHeardOptions = $('#how-voice-heard .option.selected').map(
    function() { return $(this).data('value'); }).get(); // This get() is Reeeeeeally important
  //   ... because without it we have a jQuery collection which doesn't have
  //   ... join and causes cross-site request issues.
  var howElseVoiceHeard = $('#how-else-voice-heard textarea').val();
  var voiceHeardOutcome = $('#voice-heard-outcome .option.selected').data('value');
  var howShouldVoiceHeard = $('#how-should-voice-heard textarea').val();
  var concernsAnswer = $('#concerns textarea').val();
  var senderName = $('input[name=input-name]').val();
  var province = $('select[name=province]').val();
  var sufficientlyRepresented,
      howVoiceHeard,
      concerns;

  if (howElseVoiceHeard !== "")
    howVoiceHeardOptions.push(howElseVoiceHeard);

  if (sufficientlyRepresentedOption === "yes")
    sufficientlyRepresented = "feel ";
  else if (sufficientlyRepresentedOption === "no")
    sufficientlyRepresented = "do not feel ";
  else if (sufficientlyRepresentedOption === "unsure")
    sufficientlyRepresented = "am not sure ";

  var haveTried = "I've sought national representation by ";
  if (howVoiceHeardOptions.length === 0)
    howVoiceHeard = "I have not tried institutional mechanisms for seeking national representation"
  else if (howVoiceHeardOptions.length === 1)
    howVoiceHeard = haveTried + howVoiceHeardOptions[0];
  else if (howVoiceHeardOptions.length === 2)
    howVoiceHeard = haveTried + howVoiceHeardOptions[0] + " and " + howVoiceHeardOptions[1];
  else
    howVoiceHeard = haveTried + howVoiceHeardOptions.slice(0, -1).join(", ") + ", and " + howVoiceHeardOptions.slice(-1)[0];

  if (concernsAnswer !== '')
    concerns = "My biggest concerns about South Africa are " + concernsAnswer + ".\n\n";
  else
    concerns = ''

  var context = {
    'recipient_name': selectedMP.name,
    'sufficiently_represented': sufficientlyRepresented,
    'how_else_voice_heard': howElseVoiceHeard,
    'how_voice_heard': howVoiceHeard,
    'voice_heard_outcome': voiceHeardOutcome,
    'how_should_voice_heard': howShouldVoiceHeard,
    'concerns': concerns,
    'sender_name': senderName,
    'province': province
  };
  emailData.sufficientlyRepresentedOption = sufficientlyRepresentedOption;
  emailData.sufficientlyRepresented = sufficientlyRepresented;
  emailData.howVoiceHeardOptions = howVoiceHeardOptions;
  emailData.howVoiceHeard = howVoiceHeard;
  emailData.howElseVoiceHEard = howElseVoiceHeard;
  emailData.voiceHeardOutcome = voiceHeardOutcome;
  emailData.howShouldVoiceHeard = howShouldVoiceHeard;
  emailData.concernsAnswer = concernsAnswer;
  emailData.concerns = concerns;
  emailData.province = province;

  console.log(emailData);
  return Mustache.render(template, context);
}

function chooseMP(mp) {
  // mark an MP as chosen
  $(".choose-mp .single-mp").removeClass("selected");
  $(".selected-mp-wrap").removeClass("hidden");
  $('.single-mp[data-id=' + mp.id + ']').addClass('selected');
  // we pick up the MP name from here so fix message composition if you change this
  $(".recipient").text(mp.name);
  $(".selected-mp .mp-img-wrapper").css({"background-image": mp.local_portrait_url ? ('url(' + mp.local_portrait_url + ')') : ''});
  $(".selected-mp .mp-img-wrapper .party-logo").attr("src", mp.party ? mp.party.icon_url : '');
  $(".pa-link").attr("href", mp.pa_url);
  $("form input[name=person]").val(mp.id);
  selectedMP = mp;

  composeMessage();
  pymChild.sendHeight();
}
<commit_msg>Validate email<commit_after>/* useful vars */
var selectedMP = null;
var supportsSecret = null;
var emailTxt = ""; // global for preview and then send
// literally whatever data we want to store along with the email
var emailData = {
  allowPublicListing: true // It's an open letter in this campaign
};
var submissionDeferred;

if (!getParameterByName("embedded")) {
  $('#title').show();
  $('#intro').show();
}

$(window).on('load', function() {
  // load the data into the dropdown
  var mps = {};

  var data = persons.map(function(p) {
    mps[p.id] = p;

    return {
      id: p.id,
      text: p.name + (p.party ? (' - ' + p.party.abbr) : ''),
    };
  });

  data.sort(function(a, b) {
    return a.text.localeCompare(b.text);
  });

  $(".choose-mp .single-mp").click(function() {
    var selectedId = parseInt($(this).data('id'));
    chooseMP(mps[selectedId]);
  });

  $(".neglected-mps .single-mp").first().click();

  $('#select-dropdown').on("change", function(e) {
    var selectedId = parseInt($(this).val());
    chooseMP(mps[selectedId]);
  });

  $('select.use-select2').select2({
    data: data,
    placeholder: 'Choose an MP',
  });

  $('body').append($(""));
});

$("#previewEmail").prop("disabled", false);

$(".multiple-choice .option").click(function() {
  var $this = $(this);

  $this.siblings(".multiple-choice .option").removeClass("selected");
  $this.addClass("selected");
  $("#previewEmail").prop("disabled", false);
});

$(".checkbox .option").click(function() {
  var $this = $(this);

  $this.toggleClass("selected");
});

$(".choose-one li").on('click', function(e) {
  var $this = $(this);
  $this.siblings().removeClass('active');
  $this.addClass('active');
});

/* follow-up questions */
var senderSecret = null,
    emailId = null;

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}

function emailSent() {
  ga('send', 'event', 'representation-email', 'sent');

  // prep follow up questions
  var questions = [
    {
      q: "Would you like to get updates about tools such as this?",
      a: ["Yes, please", "No, thanks"],
    },
  ];
  var q = questions[getRandomInt(0, questions.length)];

  ga('send', 'event', 'follow-up', 'asked', q.q);
  $('.follow-up-question p').text(q.q);
  $('#follow-up-answer-1').text(q.a[0]);
  $('#follow-up-answer-2').text(q.a[1]);

  // prep sharing
  $('.twitter-share').data('message', 'I wrote an open letter to ' + selectedMP.name + ' about being heard in Parliament. Join me.');
  $('.fb-share').data('message', 'I wrote an open letter to ' + selectedMP.name + ' about being heard in Parliament, you should too.');

  $("#preview-message").hide();
  $("#message-sent").show();
  pymChild.scrollParentTo('contactmps-embed-parent');
  pymChild.sendHeight();
}

$(".follow-up-question-box .toggle-select-follow-up").click(function() {
  var $this = $(this);

  $(".follow-up-question-box .toggle-select-follow-up").removeClass("selected");
  $this.addClass("selected");

  var q = $('.follow-up-question').text().trim();
  var a = $this.text().trim();

  $('.follow-up-question p').text('Thanks!');
  $('.follow-up-answer-box').hide();

  // submit to server
  submissionDeferred.done(function() {
    jQuery.ajax('/api/v1/email/' + emailId + '/qa/', {
      type: 'POST',
      data: {
        question: q,
        answer: a,
        sender_secret: senderSecret,
      },
    });
  });

  ga('send', 'event', 'follow-up', 'answered', q);
});

$("#preview-message").hide();
$("#message-sent").hide();
pymChild.sendHeight();

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

$("#previewEmail").click(function(e) {
  e.preventDefault();
  var senderName = $(".name-input").val();
  var senderEmail = $(".email-input").val();
  var emailSubject = "Representation of the public in the national assembly";
  var sufficientlyRepresentedOptions = $('#sufficiently-represented .option.selected')
  var howShouldVoiceHeard = $('#how-should-voice-heard textarea').val();
  var concerns = $('#concerns textarea').val();
  var province = $('select[name=province]').val();

  /** VALIDATION **/

  if (senderName === '') {
    alert('Please enter your name');
    $('.name-input').focus();
    return;
  }

  if (senderEmail === '' || !validateEmail(senderEmail)) {
    alert('Please enter a valid email address');
    $('.email-input').focus();
    return;
  }

  if (province === null) {
    alert('Please select your province');
    $('select[name=province]').focus();
    return;
  }

  if (sufficientlyRepresentedOptions.length !== 1) {
    alert('Please indicate whether you feel sufficiently-represented');
    pymChild.scrollParentToChildEl('sufficiently-represented');
    return;
  }

  if (howShouldVoiceHeard === '') {
    alert('Please indicate how you\'d like to make your voice heard');
    $('#how-should-voice-heard').focus();
    return;
  }

  emailTxt = composeMessage();
  emailHtml = emailTxt.replace(/\n/g, '
');

  $("#name").text(senderName);
  $("#email").text(senderEmail);
  $("#email-title").text(emailSubject);
  $("#letter-preview").html(emailHtml);

  $("#build-message").hide();
  $("#preview-message").show();
  location.hash = "#preview-message";

  pymChild.scrollParentTo('contactmps-embed-parent');
  pymChild.sendHeight();
});

$("#editMessage").click(function(e) {
  e.preventDefault();
  $("#build-message").show();
  $("#preview-message").hide();
  location.hash = "#email-secret";
  pymChild.scrollParentTo('contactmps-embed-parent');
  pymChild.sendHeight();
});

var reCaptchaValid = false;
var gReCaptchaValidated = function() {
  $("input[type=submit]").removeAttr('disabled');
  reCaptchaValid = true;
};

var gReCaptchaExpired = function() {
  $("input[type=submit]").attr('disabled','disabled');
  reCaptchaValid = false;
};

var recaptchaLoaded = function() {
  grecaptcha.render('recaptcha', {
    'sitekey': recaptchaKey,
    'callback': gReCaptchaValidated,
    'expired-callback': gReCaptchaExpired
  });
  if (typeof pymChild !== undefined) {
    pymChild.sendHeight();
  }
};

$('#representation').on('submit', submitForm);

function triggerSubmit() {
  $('#representation').submit();
}

function submitForm(e) {
  e.preventDefault();

  if (!reCaptchaValid) {
    alert("Please prove you are human first");
    pymChild.scrollParentToChildEl('recaptcha');
    return;
  }

  var senderName = $(".name-input").val();
  var senderEmail = $(".email-input").val();
  var emailSubject = $("#email-title").text();

  submissionDeferred = jQuery.ajax('/api/v1/email/', {
    type: 'POST',
    data: {
      person: selectedMP.id,
      name: senderName,
      email: senderEmail,
      body: emailTxt,
      subject: emailSubject,
      anyData: JSON.stringify(emailData),
      gRecaptchaResponse: grecaptcha.getResponse(),
      campaign_slug: 'natrepresentation',
    },
    success: function(data) {
      console.info("success", data);

      senderSecret = data.sender_secret;
      emailId = data.secure_id;
      $('.twitter-share').data('url', 'http://noconfidencevote.openup.org.za/email/' + emailId + '/');
      $('.facebook-share').data('url', 'http://noconfidencevote.openup.org.za/email/' + emailId + '/');
      $("#view-letter-link").attr("href", "/email/" + emailId);
    },
    error: function(jqXHR, textStatus, errorThrown) {
      console.error(jqXHR, textStatus, errorThrown, jqXHR.responseText);
    }
  });

  emailSent();
}

// https://stackoverflow.com/a/901144/1305080
function getParameterByName(name, url) {
    if (!url) url = window.location.href;
    name = name.replace(/[\[\]]/g, "\\$&");
    var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
        results = regex.exec(url);
    if (!results) return null;
    if (!results[2]) return '';
    return decodeURIComponent(results[2].replace(/\+/g, " "));
}

function getParentUrl() {
  return getParameterByName('parentUrl');
}

$(function() {
  ga('send', {
    hitType: 'event',
    eventCategory: 'environment',
    eventAction: 'loaded-by-parent-url',
    eventLabel: getParentUrl()
  });
});

var template = "Hon. {{{ recipient_name }}},\n\nAs a democratically elected member of Parliament, you represent me, my concerns and my hopes for the future of South Africa.\n\nI {{{ sufficiently_represented }}}that members of Parliament represent me sufficiently as a citizen, in the National Assembly.\n\n{{{ how_voice_heard }}}.\n\nI would like to be able to raise my concerns about issues of national government with you by {{{ how_should_voice_heard }}}.\n\n{{{ concerns }}}I believe it is important that I and other South Africans can have our concerns heard by national Government. I hope that we can work together to find effective ways of being heard.\n\nSincerely,\n{{{ sender_name }}}\n{{{ province }}}\n";

function composeMessage() {
  var sufficientlyRepresentedOption = $('#sufficiently-represented .option.selected').data('value');
  var howVoiceHeardOptions = $('#how-voice-heard .option.selected').map(
    function() { return $(this).data('value'); }).get(); // This get() is Reeeeeeally important
  //   ... because without it we have a jQuery collection which doesn't have
  //   ... join and causes cross-site request issues.
  var howElseVoiceHeard = $('#how-else-voice-heard textarea').val();
  var voiceHeardOutcome = $('#voice-heard-outcome .option.selected').data('value');
  var howShouldVoiceHeard = $('#how-should-voice-heard textarea').val();
  var concernsAnswer = $('#concerns textarea').val();
  var senderName = $('input[name=input-name]').val();
  var province = $('select[name=province]').val();
  var sufficientlyRepresented,
      howVoiceHeard,
      concerns;

  if (howElseVoiceHeard !== "")
    howVoiceHeardOptions.push(howElseVoiceHeard);

  if (sufficientlyRepresentedOption === "yes")
    sufficientlyRepresented = "feel ";
  else if (sufficientlyRepresentedOption === "no")
    sufficientlyRepresented = "do not feel ";
  else if (sufficientlyRepresentedOption === "unsure")
    sufficientlyRepresented = "am not sure ";

  var haveTried = "I've sought national representation by ";
  if (howVoiceHeardOptions.length === 0)
    howVoiceHeard = "I have not tried institutional mechanisms for seeking national representation"
  else if (howVoiceHeardOptions.length === 1)
    howVoiceHeard = haveTried + howVoiceHeardOptions[0];
  else if (howVoiceHeardOptions.length === 2)
    howVoiceHeard = haveTried + howVoiceHeardOptions[0] + " and " + howVoiceHeardOptions[1];
  else
    howVoiceHeard = haveTried + howVoiceHeardOptions.slice(0, -1).join(", ") + ", and " + howVoiceHeardOptions.slice(-1)[0];

  if (concernsAnswer !== '')
    concerns = "My biggest concerns about South Africa are " + concernsAnswer + ".\n\n";
  else
    concerns = ''

  var context = {
    'recipient_name': selectedMP.name,
    'sufficiently_represented': sufficientlyRepresented,
    'how_else_voice_heard': howElseVoiceHeard,
    'how_voice_heard': howVoiceHeard,
    'voice_heard_outcome': voiceHeardOutcome,
    'how_should_voice_heard': howShouldVoiceHeard,
    'concerns': concerns,
    'sender_name': senderName,
    'province': province
  };
  emailData.sufficientlyRepresentedOption = sufficientlyRepresentedOption;
  emailData.sufficientlyRepresented = sufficientlyRepresented;
  emailData.howVoiceHeardOptions = howVoiceHeardOptions;
  emailData.howVoiceHeard = howVoiceHeard;
  emailData.howElseVoiceHEard = howElseVoiceHeard;
  emailData.voiceHeardOutcome = voiceHeardOutcome;
  emailData.howShouldVoiceHeard = howShouldVoiceHeard;
  emailData.concernsAnswer = concernsAnswer;
  emailData.concerns = concerns;
  emailData.province = province;

  console.log(emailData);
  return Mustache.render(template, context);
}

function chooseMP(mp) {
  // mark an MP as chosen
  $(".choose-mp .single-mp").removeClass("selected");
  $(".selected-mp-wrap").removeClass("hidden");
  $('.single-mp[data-id=' + mp.id + ']').addClass('selected');
  // we pick up the MP name from here so fix message composition if you change this
  $(".recipient").text(mp.name);
  $(".selected-mp .mp-img-wrapper").css({"background-image": mp.local_portrait_url ? ('url(' + mp.local_portrait_url + ')') : ''});
  $(".selected-mp .mp-img-wrapper .party-logo").attr("src", mp.party ? mp.party.icon_url : '');
  $(".pa-link").attr("href", mp.pa_url);
  $("form input[name=person]").val(mp.id);
  selectedMP = mp;

  composeMessage();
  pymChild.sendHeight();
}
<|endoftext|>


<commit_before>/* jshint browser: true, jquery: true */
/* global Panel:false, console:false, server:false, addWidget:false */

var PanelManager = function(options) {
  this.panels = [];
  this.columns = 1;
  this.displayBanner = true;
  this.panelsIndex = [];
  this.server = null;
  this.addMode = false;
  this.dragAndDrop = null;
};

PanelManager.prototype.getByName = function(name) {
  var panel = null;

  for(var i = 0; i < this.panels.length; i++) {
    if(this.panels[i].name === name) {
      panel = this.panels[i];
      break;
    }
  }

  return panel;
};

PanelManager.prototype.getById = function(id) {
  var panel = null;

  var idx = this.panelsIndex.indexOf(id);

  if(idx > -1) {
    panel = this.panels[idx];
  }

  return panel;
};

PanelManager.prototype.add = function(panel) {
  this._sendAdd(panel);
};

PanelManager.prototype.update = function(panel) {
  this._sendUpdate(panel);
};

PanelManager.prototype.processMessages = function(server, id, content) {
  var processed = false;

  switch (id) {
    case Panel.MESSAGE_PANELS:
      this._receivedContent(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_ADD_PANEL:
      this._receivedAdd(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_DELETE_PANEL:
      this._receivedDelete(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_UPDATE_PANEL:
      this._receivedUpdate(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL:
      this._receivedMove(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_UP:
      this._receivedMoveUp(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_DOWN:
      this._receivedMoveDown(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_LEFT:
      this._receivedMoveLeft(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_RIGHT:
      this._receivedMoveRight(server, content);
      processed = true;
      break;
  }

  return processed;
};

PanelManager.prototype._addDefaultWidgets = function(panel, dataSourceType) {
  var i;
  var dataSourceId = '';
  var widgets = [];

  for(i = 0; i < dataSourceManager.dataSources.length; i++) {
    if(dataSourceManager.dataSources[i].type === dataSourceType) {
      dataSourceId = dataSourceManager.dataSources[i].id;
      break;
    }
  }

  widgets = DataSource.getDefaultPanels(dataSourceType, panel.id, dataSourceId, panel.defaultDataElement);

  for(i = 0; i < widgets.length; i++) {
    widgetManager.addWidget(widgets[i]);
  }
};

PanelManager.prototype._receivedAdd = function(server, panel) {
  this._addToDom(panel, this.addMode);

  switch(panel.style) {
    case Panel.STYLE_AGILE:
      this._addDefaultWidgets(panel, DataSource.TYPE_VERSION_ONE);
      break;

    case Panel.STYLE_CI:
      this._addDefaultWidgets(panel, DataSource.TYPE_JENKINS);
      break;
  }

  this.dragAndDrop.apply();

  if(this.addMode) {
    this.dragAndDrop.enable();
  }
  displayStatusMessage("The panel has been added", "Panels");
};

PanelManager.prototype._receivedDelete = function(server, id) {
  this._deleteFromDom(id);
  displayStatusMessage("The panel has been removed", "Panels");
};

PanelManager.prototype._receivedMove = function(server, content) {
  this._moveOnDom(content);
  displayStatusMessage("The panel move has been recorded", "Panels");
};

PanelManager.prototype._receivedMoveUp = function(server, id) {
  this._moveUpOnDom(id);
};

PanelManager.prototype._receivedMoveDown = function(server, id) {
  this._moveDownOnDom(id);
};

PanelManager.prototype._receivedMoveLeft = function(server, panel) {
  this._changeColumnOnDom(panel);
};

PanelManager.prototype._receivedMoveRight = function(server, panel) {
  this._changeColumnOnDom(panel);
};

PanelManager.prototype._receivedUpdate = function(server, panel) {
  // get current data
  var currentPanelElement = $('#panel' + panel.id);

  var currentPanel = new Panel(currentPanelElement.data());

  // apply changes to DOM
  if(currentPanel.name !== panel.name) {
    $('#panelName' + panel.id).text(panel.name);
    currentPanelElement.data('name', panel.name);
  }

// ignore this for now, presume that only via move left/right that this can happen
//  if(currentPanel.column !== panel.column) {
//
//  }

  if(currentPanel.iconName !== panel.iconName) {
    $('#panelIcon' + panel.id).removeClass(currentPanel.iconName).addClass(panel.iconName);
    currentPanelElement.data('icon-name', panel.iconName);
  }
};

PanelManager.prototype.preferencesChanged = function(systemPreferences) {
  var column;
  var insertBeforeBanner = false;

  if(this.columns === systemPreferences.columns) {
    return;
  }

  if(this.columns > systemPreferences.columns) {
    this._removeColumnsOnDom(this.columns, systemPreferences.columns);
  } else {
    // append extra columns
    if(this.displayBanner && !systemPreferences.displayBanner) {
      bannerManager.remove();
    }

    insertBeforeBanner = this.displayBanner && systemPreferences.displayBanner;

    this._updateColumnsOnDom(this.columns, systemPreferences.columns, insertBeforeBanner);

    if(!this.displayBanner && systemPreferences.displayBanner) {
      bannerManager.add();
    }

    this.dragAndDrop.apply();
  }

  this.displayBanner = systemPreferences.displayBanner;

  this.columns = systemPreferences.columns;
};

PanelManager.prototype._receivedContent = function(server, d) {
  var panelsArray = [];
  var item;
  var column;

  // remove content from home pane
  $("#homePane").empty();

  // add columns as defined in preferences
  this.columns = preferenceManager.getSystemPreference().columns;

  for (column = 1; column <= this.columns; column++) {
    this._addColumnToDom(column, this.columns);
  }

  this.displayBanner = preferenceManager.getSystemPreference().displayBanner;

  if(this.displayBanner) {
    bannerManager.add();
  }

  for (item in d) {
   panelsArray.push(new Panel(d[item]));
  }

  panelsArray.sort(function (a, b) {
      if (a.row  b.row) {
          return 1;
      }
      return 0;
  });

  panelsArray.forEach(function (p) {
    this._addToDom(p, this.addMode);
  }.bind(this));

  this.dragAndDrop.apply();
};

PanelManager.prototype._addColumnToDom = function(column, columns) {
  var columnClass = this._getColumnClass(columns);

  $("#homePane").append('

');
};

PanelManager.prototype._updateColumnsOnDom = function(oldColumnCount, newColumnCount, insertBeforeBanner) {
  var column = 0;
  var oldColumnClass = this._getColumnClass(oldColumnCount);
  var newColumnClass = this._getColumnClass(newColumnCount);

  var selection = $("#homePane ." + oldColumnClass);
  selection.removeClass(oldColumnClass);
  selection.addClass(newColumnClass);

  for(column = oldColumnCount + 1; column <= newColumnCount; column++) {
    if(insertBeforeBanner) {
      $("#footer").before('

');
    } else {
      $("#homePane").append('

');
    }
  }
};

PanelManager.prototype._removeColumnsOnDom = function(oldColumnCount, newColumnCount) {
  for(column = oldColumnCount; column > newColumnCount; column--) {
    $("#column" + column).remove();
  }

  var oldColumnClass = this._getColumnClass(oldColumnCount);
  var newColumnClass = this._getColumnClass(newColumnCount);

  var selection = $("#homePane ." + oldColumnClass);
  selection.removeClass(oldColumnClass);
  selection.addClass(newColumnClass);
};

PanelManager.prototype._getColumnClass = function(columns) {
  var columnClass;

  switch(columns) {
    case 1:
      columnClass = "col-md-12";
      break;

    case 2:
      columnClass = "col-md-6";
      break;

    case 3:
      columnClass = "col-md-4";
      break;

    case 4:
      columnClass = "col-md-3";
      break;

    default:
      columnClass = "col-md-12";
  }

  return columnClass;
};

PanelManager.prototype._sendAdd = function(panel) {
  this.server.sendMessage(Panel.MESSAGE_ADD_PANEL, panel);
};

PanelManager.prototype._sendDelete = function(id) {
  this.server.sendMessage(Panel.MESSAGE_DELETE_PANEL, id);
};

PanelManager.prototype._sendUpdate = function(panel) {
  this.server.sendMessage(Panel.MESSAGE_UPDATE_PANEL, panel);
};

PanelManager.prototype.sendMove = function(panelId, sourceColumn, targetColumn, row) {
  this.server.sendMessage(Panel.MESSAGE_MOVE_PANEL, {
    id: panelId,
    sourceColumn: sourceColumn,
    targetColumn: targetColumn,
    row: row
  });
};

PanelManager.prototype._addToDom = function(panel, buttonsVisible) {
  var displayButtonClass = buttonsVisible ? "displayButton" : "";
  var displayStyle = buttonsVisible ? "block" : "none";

  //$("#columnContainer" + panel.column).append(
  $("#column" + panel.column).append(
    '
' +
    '
' +
    '
' +
    '        <button id="panelBtnAddWidget' + panel.id + '" type="button" class="config-buttons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnDelete' + panel.id + '" type="button" class="config-buttons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnModify' + panel.id + '" type="button" class="config-buttons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '
' +
    '     ' + panel.name + '' +
    '
' +
    '
' +
    '
' +
    '
'
  );

  $('#panel' + panel.id).hide().fadeIn('slow');

  $('#panelBtnAddWidget' + panel.id).on('click', function() {
    viewWidgetWizard('add', {}, panel.id);
  }.bind(this));

  $('#panelBtnDelete' + panel.id).on('click', function() {
    this._sendDelete(panel.id);
  }.bind(this));

  $('#panelBtnModify' + panel.id).on('click', function() {
    displayPanelDialog('update', panel);
  }.bind(this));
};

PanelManager.prototype._deleteFromDom = function(id) {
  $('#panel' + id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "slow", queue: false})
    .fadeOut('slow', function() {
    this.remove();
  });
};

PanelManager.prototype._moveOnDom = function(content) {
  // TODO: only update if a timer expires before update panel response is received
  // this is due to flickering if we cancel the drag event and wait for backend to confirm

//  content.panelId
//  content.sourceColumn
//  content.targetColumn
//  var row = content.row;
//
//  if(row > 0) {
//    // determine appropriate entry
//    var rows = $("#" + "column" + content.targetColumn + ">div" + ".panel");
//
//    if(content.sourceColumn !== content.targetColumn) {
//      // we want the panel before the drop point, for different row this requires adjustment
//      row--;
//    }
//
//    // add after the correct position
//    $('#panel' + content.id).insertAfter("#" + rows[row].id);
//  } else {
//    $('#panel' + content.id).prependTo("#" + "column" + content.targetColumn);
//  }
};

PanelManager.prototype._moveDownOnDom = function(id) {
  var panelList = this._getList(id);

  if(panelList['panel' + id].position < (Object.keys(panelList).length - 1)) {
    // move down
    this._swapVertically(panelList['panel' + id].id, panelList['panel' + id].nextId);
  }
};

PanelManager.prototype._moveUpOnDom = function(id) {
  var panelList = this._getList(id);

  if(panelList['panel' + id].position > 0) {
    // move up
    this._swapVertically(panelList['panel' + id].id, panelList['panel' + id].prevId);
  }
};

PanelManager.prototype._changeColumnOnDom = function(panelData) {
  var panel = $('#panel' + panelData.id);

  $('#panel' + panelData.id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "fast", queue: false})
    .fadeOut('fast', function() {
      panel
        .appendTo("#columnContainer" + panelData.column)
        .hide()
        .css('height', '')
        .css('padding', '')
        .css('margin', '')
        .fadeIn('fast');

  });
};

PanelManager.prototype._getList = function(id) {
  var prevPanel = null;
  var nextPanel = null;
  var counter = 0;
  var panelList = {};

  $('#panel' + id).parent().children().each(function() {
    panelList[$(this).attr('id')] = {
      id: $(this).attr('id'),
      position: counter++,
      value: this,
      prevId: null,
      nextId: null
    };

    if(prevPanel !== null) {
      panelList[$(this).attr('id')].prevId = prevPanel.id;
      prevPanel.nextId = $(this).attr('id');
    }

    prevPanel = panelList[$(this).attr('id')];
  });

  return panelList;
};

PanelManager.prototype._swapVertically = function(a, b){
    a = $('#' + a)[0];
    b = $('#' + b)[0];

    var t = a.parentNode.insertBefore(document.createTextNode(''), a);

    b.parentNode.insertBefore(a, b);
    t.parentNode.insertBefore(b, t);
    t.parentNode.removeChild(t);
};

<commit_msg>Removed banner related logic and added column based class determination for panels<commit_after>/* jshint browser: true, jquery: true */
/* global Panel:false, console:false, server:false, addWidget:false */

var PanelManager = function(options) {
  this.panels = [];
  this.columns = 1;
  this.panelsIndex = [];
  this.server = null;
  this.addMode = false;
  this.dragAndDrop = null;
};

PanelManager.prototype.getByName = function(name) {
  var panel = null;

  for(var i = 0; i < this.panels.length; i++) {
    if(this.panels[i].name === name) {
      panel = this.panels[i];
      break;
    }
  }

  return panel;
};

PanelManager.prototype.getById = function(id) {
  var panel = null;

  var idx = this.panelsIndex.indexOf(id);

  if(idx > -1) {
    panel = this.panels[idx];
  }

  return panel;
};

PanelManager.prototype.add = function(panel) {
  this._sendAdd(panel);
};

PanelManager.prototype.update = function(panel) {
  this._sendUpdate(panel);
};

PanelManager.prototype.processMessages = function(server, id, content) {
  var processed = false;

  switch (id) {
    case Panel.MESSAGE_PANELS:
      this._receivedContent(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_ADD_PANEL:
      this._receivedAdd(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_DELETE_PANEL:
      this._receivedDelete(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_UPDATE_PANEL:
      this._receivedUpdate(server, new Panel(content));
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL:
      this._receivedMove(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_UP:
      this._receivedMoveUp(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_DOWN:
      this._receivedMoveDown(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_LEFT:
      this._receivedMoveLeft(server, content);
      processed = true;
      break;

    case Panel.MESSAGE_MOVE_PANEL_RIGHT:
      this._receivedMoveRight(server, content);
      processed = true;
      break;
  }

  return processed;
};

PanelManager.prototype._addDefaultWidgets = function(panel, dataSourceType) {
  var i;
  var dataSourceId = '';
  var widgets = [];

  for(i = 0; i < dataSourceManager.dataSources.length; i++) {
    if(dataSourceManager.dataSources[i].type === dataSourceType) {
      dataSourceId = dataSourceManager.dataSources[i].id;
      break;
    }
  }

  widgets = DataSource.getDefaultPanels(dataSourceType, panel.id, dataSourceId, panel.defaultDataElement);

  for(i = 0; i < widgets.length; i++) {
    widgetManager.addWidget(widgets[i]);
  }
};

PanelManager.prototype._receivedAdd = function(server, panel) {
  this._addToDom(panel, this.addMode);

  switch(panel.style) {
    case Panel.STYLE_AGILE:
      this._addDefaultWidgets(panel, DataSource.TYPE_VERSION_ONE);
      break;

    case Panel.STYLE_CI:
      this._addDefaultWidgets(panel, DataSource.TYPE_JENKINS);
      break;
  }

  this.dragAndDrop.apply();

  if(this.addMode) {
    this.dragAndDrop.enable();
  }
  displayStatusMessage("The panel has been added", "Panels");
};

PanelManager.prototype._receivedDelete = function(server, id) {
  this._deleteFromDom(id);
  displayStatusMessage("The panel has been removed", "Panels");
};

PanelManager.prototype._receivedMove = function(server, content) {
  this._moveOnDom(content);
  displayStatusMessage("The panel move has been recorded", "Panels");
};

PanelManager.prototype._receivedMoveUp = function(server, id) {
  this._moveUpOnDom(id);
};

PanelManager.prototype._receivedMoveDown = function(server, id) {
  this._moveDownOnDom(id);
};

PanelManager.prototype._receivedMoveLeft = function(server, panel) {
  this._changeColumnOnDom(panel);
};

PanelManager.prototype._receivedMoveRight = function(server, panel) {
  this._changeColumnOnDom(panel);
};

PanelManager.prototype._receivedUpdate = function(server, panel) {
  // get current data
  var currentPanelElement = $('#panel' + panel.id);

  var currentPanel = new Panel(currentPanelElement.data());

  // apply changes to DOM
  if(currentPanel.name !== panel.name) {
    $('#panelName' + panel.id).text(panel.name);
    currentPanelElement.data('name', panel.name);
  }

// ignore this for now, presume that only via move left/right that this can happen
//  if(currentPanel.column !== panel.column) {
//
//  }

  if(currentPanel.iconName !== panel.iconName) {
    $('#panelIcon' + panel.id).removeClass(currentPanel.iconName).addClass(panel.iconName);
    currentPanelElement.data('icon-name', panel.iconName);
  }
};

PanelManager.prototype.preferencesChanged = function(systemPreferences) {
  var column;

  if(this.columns === systemPreferences.columns) {
    return;
  }

  if(this.columns > systemPreferences.columns) {
    this._removeColumnsOnDom(this.columns, systemPreferences.columns);
  } else {
    this._updateColumnsOnDom(this.columns, systemPreferences.columns);

    this.dragAndDrop.apply();
  }

  this.columns = systemPreferences.columns;
};

PanelManager.prototype._receivedContent = function(server, d) {
  var panelsArray = [];
  var item;
  var column;

  // remove content from home pane
  $("#panelsPane").empty();

  // add columns as defined in preferences
  this.columns = preferenceManager.getSystemPreference().columns;

  for (column = 1; column <= this.columns; column++) {
    this._addColumnToDom(column, this.columns);
  }

  for (item in d) {
   panelsArray.push(new Panel(d[item]));
  }

  panelsArray.sort(function (a, b) {
      if (a.row  b.row) {
          return 1;
      }
      return 0;
  });

  panelsArray.forEach(function (p) {
    this._addToDom(p, this.addMode);
  }.bind(this));

  this.dragAndDrop.apply();
};

PanelManager.prototype._addColumnToDom = function(column, columns) {
  var columnClass = this._getColumnClass(columns);

  $("#panelsPane").append('

');
};

PanelManager.prototype._updateColumnsOnDom = function(oldColumnCount, newColumnCount) {
  var column = 0;
  var oldColumnClass = this._getColumnClass(oldColumnCount);
  var newColumnClass = this._getColumnClass(newColumnCount);

  var selection = $("#panelsPane ." + oldColumnClass);
  selection.removeClass(oldColumnClass);
  selection.addClass(newColumnClass);

  for(column = oldColumnCount + 1; column <= newColumnCount; column++) {
    $("#panelsPane").append('

');
  }
};

PanelManager.prototype._removeColumnsOnDom = function(oldColumnCount, newColumnCount) {
  for(column = oldColumnCount; column > newColumnCount; column--) {
    $("#column" + column).remove();
  }

  var oldColumnClass = this._getColumnClass(oldColumnCount);
  var newColumnClass = this._getColumnClass(newColumnCount);

  var selection = $("#panelsPane ." + oldColumnClass);
  selection.removeClass(oldColumnClass);
  selection.addClass(newColumnClass);
};

PanelManager.prototype._getColumnClass = function(columns) {
  var columnClass;

  switch(columns) {
    case 1:
      columnClass = "col-md-12";
      break;

    case 2:
      columnClass = "col-md-6";
      break;

    case 3:
      columnClass = "col-md-4";
      break;

    case 4:
      columnClass = "col-md-3";
      break;

    default:
      columnClass = "col-md-12";
  }

  return columnClass;
};

PanelManager.prototype._sendAdd = function(panel) {
  this.server.sendMessage(Panel.MESSAGE_ADD_PANEL, panel);
};

PanelManager.prototype._sendDelete = function(id) {
  this.server.sendMessage(Panel.MESSAGE_DELETE_PANEL, id);
};

PanelManager.prototype._sendUpdate = function(panel) {
  this.server.sendMessage(Panel.MESSAGE_UPDATE_PANEL, panel);
};

PanelManager.prototype.sendMove = function(panelId, sourceColumn, targetColumn, row) {
  this.server.sendMessage(Panel.MESSAGE_MOVE_PANEL, {
    id: panelId,
    sourceColumn: sourceColumn,
    targetColumn: targetColumn,
    row: row
  });
};

PanelManager.prototype._addToDom = function(panel, buttonsVisible) {
  var displayButtonClass = buttonsVisible ? "displayButton" : "";
  var displayStyle = buttonsVisible ? "block" : "none";

  console.log("adding panel: " + panel.column + " : " + this.columns);
  var panelClass = "panel";

  if(this.columns === 1) {
    panelClass += " panel-left panel-left";
  } else if(panel.column === 1) {
    panelClass += " panel-left";
  } else if(panel.column === this.columns) {
    panelClass += " panel-right";
  }

  //$("#columnContainer" + panel.column).append(
  $("#column" + panel.column).append(
    '
' +
    '
' +
    '
' +
    '        <button id="panelBtnAddWidget' + panel.id + '" type="button" class="config-buttons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnDelete' + panel.id + '" type="button" class="config-buttons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '        <button id="panelBtnModify' + panel.id + '" type="button" class="config-buttons btn btn-default btn-sml ' + displayButtonClass + '" style="display: ' + displayStyle + ';">' +
    '          ' +
    '        </button>' +
    '
' +
    '     ' + panel.name + '' +
    '
' +
    '
' +
    '
' +
    '
'
  );

  $('#panel' + panel.id).hide().fadeIn('slow');

  $('#panelBtnAddWidget' + panel.id).on('click', function() {
    viewWidgetWizard('add', {}, panel.id);
  }.bind(this));

  $('#panelBtnDelete' + panel.id).on('click', function() {
    this._sendDelete(panel.id);
  }.bind(this));

  $('#panelBtnModify' + panel.id).on('click', function() {
    displayPanelDialog('update', panel);
  }.bind(this));
};

PanelManager.prototype._deleteFromDom = function(id) {
  $('#panel' + id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "slow", queue: false})
    .fadeOut('slow', function() {
    this.remove();
  });
};

PanelManager.prototype._moveOnDom = function(content) {
  // TODO: only update if a timer expires before update panel response is received
  // this is due to flickering if we cancel the drag event and wait for backend to confirm

//  content.panelId
//  content.sourceColumn
//  content.targetColumn
//  var row = content.row;
//
//  if(row > 0) {
//    // determine appropriate entry
//    var rows = $("#" + "column" + content.targetColumn + ">div" + ".panel");
//
//    if(content.sourceColumn !== content.targetColumn) {
//      // we want the panel before the drop point, for different row this requires adjustment
//      row--;
//    }
//
//    // add after the correct position
//    $('#panel' + content.id).insertAfter("#" + rows[row].id);
//  } else {
//    $('#panel' + content.id).prependTo("#" + "column" + content.targetColumn);
//  }
};

PanelManager.prototype._moveDownOnDom = function(id) {
  var panelList = this._getList(id);

  if(panelList['panel' + id].position < (Object.keys(panelList).length - 1)) {
    // move down
    this._swapVertically(panelList['panel' + id].id, panelList['panel' + id].nextId);
  }
};

PanelManager.prototype._moveUpOnDom = function(id) {
  var panelList = this._getList(id);

  if(panelList['panel' + id].position > 0) {
    // move up
    this._swapVertically(panelList['panel' + id].id, panelList['panel' + id].prevId);
  }
};

PanelManager.prototype._changeColumnOnDom = function(panelData) {
  var panel = $('#panel' + panelData.id);

  $('#panel' + panelData.id)
    .animate({height: 0, padding: 0, margin: 0}, {easing: "linear", duration: "fast", queue: false})
    .fadeOut('fast', function() {
      panel
        .appendTo("#columnContainer" + panelData.column)
        .hide()
        .css('height', '')
        .css('padding', '')
        .css('margin', '')
        .fadeIn('fast');

  });
};

PanelManager.prototype._getList = function(id) {
  var prevPanel = null;
  var nextPanel = null;
  var counter = 0;
  var panelList = {};

  $('#panel' + id).parent().children().each(function() {
    panelList[$(this).attr('id')] = {
      id: $(this).attr('id'),
      position: counter++,
      value: this,
      prevId: null,
      nextId: null
    };

    if(prevPanel !== null) {
      panelList[$(this).attr('id')].prevId = prevPanel.id;
      prevPanel.nextId = $(this).attr('id');
    }

    prevPanel = panelList[$(this).attr('id')];
  });

  return panelList;
};

PanelManager.prototype._swapVertically = function(a, b){
    a = $('#' + a)[0];
    b = $('#' + b)[0];

    var t = a.parentNode.insertBefore(document.createTextNode(''), a);

    b.parentNode.insertBefore(a, b);
    t.parentNode.insertBefore(b, t);
    t.parentNode.removeChild(t);
};

<|endoftext|>


<commit_before>/**
 * tracingtool.js
 *
 * requirements:
 *	 tools.js
 *	 ui.js
 *	 slider.js
 *   stack.js
 */

/**
 */

/**
 * Constructor for the tracing tool.
 */
function TracingTool()
{
  this.prototype = new Navigator();

  var self = this;
  var tracingLayer = null;
  var stack = null;
  var bindings = {};

	this.resize = function( width, height )
	{
        self.prototype.resize( width, height );
		return;
	}

  var setupSubTools = function()
  {
    // TODO: replace with project.js strings
    if ( self.prototype.stack == null ) {
      var box = $( '

' );
      [ { name : "skeleton", alt : "skeleton" },
        { name : "synapse", alt : "synapse" },
        { name : "goactive", alt : "go to active element" },
        { name : "skelsplitting", alt : "split skeleton" },
        { name : "skelrerooting", alt : "reroot skeleton" },
        { name : "togglelabels", alt : "toggle labels" },
        { name : "3dview", alt : "3d view" } ].map(
        function( button ) {
          var a = document.createElement('a');
          a.setAttribute('class', 'button');
          a.setAttribute('id', 'trace_button_' + button.name);
          a.onclick = function( e ) {
            tracingLayer.svgOverlay.tracingCommand(button.name);
            return false;
          };
          var img = document.createElement('img');
          img.setAttribute('title', button.alt);
          img.setAttribute('alt', button.alt);
          img.setAttribute('src', 'widgets/themes/kde/trace_' + button.name + '.png');
          a.appendChild(img);
          box.append(a);
        }
      );
      $( "#toolbar_nav" ).prepend( box );
    }
  }

  var createTracingLayer = function( parentStack )
  {
    stack = parentStack;
    tracingLayer = new TracingLayer( parentStack );
    //this.prototype.mouseCatcher = tracingLayer.svgOverlay.getView();
    self.prototype.setMouseCatcher( tracingLayer.svgOverlay.view );
    parentStack.addLayer( "TracingLayer", tracingLayer );

    // Call register AFTER changing the mouseCatcher
    self.prototype.register( parentStack, "edit_button_trace" );

    // NOW set the mode TODO cleanup this initialization problem
    tracingLayer.svgOverlay.set_tracing_mode( "skeletontracing" );
    tracingLayer.svgOverlay.updateNodes();

    // view is the mouseCatcher now
    var view = tracingLayer.svgOverlay.view;

    var proto_onmousedown = view.onmousedown;
    view.onmousedown = function( e ) {
      switch ( ui.getMouseButton( e ) )
      {
        case 1:
          tracingLayer.svgOverlay.whenclicked( e );
          break;
        case 2:
          proto_onmousedown( e );
          ui.registerEvent( "onmousemove", updateStatusBar );
          ui.registerEvent( "onmouseup",
            function onmouseup (e) {
              ui.releaseEvents();
              ui.removeEvent( "onmousemove", updateStatusBar );
              ui.removeEvent( "onmouseup", onmouseup );
              // Recreate nodes by feching them from the database for the new field of view
              tracingLayer.svgOverlay.updateNodes();
            });
          break;
        default:
          proto_onmousedown( e );
          break;
      }
      return;
    };

    var proto_changeSlice = self.prototype.changeSlice;
    self.prototype.changeSlice =
      function( val ) {
        proto_changeSlice( val );
        tracingLayer.svgOverlay.updateNodes();
      };
  }

	/**
	 * install this tool in a stack.
	 * register all GUI control elements and event handlers
	 */
	this.register = function( parentStack )
	{
    setupSubTools();

    if (tracingLayer && stack) {
      if (stack !== parentStack) {
        // If the tracing layer exists and it belongs to a different stack, replace it
        stack.removeLayer( tracingLayer );
        createTracingLayer( parentStack );
      } else {
        reactivateBindings();
      }
    } else {
      createTracingLayer( parentStack );
    }

    return;
  }

  /** Inactivate only onmousedown, given that the others are injected when onmousedown is called.
   * Leave alone onmousewheel: it is different in every browser, and it cannot do any harm to have it active. */
  var inactivateBindings = function() {
    var c = self.prototype.mouseCatcher;
    ['onmousedown'].map(
      function ( fn ) {
        if (c[fn]) {
          bindings[fn] = c[fn];
          delete c[fn];
        }
      });
  }

  var reactivateBindings = function() {
    var c = self.prototype.mouseCatcher;
    for (var b in bindings) {
      if (bindings.hasOwnProperty(b)) {
        c[b.name] = b;
      }
    }
  };

	/**
	 * unregister all stack related mouse and keyboard controls
	 */
	this.unregister = function()
	{
    // do it before calling the prototype destroy that sets stack to null
    if (self.prototype.stack) {
      inactivateBindings();
    }
    // Do NOT unregister: would remove the mouseCatcher layer
    // and the annotations would disappear
    //self.prototype.unregister();
    return;
	}

	/**
	 * unregister all project related GUI control connections and event
	 * handlers, toggle off tool activity signals (like buttons)
	 */
	this.destroy = function()
	{
    // Synchronize data with database
    tracingLayer.svgOverlay.updateNodeCoordinatesinDB();

    // the prototype destroy calls the prototype's unregister, not self.unregister
    // do it before calling the prototype destroy that sets stack to null
    self.prototype.stack.removeLayer( "TracingLayer" );
    self.prototype.destroy( "edit_button_trace" );
    $( "#tracingbuttons" ).remove();
    tracingLayer.svgOverlay.destroy();
    //
    for (var b in bindings) {
      if (bindings.hasOwnProperty(b)) {
        delete bindings[b];
      }
    }
    return;
	};

  var updateStatusBar = function( e ) {
    var m = ui.getMouse(e, true);
    var offX, offY, pos_x, pos_y;
    if (m) {
      // add right move of svgOverlay to the m.offsetX
      offX = m.offsetX + tracingLayer.svgOverlay.offleft;
      // add down move of svgOverlay to the m.offsetY
      offY = m.offsetY + tracingLayer.svgOverlay.offtop;

      // TODO pos_x and pos_y never change
      pos_x = stack.translation.x + (stack.x + (offX - stack.viewWidth / 2) / stack.scale) * stack.resolution.x;
      pos_y = stack.translation.x + (stack.y + (offY - stack.viewHeight / 2) / stack.scale) * stack.resolution.y;
      statusBar.replaceLast("[" + pos_x.toFixed(3) + ", " + pos_y.toFixed(3) + "]" + " stack.x,y: " + stack.x + ", " + stack.y);
    }
    return true;
  };

    var actions = [];

    this.addAction = function ( action ) {
	actions.push( action );
    }

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

    var arrowKeyCodes = {
	left: 37,
	up: 38,
	right: 39,
	down: 40
    };

    this.addAction( new Action({
	helpText: "Zoom in",
	keyShortcuts: {
	    '+': [ 43, 107, 61, 187 ]
	},
	run: function (e) {
	    slider_s.move(1);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Zoom out",
	keyShortcuts: {
	    '-': [ 45, 109, 189 ]
	},
	run: function (e) {
	    slider_s.move(-1);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move up 1 slice in z (or 10 with Shift held)",
	keyShortcuts: {
	    ',': [ 44, 188 ]
	},
	run: function (e) {
	    slider_z.move(-(e.shiftKey ? 10 : 1));
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move down 1 slice in z (or 10 with Shift held)",
	keyShortcuts: {
	    '.': [ 46, 190 ]
	},
	run: function (e) {
	    slider_z.move((e.shiftKey ? 10 : 1));
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move left (towards negative x)",
	keyShortcuts: {
	    "\u2190": [ arrowKeyCodes.left ]
	},
	run: function (e) {
	    input_x.value = parseInt(input_x.value, 10) - (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_x.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move right (towards positive x)",
	keyShortcuts: {
	    "\u2192": [ arrowKeyCodes.right ],
	},
	run: function (e) {
	    input_x.value = parseInt(input_x.value, 10) + (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_x.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move up (towards negative y)",
	keyShortcuts: {
	    "\u2191": [ arrowKeyCodes.up ]
	},
	run: function (e) {
	    input_y.value = parseInt(input_y.value, 10) - (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_y.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move down (towards positive y)",
	keyShortcuts: {
	    "\u2193": [ arrowKeyCodes.down ]
	},
	run: function (e) {
	    input_y.value = parseInt(input_y.value, 10) + (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_y.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Go to active node",
	buttonID: [ 'trace_button_goactive' ],
	keyShortcuts: {
	    "A": [ 65 ]
	},
	run: function (e) {
	    project.tracingCommand('goactive');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Deselect the active node",
	keyShortcuts:  {
	    "M": [ 77 ]
	},
	run: function (e) {
	    activateNode(null);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Go to the parent of the active node (?)",
	keyShortcuts: {
	    "P": [ 80 ]
	},
	run: function (e) {
	    project.tracingCommand('goparent');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Go to last edited node in this skeleton",
	keyShortcuts: {
	    "E": [ 69 ]
	},
	run: function (e) {
	    project.tracingCommand('golastedited');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Split this skeleton at the active node",
	buttonID: [ 'trace_button_skelsplitting' ],
	keyShortcuts: {
	    "5": [ 53 ]
	},
	run: function (e) {
	    project.tracingCommand('skeletonsplitting');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Re-root this skeleton at the active node",
	buttonID: [ 'trace_button_skelrerooting' ],
	keyShortcuts: {
	    "6": [ 54 ]
	},
	run: function (e) {
	    project.tracingCommand('skeletonreroot');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Toggle the display of labels",
	buttonID: [ 'trace_button_togglelabels' ],
	keyShortcuts: {
	    "7": [ 55 ]
	},
	run: function (e) {
	    project.tracingCommand('togglelabels');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Export to SWC",
	buttonID: [ 'trace_button_exportswc' ],
	keyShortcuts: {
	    "S": [ 83 ]
	},
	run: function (e) {
	    project.tracingCommand('exportswc');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Tag the active node",
	keyShortcuts: {
	    "T": [ 84 ]
	},
	run: function (e) {
	    if (!(e.ctrlKey || e.metaKey)) {
		project.tracingCommand('tagging');
	    }
	    return true;
	}
    }) );

    this.addAction( new Action({
	helpText: "Select the nearest node to the mouse cursor",
	keyShortcuts: {
	    "G": [ 71 ]
	},
	run: function (e) {
	    if (!(e.ctrlKey || e.metaKey)) {
		project.activateNearestNode();
	    }
	    return true;
	}
    }) );

    var keyCodeToAction = getKeyCodeToActionMap(actions);

    setButtonClicksFromActions(actions);

    /** This function should return true if there was any action
        linked to the key code, or false otherwise. */

    this.handleKeyPress = function( e ) {
        var keyAction = keyCodeToAction[e.keyCode];
        if (keyAction) {
	    keyAction.run(e || event);
	    return true;
	} else {
            return false;
	}
    }

}
<commit_msg>A first attempt at getting the 'go to active node' keypress to work<commit_after>/**
 * tracingtool.js
 *
 * requirements:
 *	 tools.js
 *	 ui.js
 *	 slider.js
 *   stack.js
 */

/**
 */

/**
 * Constructor for the tracing tool.
 */
function TracingTool()
{
  this.prototype = new Navigator();

  var self = this;
  var tracingLayer = null;
  var stack = null;
  var bindings = {};

	this.resize = function( width, height )
	{
        self.prototype.resize( width, height );
		return;
	}

  var setupSubTools = function()
  {
    // TODO: replace with project.js strings
    if ( self.prototype.stack == null ) {
      var box = $( '

' );
      [ { name : "skeleton", alt : "skeleton" },
        { name : "synapse", alt : "synapse" },
        { name : "goactive", alt : "go to active element" },
        { name : "skelsplitting", alt : "split skeleton" },
        { name : "skelrerooting", alt : "reroot skeleton" },
        { name : "togglelabels", alt : "toggle labels" },
        { name : "3dview", alt : "3d view" } ].map(
        function( button ) {
          var a = document.createElement('a');
          a.setAttribute('class', 'button');
          a.setAttribute('id', 'trace_button_' + button.name);
          a.onclick = function( e ) {
            tracingLayer.svgOverlay.tracingCommand(button.name);
            return false;
          };
          var img = document.createElement('img');
          img.setAttribute('title', button.alt);
          img.setAttribute('alt', button.alt);
          img.setAttribute('src', 'widgets/themes/kde/trace_' + button.name + '.png');
          a.appendChild(img);
          box.append(a);
        }
      );
      $( "#toolbar_nav" ).prepend( box );
    }
  }

  var createTracingLayer = function( parentStack )
  {
    stack = parentStack;
    tracingLayer = new TracingLayer( parentStack );
    //this.prototype.mouseCatcher = tracingLayer.svgOverlay.getView();
    self.prototype.setMouseCatcher( tracingLayer.svgOverlay.view );
    parentStack.addLayer( "TracingLayer", tracingLayer );

    // Call register AFTER changing the mouseCatcher
    self.prototype.register( parentStack, "edit_button_trace" );

    // NOW set the mode TODO cleanup this initialization problem
    tracingLayer.svgOverlay.set_tracing_mode( "skeletontracing" );
    tracingLayer.svgOverlay.updateNodes();

    // view is the mouseCatcher now
    var view = tracingLayer.svgOverlay.view;

    var proto_onmousedown = view.onmousedown;
    view.onmousedown = function( e ) {
      switch ( ui.getMouseButton( e ) )
      {
        case 1:
          tracingLayer.svgOverlay.whenclicked( e );
          break;
        case 2:
          proto_onmousedown( e );
          ui.registerEvent( "onmousemove", updateStatusBar );
          ui.registerEvent( "onmouseup",
            function onmouseup (e) {
              ui.releaseEvents();
              ui.removeEvent( "onmousemove", updateStatusBar );
              ui.removeEvent( "onmouseup", onmouseup );
              // Recreate nodes by feching them from the database for the new field of view
              tracingLayer.svgOverlay.updateNodes();
            });
          break;
        default:
          proto_onmousedown( e );
          break;
      }
      return;
    };

    var proto_changeSlice = self.prototype.changeSlice;
    self.prototype.changeSlice =
      function( val ) {
        proto_changeSlice( val );
        tracingLayer.svgOverlay.updateNodes();
      };
  }

	/**
	 * install this tool in a stack.
	 * register all GUI control elements and event handlers
	 */
	this.register = function( parentStack )
	{
    setupSubTools();

    if (tracingLayer && stack) {
      if (stack !== parentStack) {
        // If the tracing layer exists and it belongs to a different stack, replace it
        stack.removeLayer( tracingLayer );
        createTracingLayer( parentStack );
      } else {
        reactivateBindings();
      }
    } else {
      createTracingLayer( parentStack );
    }

    return;
  }

  /** Inactivate only onmousedown, given that the others are injected when onmousedown is called.
   * Leave alone onmousewheel: it is different in every browser, and it cannot do any harm to have it active. */
  var inactivateBindings = function() {
    var c = self.prototype.mouseCatcher;
    ['onmousedown'].map(
      function ( fn ) {
        if (c[fn]) {
          bindings[fn] = c[fn];
          delete c[fn];
        }
      });
  }

  var reactivateBindings = function() {
    var c = self.prototype.mouseCatcher;
    for (var b in bindings) {
      if (bindings.hasOwnProperty(b)) {
        c[b.name] = b;
      }
    }
  };

	/**
	 * unregister all stack related mouse and keyboard controls
	 */
	this.unregister = function()
	{
    // do it before calling the prototype destroy that sets stack to null
    if (self.prototype.stack) {
      inactivateBindings();
    }
    // Do NOT unregister: would remove the mouseCatcher layer
    // and the annotations would disappear
    //self.prototype.unregister();
    return;
	}

	/**
	 * unregister all project related GUI control connections and event
	 * handlers, toggle off tool activity signals (like buttons)
	 */
	this.destroy = function()
	{
    // Synchronize data with database
    tracingLayer.svgOverlay.updateNodeCoordinatesinDB();

    // the prototype destroy calls the prototype's unregister, not self.unregister
    // do it before calling the prototype destroy that sets stack to null
    self.prototype.stack.removeLayer( "TracingLayer" );
    self.prototype.destroy( "edit_button_trace" );
    $( "#tracingbuttons" ).remove();
    tracingLayer.svgOverlay.destroy();
    //
    for (var b in bindings) {
      if (bindings.hasOwnProperty(b)) {
        delete bindings[b];
      }
    }
    return;
	};

  var updateStatusBar = function( e ) {
    var m = ui.getMouse(e, true);
    var offX, offY, pos_x, pos_y;
    if (m) {
      // add right move of svgOverlay to the m.offsetX
      offX = m.offsetX + tracingLayer.svgOverlay.offleft;
      // add down move of svgOverlay to the m.offsetY
      offY = m.offsetY + tracingLayer.svgOverlay.offtop;

      // TODO pos_x and pos_y never change
      pos_x = stack.translation.x + (stack.x + (offX - stack.viewWidth / 2) / stack.scale) * stack.resolution.x;
      pos_y = stack.translation.x + (stack.y + (offY - stack.viewHeight / 2) / stack.scale) * stack.resolution.y;
      statusBar.replaceLast("[" + pos_x.toFixed(3) + ", " + pos_y.toFixed(3) + "]" + " stack.x,y: " + stack.x + ", " + stack.y);
    }
    return true;
  };

    var actions = [];

    this.addAction = function ( action ) {
	actions.push( action );
    }

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

    var arrowKeyCodes = {
	left: 37,
	up: 38,
	right: 39,
	down: 40
    };

    this.addAction( new Action({
	helpText: "Zoom in",
	keyShortcuts: {
	    '+': [ 43, 107, 61, 187 ]
	},
	run: function (e) {
	    slider_s.move(1);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Zoom out",
	keyShortcuts: {
	    '-': [ 45, 109, 189 ]
	},
	run: function (e) {
	    slider_s.move(-1);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move up 1 slice in z (or 10 with Shift held)",
	keyShortcuts: {
	    ',': [ 44, 188 ]
	},
	run: function (e) {
	    slider_z.move(-(e.shiftKey ? 10 : 1));
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move down 1 slice in z (or 10 with Shift held)",
	keyShortcuts: {
	    '.': [ 46, 190 ]
	},
	run: function (e) {
	    slider_z.move((e.shiftKey ? 10 : 1));
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move left (towards negative x)",
	keyShortcuts: {
	    "\u2190": [ arrowKeyCodes.left ]
	},
	run: function (e) {
	    input_x.value = parseInt(input_x.value, 10) - (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_x.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move right (towards positive x)",
	keyShortcuts: {
	    "\u2192": [ arrowKeyCodes.right ],
	},
	run: function (e) {
	    input_x.value = parseInt(input_x.value, 10) + (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_x.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move up (towards negative y)",
	keyShortcuts: {
	    "\u2191": [ arrowKeyCodes.up ]
	},
	run: function (e) {
	    input_y.value = parseInt(input_y.value, 10) - (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_y.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Move down (towards positive y)",
	keyShortcuts: {
	    "\u2193": [ arrowKeyCodes.down ]
	},
	run: function (e) {
	    input_y.value = parseInt(input_y.value, 10) + (e.shiftKey ? 100 : (e.altKey ? 1 : 10));
	    input_y.onchange(e);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Go to active node",
	buttonID: [ 'trace_button_goactive' ],
	keyShortcuts: {
	    "A": [ 65 ]
	},
	run: function (e) {
	    var atn = SkeletonAnnotations.getActiveNode();
	    if (atn != null) {
		project.moveTo(
		    tracingLayer.svgOverlay.pix2physZ(atn.z),
		    tracingLayer.svgOverlay.pix2physY(atn.y),
		    tracingLayer.svgOverlay.pix2physX(atn.x));
	    } else {
		alert("No active node to go to!");
	    }
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Deselect the active node",
	keyShortcuts:  {
	    "M": [ 77 ]
	},
	run: function (e) {
	    activateNode(null);
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Go to the parent of the active node (?)",
	keyShortcuts: {
	    "P": [ 80 ]
	},
	run: function (e) {
	    project.tracingCommand('goparent');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Go to last edited node in this skeleton",
	keyShortcuts: {
	    "E": [ 69 ]
	},
	run: function (e) {
	    project.tracingCommand('golastedited');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Split this skeleton at the active node",
	buttonID: [ 'trace_button_skelsplitting' ],
	keyShortcuts: {
	    "5": [ 53 ]
	},
	run: function (e) {
	    project.tracingCommand('skeletonsplitting');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Re-root this skeleton at the active node",
	buttonID: [ 'trace_button_skelrerooting' ],
	keyShortcuts: {
	    "6": [ 54 ]
	},
	run: function (e) {
	    project.tracingCommand('skeletonreroot');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Toggle the display of labels",
	buttonID: [ 'trace_button_togglelabels' ],
	keyShortcuts: {
	    "7": [ 55 ]
	},
	run: function (e) {
	    project.tracingCommand('togglelabels');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Export to SWC",
	buttonID: [ 'trace_button_exportswc' ],
	keyShortcuts: {
	    "S": [ 83 ]
	},
	run: function (e) {
	    project.tracingCommand('exportswc');
	    return false;
	}
    }) );

    this.addAction( new Action({
	helpText: "Tag the active node",
	keyShortcuts: {
	    "T": [ 84 ]
	},
	run: function (e) {
	    if (!(e.ctrlKey || e.metaKey)) {
		project.tracingCommand('tagging');
	    }
	    return true;
	}
    }) );

    this.addAction( new Action({
	helpText: "Select the nearest node to the mouse cursor",
	keyShortcuts: {
	    "G": [ 71 ]
	},
	run: function (e) {
	    if (!(e.ctrlKey || e.metaKey)) {
		project.activateNearestNode();
	    }
	    return true;
	}
    }) );

    var keyCodeToAction = getKeyCodeToActionMap(actions);

    setButtonClicksFromActions(actions);

    /** This function should return true if there was any action
        linked to the key code, or false otherwise. */

    this.handleKeyPress = function( e ) {
        var keyAction = keyCodeToAction[e.keyCode];
        if (keyAction) {
	    keyAction.run(e || event);
	    return true;
	} else {
            return false;
	}
    }

}
<|endoftext|>


<commit_before>function convertToDate(timeStamp) {
		var ts = String(timeStamp);
		// Format: YYYY-MM-DD HH:MM:SS
		var string_split = ts.split(" ");
		var date_part = string_split[0].split("-");
		var time_part = string_split[1].split(":");
		return new Date(date_part[0], date_part[1], date_part[2], time_part[0], time_part[1], time_part[2]);
}

function drawViewersChart(rowData, gameShortName) {
		console.log("Drawing viewers chart for: " + gameShortName);
		// Create the data table
		var data = new google.visualization.DataTable();
		data.addColumn('string', 'name');
		data.addColumn('number', 'viewers');
		data.addRows(rowData);
		var columnWidth = $('#' + gameShortName + '-followers-graph').width();
		// Set options
		var options = {
				title: 'Average Viewers over time',
				width: columnWidth,
				height: 400
		};

		// Instantiate and draw the chart
		var chart = new google.visualization.LineChart(document.getElementById(gameShortName + '-average-viewers-graph'));
		chart.draw(data, options);
}

function drawFollowersChart(rowData, gameShortName) {
		console.log("Drawing followers chart for: " + gameShortName);
		// Create the data table
		var data = new google.visualization.DataTable();
		data.addColumn('string', 'Topping');
		data.addColumn('number', 'Slices');
		data.addRows(rowData);
		var columnWidth = $('#' + gameShortName + '-followers-graph').width();
		// Set options
			var options = {
					title: 'Followers over time',
					width: columnWidth,
					height: 400
			};

		// Instantiate and draw the chart
		var chart = new google.visualization.LineChart(document.getElementById(gameShortName + '-followers-graph'));
		chart.draw(data, options);
}

// Draw all the charts
function drawCharts(streamer_name) {
		// Make an AJAX call to get the data from the API
		$.ajax({
				url: "/api/v1/streamer/" + streamer_name,
				success: function(data) {
						jsonData = JSON.parse(data);
						console.log("Data received from API:");
						console.log(jsonData);
						for (var key in jsonData) {
								if (key == 'streamer_name') {
										continue;
								}
								if (!$.isEmptyObject(jsonData[key])) {
										drawViewersChart(jsonData[key]['viewers_average'], key);
										drawFollowersChart(jsonData[key]['followers'], key);
								}
						}
				}
		});
}

function loadCharts(streamer_name) {
		// Load packages required
		google.charts.load('current', {'packages':['corechart', 'line']});
		// Set Callbacks
		google.charts.setOnLoadCallback(function() {
				drawCharts(streamer_name);
		});
}
<commit_msg>Fixed wrong label<commit_after>function convertToDate(timeStamp) {
		var ts = String(timeStamp);
		// Format: YYYY-MM-DD HH:MM:SS
		var string_split = ts.split(" ");
		var date_part = string_split[0].split("-");
		var time_part = string_split[1].split(":");
		return new Date(date_part[0], date_part[1], date_part[2], time_part[0], time_part[1], time_part[2]);
}

function drawViewersChart(rowData, gameShortName) {
		console.log("Drawing viewers chart for: " + gameShortName);
		// Create the data table
		var data = new google.visualization.DataTable();
		data.addColumn('string', 'name');
		data.addColumn('number', 'viewers');
		data.addRows(rowData);
		var columnWidth = $('#' + gameShortName + '-followers-graph').width();
		// Set options
		var options = {
				title: 'Average Viewers over time',
				width: columnWidth,
				height: 400
		};

		// Instantiate and draw the chart
		var chart = new google.visualization.LineChart(document.getElementById(gameShortName + '-average-viewers-graph'));
		chart.draw(data, options);
}

function drawFollowersChart(rowData, gameShortName) {
		console.log("Drawing followers chart for: " + gameShortName);
		// Create the data table
		var data = new google.visualization.DataTable();
		data.addColumn('string', 'Topping');
		data.addColumn('number', 'followers');
		data.addRows(rowData);
		var columnWidth = $('#' + gameShortName + '-followers-graph').width();
		// Set options
			var options = {
					title: 'Followers over time',
					width: columnWidth,
					height: 400
			};

		// Instantiate and draw the chart
		var chart = new google.visualization.LineChart(document.getElementById(gameShortName + '-followers-graph'));
		chart.draw(data, options);
}

// Draw all the charts
function drawCharts(streamer_name) {
		// Make an AJAX call to get the data from the API
		$.ajax({
				url: "/api/v1/streamer/" + streamer_name,
				success: function(data) {
						jsonData = JSON.parse(data);
						console.log("Data received from API:");
						console.log(jsonData);
						for (var key in jsonData) {
								if (key == 'streamer_name') {
										continue;
								}
								if (!$.isEmptyObject(jsonData[key])) {
										drawViewersChart(jsonData[key]['viewers_average'], key);
										drawFollowersChart(jsonData[key]['followers'], key);
								}
						}
				}
		});
}

function loadCharts(streamer_name) {
		// Load packages required
		google.charts.load('current', {'packages':['corechart', 'line']});
		// Set Callbacks
		google.charts.setOnLoadCallback(function() {
				drawCharts(streamer_name);
		});
}
<|endoftext|>