<commit_before>var PT = PT || {};

PT.colors = [
  "#66c6ba",
  "#f5be59", 
  "#ea7669", 
  "#545757", 
  "#42847b", 
  "#efa9a3",
  "#e6e7e8",
  "#000000"
];

// Aggregate data

PT.getSurveyInputById = function(surveyDefinition, inputId) {
  for(var i = 0; i< surveyDefinition.inputs.length; i++) {
    if(surveyDefinition.inputs[i].id == inputId) return surveyDefinition.inputs[i];
  }
};

PT.getAnswerById = function(response, inputId) {
  for(var i = 0; i< response.answers.length; i++) {
    if(response.answers[i].id == inputId) {
      return response.answers[i];
    }
  }
};

PT.getAnswersById = function(responses, inputId){
  var answers = [];
  for(var i = 0; i<responses.length; i++) {
    answers.push(PT.getAnswerById(responses[i], inputId).value);
  }

  return answers;
}

PT.count = function(responses, inputId) { //Count responses by value or date
  var counts = {};
  for(var i = 0; i< responses.length; i++) {
    var value;
    inputId ? value = $.trim(PT.getAnswerById(responses[i], inputId).value) : value = responses[i].timestamp;
    counts[value] = counts[value] ? counts[value]+1 : 1;
  }
  return counts;
};

PT.aggregateAnswers = function(responses, inputId){ //Collect answers for text and image
  var answers = [];

  if(inputId){
    responses.forEach(function(response){
      var relevantAnswer = response.answers.filter(function(answer){
        if(answer.id == inputId){
          if(answer.value && String(answer.value).length > 0){
            var item = answer;
            item.parentResponse = response;
            return item;
          }
        }
      })[0];

      relevantAnswer ? answers = PT.separateMultipleEntries(relevantAnswer, response, answers) : false;
    });
  } else {
    answers = PT.extractAllImages(responses);
  }

  return answers;
};

PT.separateMultipleEntries = function(answer, response, answersArray){
  var formatAnswer = function(value){
    return {
      value: value,
      parentResponse: response,
      type: answer.input_type
    }
  };

  if(answer.value.constructor === Array){ // Support new multiple image answer
    answer.value.forEach(function(value){
      answersArray.push(formatAnswer(value));
    });
  } else { // Support old single image answer
    answersArray.push(formatAnswer(answer.value));
  }

  return answersArray;
};

PT.extractAllImages = function(responses){
  var images = [];

  responses.forEach(function(response){
    if(response.answers){
      response.answers.forEach(function(answer){
        if(answer.input_type == "image" && answer.value){
          PT.separateMultipleEntries(answer, response, images)
        }
      });
    }
  });

  return images;
};

// Render visualizations

PT.renderGoogleMap = function(responses, containerId){
  var markers = [];
  var surveyDefinition = {};
  var map = null,
      infoWindow = null;
  var $container;

  var attachMarkerClickEvent = function(marker, response){
    var markerInfo = {
      id: response.id,
      image: null,
      displayableAnswers: {}
    };

    // 1. Browse through answers, extract image and responses
    for(var i=0,len=response.answers.length;i<len;i++){
      var answer = response.answers[i];
      switch(answer.input_type){
        case "location":
          break;
        case "image":
          if(typeof answer.value!=="undefined" && answer.value){
            markerInfo.image = answer.value;
          }
          break;
        default:
          markerInfo.displayableAnswers[surveyDefinition[answer.id]] = answer.value || "";
      }
    }

    // 2. Build marker popup template and attach listener
    google.maps.event.addListener(marker, "click", function(){
      infoWindow.setContent(HandlebarsTemplates["map_info_window"](markerInfo));
      infoWindow.open(map, marker);
    });
  };

  window.mapInit = function(){
    // Start initialization, create map
    var mapOptions = {
      zoom: 8,
      center: new google.maps.LatLng(-34.397, 150.644),
      scrollwheel: false
    };
    map = new google.maps.Map(document.getElementById(containerId), mapOptions);
    infoWindow = new google.maps.InfoWindow({
        content: ""
    });

    // Store survey definition
    var surveyInputs = PT.surveyDefinition.inputs;
    for(var i=0,len=surveyInputs.length;i<len;i++){
      var input = surveyInputs[i];
      surveyDefinition[input.id] = input.label;
    }

    // Create map markers
    for(var i=0,len=responses.length;i<len;i++){
      var response = responses[i];

      // Find geo location
      var lat = null,
          lon = null;
      for(var j=0,len2=response.answers.length;j<len2;j++){
        var answer = response.answers[j];
        if(typeof answer.input_type!=="undefined" && answer.input_type=="location" && typeof answer.value!=="undefined" && typeof answer.value.lon!=="undefined"){
          lat = answer.value.lat;
          lon = answer.value.lon;
          break;
        }
      }
      if(lat==null || lon==null){
        if(response.locationstamp && response.locationstamp.lat !== "undefined" && response.locationstamp.long !== "undefined"){
          lat = response.locationstamp.lat;
          lon = response.locationstamp.lon;
        } else {
          continue; // Find if next response is a geolocation
        }
      }

      // Create marker
      if(lat && lon){
        var marker = new google.maps.Marker({
          map: map,
          position: new google.maps.LatLng(lat, lon)
        });
        markers.push(marker);

        attachMarkerClickEvent(marker, response);
      }
    }

    // Scale map viewport to include all markers
    var points = $.map(markers, function(a){
        return a.getPosition();
    });
    var bounds = new google.maps.LatLngBounds();
    for(var i=0;i<points.length;i++){
        bounds.extend(points[i]);
    }
    
    map.fitBounds(bounds);

    $("#map-tab").on("shown.bs.tab", function(){
      google.maps.event.trigger(map, "resize");
      map.fitBounds(bounds);
    });

  };

  // Load map script

  if(typeof google === "undefined"){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "https://maps.googleapis.com/maps/api/js?v=3.exp&" +
        "callback=mapInit&key=" + "<%= ENV['GOOGLE_MAPS_KEY'] %>";
    document.body.appendChild(script);
  } else {
    window.mapInit();
  }
};

PT.renderGallery = function(responses, containerId, galleryName, inputId){
  var $container = $(containerId);

  var populate = function(container, responses){
    var images = PT.aggregateAnswers(responses, inputId);
    $container.empty();

    if(images.length > 0){
      images.forEach(function(image, index){
        // Ensure no local image uploads are included
        if(image.value.slice(0,4) !== "file"){
          var $a = $("<a/>");
          var $image = $("<div/>", {class: "gallery-image"});

          $a.attr("href", image.value);
          $a.attr("data-lightbox", galleryName);
          $image.css("background", "url(" + image.value + ") no-repeat center center");
          $image.css("background-size", "cover");
          $a.append($image);
          $container.append($a);
        }
      });
    }
  };

  populate($container, responses);
  $container.on("update", function(event, newResponses){
    populate($container, newResponses);
  });
};

PT.renderText = function(containerId, responses, inputId){
  var $container = $(containerId);

  var populate = function(responseArray){
    var $textBlock;
    var answers = PT.aggregateAnswers(responseArray, inputId);
    
    $container.empty();
    answers.forEach(function(item, index){
      $textBlock = $("<span/>", {class: "text-viz"}).html(item.value);
      $textBlock.css("color", PT.colors[(index % PT.colors.length)]);
      $textBlock.css("font-size", Math.floor(Math.random(5) * 10 + 12) + "px");
      $container.append($textBlock);

      $textBlock.on("click", function(event){
        PT.showResponsePopup(event, item.parentResponse, PT.surveyDefinition);
      });
    });
  };

  populate(responses);
  $container.on("update", function(event, newResponses){
    populate(newResponses);
  });
};

PT.renderDate = function(containerId, responses, inputId) {
  var $container = $(containerId);
  $container.css({height: "250px", margin: "0 auto"});

  var getDates = function(responses){
    var counts = PT.count(responses, inputId);
    // convert counts to array
    var dates = [];
    for(var key in counts) {
      if(key.length > 0 && key !== "undefined"){
        dates.push([parseInt(Date.parse(key)), counts[key]])
      }
    }
    return dates.sort(function (a, b) {return a[0] - b[0]});
  };

  // plot
  var options = {
    chart: {
      type: 'column'
    },
    title: {
      text: ''
    },
    xAxis: {
      type: 'datetime',
      dateTimeLabelFormats: { // don't display the dummy year
        month: '%e. %b',
        year: '%b'
      },
      title: {
        text: I18n.t("campaigns.collect.date")
      }
    },
    yAxis: {
      title: {
        text: I18n.t("campaigns.collect.number_of_responses")
      },
      min: 0
    },
    tooltip: {
      headerFormat: '',
      pointFormat: '{point.x:%e/%b}: {point.y}'
    },
    colors: PT.colors,
    series: [{
      showInLegend: false, 
      data: getDates(responses)
    }],
    credits: {
      enabled: false
    }
  };

  $container.highcharts(options);

  $container.on("update", function(event, newResponses){
    options.series[0].data = getDates(newResponses);
    $(this).highcharts(options);
  });
};

PT.renderMapForLocationQuestion = function(containerId, responses, inputId){
  var $div = $(containerId);

  // Find and format valid answers
  if(responses.length <= 0) return;  // Quit if no responses

  var answers = [];
  responses.forEach(function(response){
    response.answers.forEach(function(answer){
      if(answer.id == inputId){
        if(answer.value && answer.value.lat && answer.value.lon){
          answers.push({
            lon: answer.value.lon,
            lat: answer.value.lat,
            data: response
          });
        }
      }
    });
  });

  var markerData = [{
    color: PT.colors[1],
    points: answers
  }];

  $div.empty();
  var $canvas = $('<canvas width="'+$div.innerWidth()+'" height="'+$div.innerHeight()+'"/>').appendTo($div);

  $canvas.osmStaticMap({
    url: "http://s.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
    circleRadius: 8,
    markers: markerData,
    interactive: true,
    click: function(event, p){
      PT.showResponsePopup(event, p[0].data.data);
    }
  });
};

PT.renderInputSummaries = function(responses, containerId, callback){
  if(responses.length > 0){
    var $container = $(containerId);
    $container.empty();

    PT.surveyDefinition.inputs.forEach(function(input){
      $container.append(HandlebarsTemplates["input_viz"](input));

      switch(input.input_type){
        case "select":
        case "select1":
          if(input.options.length < 3){
            PT.renderPieChart("#viz-input-" + input.id, responses, input.id, true, false);
          } else {
            PT.renderColumnChart("#viz-input-" + input.id, responses, input.id, true, false);
          };

          break;

        case "location":
          PT.renderMapForLocationQuestion("#viz-input-" + input.id, responses, input.id);
          break;

        case "number":
        case "text":
          PT.renderText("#viz-input-" + input.id, responses, input.id);
          break;

        case "date":
          PT.renderDate("#viz-input-" + input.id, responses, input.id);
          break;

        case "image":
          PT.renderGallery(responses, "#viz-input-" + input.id, "gallery-" + input.id, input.id);
          break;
      }
    })
  } else {
    $("#graph-placeholder").show();
  }
  $(window).resize();
};

PT.renderReportGraphs = function(responses, containerId){
  if(responses.length > 0){
    var $container = $(containerId);
    $container.empty();

    PT.surveyDefinition.inputs.forEach(function(input){
      if(input.input_type == "select" || input.input_type == "select1"){
        var $graphSquare = $(document.createElement("div"));

        $graphSquare.addClass("viz-container item");
        $graphSquare.attr("id", "graph-" + input.id)
        $container.append($graphSquare);

        if(input.options.length < 3){
          PT.renderPieChart("#graph-" + input.id, responses, input.id, false, true);
        } else {
          PT.renderColumnChart("#graph-" + input.id, responses, input.id, false, true);
        };
      }
    });

    // Show first item in graph carousel
    $(containerId + " .item").addClass("active");
    $(window).resize();
    if($(".carousel .item").length < 2){
      $(".carousel-control").hide();
    }
  }
};

PT.renderPieChart = function(containerId, responses, inputId, titleDisabled, download){
  var title, label;

  var getValues = function(responseArray){
    var counts = PT.count(responseArray, inputId);
    var values = [];
    for(var key in counts) {
      key ? label = key : label = I18n.t("campaigns.share.no_response");
      values.push({name: label, y:counts[key]})
    }

    return values;
  }

  titleDisabled ? title = "" : title = PT.getSurveyInputById(PT.surveyDefinition, inputId).label;
  
  // plot
  var options = {
    chart: {
      plotBackgroundColor: null,
      plotBorderWidth: null,
      plotShadow: false,
      type: 'pie'
    },
    title: {
      text: title
    },
    colors: PT.colors,
    tooltip: {
      formatter: function(){
        return "<b>" + Highcharts.numberFormat(this.point.percentage, 1) + "%</b><br><span style='color: grey; font-size: .9em;'>(" + this.point.y + " " + I18n.t("campaigns.collect.responses.counting", {count: this.point.y}) + ")</span>"
      }
    },
    credits: {
      enabled: false
    },
    plotOptions: {
      pie: {
        allowPointSelect: true,
        cursor: "pointer",
        dataLabels: {
          enabled: true,
          format: "<b>{point.name}</b>: {point.percentage:.0f} %",
          style: {
              color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || "black",
              width: "120px"
          },
          distance: 1
        }
      }
    },

    series: [{
        name: 'registros',
        colorByPoint: true,
        data: getValues(responses)
    }]
  };
  
  var chart = $(containerId).highcharts(options);
  chart.on("update", function(event, newResponses){
    options.series[0].data = getValues(newResponses);
    $(this).highcharts(options);
  });
};

PT.renderColumnChart = function(containerId, responses, inputId, titleDisabled, download){
  var title, label;

  var getSeries = function(responses){
    var counts = PT.count(responses, inputId);
    var series = [];
    for(var key in counts){
      key ? label = key : label = "(" + I18n.t("campaigns.share.no_response") + ")";
      series.push({
        name: label, 
        data: [counts[key]],
        index: key ?  "" : Object.keys(counts).length -1,
        legendIndex: key ?  "" : Object.keys(counts).length -1
      });
    };

    return series;
  }

  titleDisabled ? title = "" : title = PT.getSurveyInputById(PT.surveyDefinition, inputId).label;

  var options = {
    chart: {
      type: "column",
      plotBackgroundColor: null,
      plotShadow: false
    },
    colors: PT.colors,
    title: {
      text: title
    },
    tooltip: {
      headerFormat: "{point.series.name}",
      pointFormat: "<br><b>{point.y} " + I18n.t("campaigns.collect.responses.counting.other") + "</b>"
    },
    credits: {
      enabled: false
    },
    xAxis: {
      type: 'category',
      lineColor: "transparent",
      tickWidth: 0,
      labels: {
        enabled: false
      }
    },
    yAxis: {
      allowDecimals: false,
      title: {
        text: ""
      }
    },
    plotOptions: {
      column: {
        allowPointSelect: true,
        cursor: "pointer",
        pointPadding: .25,
        borderWidth: 0,
        dataLabels: {
          enabled: false,
        }
      }
    },
    series: getSeries(responses),
    exporting: {
      enabled: download
    },
    navigation: {
      buttonOptions: {
        verticalAlign: "bottom",
        x: -60
      }
    }
  };

  var chart = $(containerId).highcharts(options);
  
  chart.on("update", function(event, newResponses){
    options.series = getSeries(newResponses);
    $(this).highcharts(options);
  });
};

// Map popup window for single response
PT.showResponsePopup = function(event, response){
  event.stopPropagation();

  $(".response-container-outer").css({
    position: "absolute",
    top: event.pageY + 15,
    left: event.pageX - 50,
    width: window.innerWidth / 3 + "px"
  });

  PT.viewResponse(response);
  $(".response-container-outer").show();
};

// Set slider for date filter
PT.setDateRangeSlider = function(sliderId){
  var dates = Object.keys(PT.count(PT.responses));
  var min = dates[dates.length - 1];
  var max = parseInt(dates[0]) + 86400000;
  $(sliderId).dateSlider(min, max, min, max);
  
  var getValues = function(){
    return [$("#date-range").slider("values")[0] * 1000, $("#date-range").slider("values")[1] * 1000]; 
  };

  var setReadOut = function(){
    $(sliderId).closest(".filter").find(".read-out").html(moment(getValues()[0]).format('D MMM YYYY') + " - " + moment(getValues()[1]).format('D MMM YYYY'));
  };

  setReadOut();
  $(sliderId).on("slide", _.debounce(setReadOut, 50, false));
  $(sliderId).on("slidestop", PT.applyFilters);
};

PT.applyFilters = function(){
  var selection;
  var startDate = $("#date-range").slider("values")[0] * 1000;
  var endDate =  $("#date-range").slider("values")[1] * 1000 + 86400000; // Set limit to midnight
  var selectedInputId = parseInt($("#select-field").val());
  var selectedValue = $("#select-value option:selected").html().trim() || "---";

  var byDate = PT.responses.filter(function(response){
    return startDate <= response.timestamp && endDate >= response.timestamp;
  });

  if(selectedInputId !== "---" && selectedValue !== "---"){
    selection = byDate.filter(function(response){
      var answer = PT.getAnswerById(response, selectedInputId).value.trim();
      return answer == selectedValue;
    });
  } else {
    selection = byDate;
  };

  $("#graph-container .viz-container").each(function(index, graph){  
    $(graph).trigger("update", [selection]);
  });
  PT.renderGallery(selection, "#image-viz", "main-gallery");
  PT.renderGoogleMap(selection, "map-viz");
};

PT.configureFilters = function(){
  PT.setDateRangeSlider("#date-range");
  PT.setFieldFilter();

  $(".filter-field").on("change", PT.applyFilters);
};

PT.setFieldFilter = function(){
  // Populate value options
  var fields = ["<option value='none'>---</option>"]
  $.each(PT.surveyDefinition.inputs, function (i, input) {
    if(input.input_type == "select1"){
      fields.push("<option value='" + input.id + "'>" + input.label + "</option>")
    }
  });
  $("#select-field").html(fields.join(""));
  
  // Populate value options
  $("#select-value").html("<option value='none'>---</option>");

  $("#select-field").on("change", function(){
    var values = ["<option value='none'>---</option>"];
    if(this.value == "none"){
      plotAll(data)}
    else {
      var inputId = this.value;
      $.each(PT.getSurveyInputById(PT.surveyDefinition, inputId).options, function (i, option) {
        values.push("<option value='"+ inputId + "-" + i + "'>" + option + "</option>");
      });
    }

    $("#select-value").html(values.join(""));
  });
};


// Profile page data section
$(function(){
  $(".carousel").carousel({
    pause: true,
    interval: false
  });

  // Hide carousel controls on first and last slide
  $(".carousel").on("slid.bs.carousel", function() {
    var $active = $(".carousel .item.active");
    $(window).resize();

    if($active.is(":first-child")){
      $(".carousel-control.left").fadeOut();
      $(".carousel-control.right").fadeIn();
    } else if($active.is(":last-child")) {
      $(".carousel-control.right").fadeOut();
      $(".carousel-control.left").fadeIn();
    } else {
      $(".carousel-control").fadeIn();
    }
  });

  $(document).ajaxSend(function(event, request, settings) {
    $(".ajax-loading").show();
  });

  $(document).ajaxComplete(function(event, request, settings) {
    $(".ajax-loading").hide();
  });
});
<commit_msg>Account for missing answers in viz graphs<commit_after>var PT = PT || {};

PT.colors = [
  "#66c6ba",
  "#f5be59", 
  "#ea7669", 
  "#545757", 
  "#42847b", 
  "#efa9a3",
  "#e6e7e8",
  "#000000"
];

// Aggregate data

PT.getSurveyInputById = function(surveyDefinition, inputId) {
  for(var i = 0; i< surveyDefinition.inputs.length; i++) {
    if(surveyDefinition.inputs[i].id == inputId) return surveyDefinition.inputs[i];
  }
};

PT.getAnswerById = function(response, inputId) {
  for(var i = 0; i< response.answers.length; i++) {
    if(response.answers[i].id == inputId) {
      return response.answers[i];
    }
  }
};

PT.getAnswersById = function(responses, inputId){
  var answers = [];
  for(var i = 0; i<responses.length; i++) {
    answers.push(PT.getAnswerById(responses[i], inputId).value);
  }

  return answers;
}

PT.count = function(responses, inputId) { //Count responses by value or date
  var counts = {};
  for(var i = 0; i< responses.length; i++) {
    var value;
    if(inputId){
      // Set to 'no response' if answer value not found
      answer = PT.getAnswerById(responses[i], inputId);
      answer ? value = answer.value : answer = I18n.t("campaigns.share.no_response");
    } else {
      value = responses[i].timestamp;
    }
    counts[value] = counts[value] ? counts[value]+1 : 1;
  }
  return counts;
};

PT.aggregateAnswers = function(responses, inputId){ //Collect answers for text and image
  var answers = [];

  if(inputId){
    responses.forEach(function(response){
      var relevantAnswer = response.answers.filter(function(answer){
        if(answer.id == inputId){
          if(answer.value && String(answer.value).length > 0){
            var item = answer;
            item.parentResponse = response;
            return item;
          }
        }
      })[0];

      relevantAnswer ? answers = PT.separateMultipleEntries(relevantAnswer, response, answers) : false;
    });
  } else {
    answers = PT.extractAllImages(responses);
  }

  return answers;
};

PT.separateMultipleEntries = function(answer, response, answersArray){
  var formatAnswer = function(value){
    return {
      value: value,
      parentResponse: response,
      type: answer.input_type
    }
  };

  if(answer.value.constructor === Array){ // Support new multiple image answer
    answer.value.forEach(function(value){
      answersArray.push(formatAnswer(value));
    });
  } else { // Support old single image answer
    answersArray.push(formatAnswer(answer.value));
  }

  return answersArray;
};

PT.extractAllImages = function(responses){
  var images = [];

  responses.forEach(function(response){
    if(response.answers){
      response.answers.forEach(function(answer){
        if(answer.input_type == "image" && answer.value){
          PT.separateMultipleEntries(answer, response, images)
        }
      });
    }
  });

  return images;
};

// Render visualizations

PT.renderGoogleMap = function(responses, containerId){
  var markers = [];
  var surveyDefinition = {};
  var map = null,
      infoWindow = null;
  var $container;

  var attachMarkerClickEvent = function(marker, response){
    var markerInfo = {
      id: response.id,
      image: null,
      displayableAnswers: {}
    };

    // 1. Browse through answers, extract image and responses
    for(var i=0,len=response.answers.length;i<len;i++){
      var answer = response.answers[i];
      switch(answer.input_type){
        case "location":
          break;
        case "image":
          if(typeof answer.value!=="undefined" && answer.value){
            markerInfo.image = answer.value;
          }
          break;
        default:
          markerInfo.displayableAnswers[surveyDefinition[answer.id]] = answer.value || "";
      }
    }

    // 2. Build marker popup template and attach listener
    google.maps.event.addListener(marker, "click", function(){
      infoWindow.setContent(HandlebarsTemplates["map_info_window"](markerInfo));
      infoWindow.open(map, marker);
    });
  };

  window.mapInit = function(){
    // Start initialization, create map
    var mapOptions = {
      zoom: 8,
      center: new google.maps.LatLng(-34.397, 150.644),
      scrollwheel: false
    };
    map = new google.maps.Map(document.getElementById(containerId), mapOptions);
    infoWindow = new google.maps.InfoWindow({
        content: ""
    });

    // Store survey definition
    var surveyInputs = PT.surveyDefinition.inputs;
    for(var i=0,len=surveyInputs.length;i<len;i++){
      var input = surveyInputs[i];
      surveyDefinition[input.id] = input.label;
    }

    // Create map markers
    for(var i=0,len=responses.length;i<len;i++){
      var response = responses[i];

      // Find geo location
      var lat = null,
          lon = null;
      for(var j=0,len2=response.answers.length;j<len2;j++){
        var answer = response.answers[j];
        if(typeof answer.input_type!=="undefined" && answer.input_type=="location" && typeof answer.value!=="undefined" && typeof answer.value.lon!=="undefined"){
          lat = answer.value.lat;
          lon = answer.value.lon;
          break;
        }
      }
      if(lat==null || lon==null){
        if(response.locationstamp && response.locationstamp.lat !== "undefined" && response.locationstamp.long !== "undefined"){
          lat = response.locationstamp.lat;
          lon = response.locationstamp.lon;
        } else {
          continue; // Find if next response is a geolocation
        }
      }

      // Create marker
      if(lat && lon){
        var marker = new google.maps.Marker({
          map: map,
          position: new google.maps.LatLng(lat, lon)
        });
        markers.push(marker);

        attachMarkerClickEvent(marker, response);
      }
    }

    // Scale map viewport to include all markers
    var points = $.map(markers, function(a){
        return a.getPosition();
    });
    var bounds = new google.maps.LatLngBounds();
    for(var i=0;i<points.length;i++){
        bounds.extend(points[i]);
    }
    
    map.fitBounds(bounds);

    $("#map-tab").on("shown.bs.tab", function(){
      google.maps.event.trigger(map, "resize");
      map.fitBounds(bounds);
    });

  };

  // Load map script

  if(typeof google === "undefined"){
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "https://maps.googleapis.com/maps/api/js?v=3.exp&" +
        "callback=mapInit&key=" + "<%= ENV['GOOGLE_MAPS_KEY'] %>";
    document.body.appendChild(script);
  } else {
    window.mapInit();
  }
};

PT.renderGallery = function(responses, containerId, galleryName, inputId){
  var $container = $(containerId);

  var populate = function(container, responses){
    var images = PT.aggregateAnswers(responses, inputId);
    $container.empty();

    if(images.length > 0){
      images.forEach(function(image, index){
        // Ensure no local image uploads are included
        if(image.value.slice(0,4) !== "file"){
          var $a = $("<a/>");
          var $image = $("<div/>", {class: "gallery-image"});

          $a.attr("href", image.value);
          $a.attr("data-lightbox", galleryName);
          $image.css("background", "url(" + image.value + ") no-repeat center center");
          $image.css("background-size", "cover");
          $a.append($image);
          $container.append($a);
        }
      });
    }
  };

  populate($container, responses);
  $container.on("update", function(event, newResponses){
    populate($container, newResponses);
  });
};

PT.renderText = function(containerId, responses, inputId){
  var $container = $(containerId);

  var populate = function(responseArray){
    var $textBlock;
    var answers = PT.aggregateAnswers(responseArray, inputId);
    
    $container.empty();
    answers.forEach(function(item, index){
      $textBlock = $("<span/>", {class: "text-viz"}).html(item.value);
      $textBlock.css("color", PT.colors[(index % PT.colors.length)]);
      $textBlock.css("font-size", Math.floor(Math.random(5) * 10 + 12) + "px");
      $container.append($textBlock);

      $textBlock.on("click", function(event){
        PT.showResponsePopup(event, item.parentResponse, PT.surveyDefinition);
      });
    });
  };

  populate(responses);
  $container.on("update", function(event, newResponses){
    populate(newResponses);
  });
};

PT.renderDate = function(containerId, responses, inputId) {
  var $container = $(containerId);
  $container.css({height: "250px", margin: "0 auto"});

  var getDates = function(responses){
    var counts = PT.count(responses, inputId);
    // convert counts to array
    var dates = [];
    for(var key in counts) {
      if(key.length > 0 && key !== "undefined"){
        dates.push([parseInt(Date.parse(key)), counts[key]])
      }
    }
    return dates.sort(function (a, b) {return a[0] - b[0]});
  };

  // plot
  var options = {
    chart: {
      type: 'column'
    },
    title: {
      text: ''
    },
    xAxis: {
      type: 'datetime',
      dateTimeLabelFormats: { // don't display the dummy year
        month: '%e. %b',
        year: '%b'
      },
      title: {
        text: I18n.t("campaigns.collect.date")
      }
    },
    yAxis: {
      title: {
        text: I18n.t("campaigns.collect.number_of_responses")
      },
      min: 0
    },
    tooltip: {
      headerFormat: '',
      pointFormat: '{point.x:%e/%b}: {point.y}'
    },
    colors: PT.colors,
    series: [{
      showInLegend: false, 
      data: getDates(responses)
    }],
    credits: {
      enabled: false
    }
  };

  $container.highcharts(options);

  $container.on("update", function(event, newResponses){
    options.series[0].data = getDates(newResponses);
    $(this).highcharts(options);
  });
};

PT.renderMapForLocationQuestion = function(containerId, responses, inputId){
  var $div = $(containerId);

  // Find and format valid answers
  if(responses.length <= 0) return;  // Quit if no responses

  var answers = [];
  responses.forEach(function(response){
    response.answers.forEach(function(answer){
      if(answer.id == inputId){
        if(answer.value && answer.value.lat && answer.value.lon){
          answers.push({
            lon: answer.value.lon,
            lat: answer.value.lat,
            data: response
          });
        }
      }
    });
  });

  var markerData = [{
    color: PT.colors[1],
    points: answers
  }];

  $div.empty();
  var $canvas = $('<canvas width="'+$div.innerWidth()+'" height="'+$div.innerHeight()+'"/>').appendTo($div);

  $canvas.osmStaticMap({
    url: "http://s.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png",
    circleRadius: 8,
    markers: markerData,
    interactive: true,
    click: function(event, p){
      PT.showResponsePopup(event, p[0].data.data);
    }
  });
};

PT.renderInputSummaries = function(responses, containerId, callback){
  if(responses.length > 0){
    var $container = $(containerId);
    $container.empty();

    PT.surveyDefinition.inputs.forEach(function(input){
      $container.append(HandlebarsTemplates["input_viz"](input));

      switch(input.input_type){
        case "select":
        case "select1":
          if(input.options.length < 3){
            PT.renderPieChart("#viz-input-" + input.id, responses, input.id, true, false);
          } else {
            PT.renderColumnChart("#viz-input-" + input.id, responses, input.id, true, false);
          };

          break;

        case "location":
          PT.renderMapForLocationQuestion("#viz-input-" + input.id, responses, input.id);
          break;

        case "number":
        case "text":
          PT.renderText("#viz-input-" + input.id, responses, input.id);
          break;

        case "date":
          PT.renderDate("#viz-input-" + input.id, responses, input.id);
          break;

        case "image":
          PT.renderGallery(responses, "#viz-input-" + input.id, "gallery-" + input.id, input.id);
          break;
      }
    })
  } else {
    $("#graph-placeholder").show();
  }
  $(window).resize();
};

PT.renderReportGraphs = function(responses, containerId){
  if(responses.length > 0){
    var $container = $(containerId);
    $container.empty();

    PT.surveyDefinition.inputs.forEach(function(input){
      if(input.input_type == "select" || input.input_type == "select1"){
        var $graphSquare = $(document.createElement("div"));

        $graphSquare.addClass("viz-container item");
        $graphSquare.attr("id", "graph-" + input.id)
        $container.append($graphSquare);

        if(input.options.length < 3){
          PT.renderPieChart("#graph-" + input.id, responses, input.id, false, true);
        } else {
          PT.renderColumnChart("#graph-" + input.id, responses, input.id, false, true);
        };
      }
    });

    // Show first item in graph carousel
    $(containerId + " .item").addClass("active");
    $(window).resize();
    if($(".carousel .item").length < 2){
      $(".carousel-control").hide();
    }
  }
};

PT.renderPieChart = function(containerId, responses, inputId, titleDisabled, download){
  var title, label;

  var getValues = function(responseArray){
    var counts = PT.count(responseArray, inputId);
    var values = [];
    for(var key in counts) {
      key ? label = key : label = I18n.t("campaigns.share.no_response");
      values.push({name: label, y:counts[key]})
    }

    return values;
  }

  titleDisabled ? title = "" : title = PT.getSurveyInputById(PT.surveyDefinition, inputId).label;
  
  // plot
  var options = {
    chart: {
      plotBackgroundColor: null,
      plotBorderWidth: null,
      plotShadow: false,
      type: 'pie'
    },
    title: {
      text: title
    },
    colors: PT.colors,
    tooltip: {
      formatter: function(){
        return "<b>" + Highcharts.numberFormat(this.point.percentage, 1) + "%</b><br><span style='color: grey; font-size: .9em;'>(" + this.point.y + " " + I18n.t("campaigns.collect.responses.counting", {count: this.point.y}) + ")</span>"
      }
    },
    credits: {
      enabled: false
    },
    plotOptions: {
      pie: {
        allowPointSelect: true,
        cursor: "pointer",
        dataLabels: {
          enabled: true,
          format: "<b>{point.name}</b>: {point.percentage:.0f} %",
          style: {
              color: (Highcharts.theme && Highcharts.theme.contrastTextColor) || "black",
              width: "120px"
          },
          distance: 1
        }
      }
    },

    series: [{
        name: 'registros',
        colorByPoint: true,
        data: getValues(responses)
    }]
  };
  
  var chart = $(containerId).highcharts(options);
  chart.on("update", function(event, newResponses){
    options.series[0].data = getValues(newResponses);
    $(this).highcharts(options);
  });
};

PT.renderColumnChart = function(containerId, responses, inputId, titleDisabled, download){
  var title, label;

  var getSeries = function(responses){
    var counts = PT.count(responses, inputId);
    var series = [];
    for(var key in counts){
      key ? label = key : label = "(" + I18n.t("campaigns.share.no_response") + ")";
      series.push({
        name: label, 
        data: [counts[key]],
        index: key ?  "" : Object.keys(counts).length -1,
        legendIndex: key ?  "" : Object.keys(counts).length -1
      });
    };

    return series;
  }

  titleDisabled ? title = "" : title = PT.getSurveyInputById(PT.surveyDefinition, inputId).label;

  var options = {
    chart: {
      type: "column",
      plotBackgroundColor: null,
      plotShadow: false
    },
    colors: PT.colors,
    title: {
      text: title
    },
    tooltip: {
      headerFormat: "{point.series.name}",
      pointFormat: "<br><b>{point.y} " + I18n.t("campaigns.collect.responses.counting.other") + "</b>"
    },
    credits: {
      enabled: false
    },
    xAxis: {
      type: 'category',
      lineColor: "transparent",
      tickWidth: 0,
      labels: {
        enabled: false
      }
    },
    yAxis: {
      allowDecimals: false,
      title: {
        text: ""
      }
    },
    plotOptions: {
      column: {
        allowPointSelect: true,
        cursor: "pointer",
        pointPadding: .25,
        borderWidth: 0,
        dataLabels: {
          enabled: false,
        }
      }
    },
    series: getSeries(responses),
    exporting: {
      enabled: download
    },
    navigation: {
      buttonOptions: {
        verticalAlign: "bottom",
        x: -60
      }
    }
  };

  var chart = $(containerId).highcharts(options);
  
  chart.on("update", function(event, newResponses){
    options.series = getSeries(newResponses);
    $(this).highcharts(options);
  });
};

// Map popup window for single response
PT.showResponsePopup = function(event, response){
  event.stopPropagation();

  $(".response-container-outer").css({
    position: "absolute",
    top: event.pageY + 15,
    left: event.pageX - 50,
    width: window.innerWidth / 3 + "px"
  });

  PT.viewResponse(response);
  $(".response-container-outer").show();
};

// Set slider for date filter
PT.setDateRangeSlider = function(sliderId){
  var dates = Object.keys(PT.count(PT.responses));
  var min = dates[dates.length - 1];
  var max = parseInt(dates[0]) + 86400000;
  $(sliderId).dateSlider(min, max, min, max);
  
  var getValues = function(){
    return [$("#date-range").slider("values")[0] * 1000, $("#date-range").slider("values")[1] * 1000]; 
  };

  var setReadOut = function(){
    $(sliderId).closest(".filter").find(".read-out").html(moment(getValues()[0]).format('D MMM YYYY') + " - " + moment(getValues()[1]).format('D MMM YYYY'));
  };

  setReadOut();
  $(sliderId).on("slide", _.debounce(setReadOut, 50, false));
  $(sliderId).on("slidestop", PT.applyFilters);
};

PT.applyFilters = function(){
  var selection;
  var startDate = $("#date-range").slider("values")[0] * 1000;
  var endDate =  $("#date-range").slider("values")[1] * 1000 + 86400000; // Set limit to midnight
  var selectedInputId = parseInt($("#select-field").val());
  var selectedValue = $("#select-value option:selected").html().trim() || "---";

  var byDate = PT.responses.filter(function(response){
    return startDate <= response.timestamp && endDate >= response.timestamp;
  });

  if(selectedInputId !== "---" && selectedValue !== "---"){
    selection = byDate.filter(function(response){
      var answer = PT.getAnswerById(response, selectedInputId).value.trim();
      return answer == selectedValue;
    });
  } else {
    selection = byDate;
  };

  $("#graph-container .viz-container").each(function(index, graph){  
    $(graph).trigger("update", [selection]);
  });
  PT.renderGallery(selection, "#image-viz", "main-gallery");
  PT.renderGoogleMap(selection, "map-viz");
};

PT.configureFilters = function(){
  PT.setDateRangeSlider("#date-range");
  PT.setFieldFilter();

  $(".filter-field").on("change", PT.applyFilters);
};

PT.setFieldFilter = function(){
  // Populate value options
  var fields = ["<option value='none'>---</option>"]
  $.each(PT.surveyDefinition.inputs, function (i, input) {
    if(input.input_type == "select1"){
      fields.push("<option value='" + input.id + "'>" + input.label + "</option>")
    }
  });
  $("#select-field").html(fields.join(""));
  
  // Populate value options
  $("#select-value").html("<option value='none'>---</option>");

  $("#select-field").on("change", function(){
    var values = ["<option value='none'>---</option>"];
    if(this.value == "none"){
      plotAll(data)}
    else {
      var inputId = this.value;
      $.each(PT.getSurveyInputById(PT.surveyDefinition, inputId).options, function (i, option) {
        values.push("<option value='"+ inputId + "-" + i + "'>" + option + "</option>");
      });
    }

    $("#select-value").html(values.join(""));
  });
};


// Profile page data section
$(function(){
  $(".carousel").carousel({
    pause: true,
    interval: false
  });

  // Hide carousel controls on first and last slide
  $(".carousel").on("slid.bs.carousel", function() {
    var $active = $(".carousel .item.active");
    $(window).resize();

    if($active.is(":first-child")){
      $(".carousel-control.left").fadeOut();
      $(".carousel-control.right").fadeIn();
    } else if($active.is(":last-child")) {
      $(".carousel-control.right").fadeOut();
      $(".carousel-control.left").fadeIn();
    } else {
      $(".carousel-control").fadeIn();
    }
  });

  $(document).ajaxSend(function(event, request, settings) {
    $(".ajax-loading").show();
  });

  $(document).ajaxComplete(function(event, request, settings) {
    $(".ajax-loading").hide();
  });
});

{'fraction_non_alphanumeric': 0.17007516595080047, 'fraction_numerical': 0.00815111284654432, 'mean_word_length': 2.740142387732749, 'pattern_counts': {'":': 22, '<': 81, '<?xml version=': 0, '>': 65, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7040060', 'n_tokens_mistral': 13329, 'n_tokens_neox': 12871, 'n_words': 2469}