<commit_before>// Here are some global variables.
// As we refactor the app, let's think about a more appropriate place to put them.  
var map;
var mapMenuData;
var layerOptions = [];
var dataMenu;
// End global variables

function hideAllOptionalLayers(){
  for(var i = 0; i < layerOptions.length; i++){
    layerOptions[i].hide();
  }
}

(function getMenuData(){
  var xhr = new XMLHttpRequest();
  // Currently assumes a server running in the project root directory.
  xhr.open('GET','/javascript/tool/data/datasets_for_browse.json');
  xhr.send();
  xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
      mapMenuData = JSON.parse(xhr.responseText);
      prepareInitialMapData();
    }
  }
})();

// DataMenu includes the 'map' argument to avoid a scoping/closure issue
// that caused 'map' to be undefined. Let's find a more elegant way to make 
// map available to LayerOption, so we don't have to use an argument for something
// that should be kind of global.
function DataMenu(map){
  var _this = this;
  for (var i = 0, datasets = Object.keys(mapMenuData.optional_datasets); i < datasets.length; i++) {
    var id = datasets[i];
    var link = document.createElement('a');
    link.href = '#';
    link.className = 'inactive';
    link.textContent = id.toTitle();
    link.dataset.id = id;

    var data_sources = document.getElementById('data-menu');
    data_sources.appendChild(link);
    link.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      _this.emptyZoneOptions();
      _this.makeZoneOptions(this.getAttribute('data-id'));
    });
  }

  this.makeZoneOptions = function(tableName){
    var eligibleZoneIds = mapMenuData['optional_datasets'][tableName];
    for (var i = 0; i < eligibleZoneIds.length; i++) {
      var id = eligibleZoneIds[i];

      var link = document.createElement('a');
      link.href = '#';
      link.textContent = id.toTitle();
      link.dataset.id = id;

      link.onclick = function (e) {
        e.preventDefault();
        e.stopPropagation();
        
        var possibleLayerOptions = layerOptions.filter(function(opt){
          return opt.layerName == link.getAttribute('data-id') && opt.dataName == tableName;
        });

        hideAllOptionalLayers();

        var selectedLayerOption = possibleLayerOptions[0] || new LayerOption(tableName, this.getAttribute('data-id'), map);

        setHeader(map);
        changeZoneType(); // defined in pie.js
      };

      var layers = document.getElementById('menu');
      layers.appendChild(link);
    }
  }

  this.emptyZoneOptions = function(){
    var layers = document.getElementById('menu');
    while(layers.children.length > 0 ){
      layers.removeChild(layers.children[0]);
    }
  }
}

// LayerOption assumes that there is one zones menu and one data menu. 
// It also assumes that the id attribute for each link in the data layers menu 
// corresponds to a table name, and the id attribute for each link in the 
// zones menu corresponds to a zone layer.

// LayerOption includes the third 'map' argument to avoid a scoping/closure issue
// that caused 'map' to be undefined. Let's find a more elegant way to make 
// map available to LayerOption, so we don't have to use an argument for something
// that should be kind of global.
function LayerOption(dataName, zoneName, map){
  var _this = this;
  layerOptions.push(this);
  this.dataName = dataName;
  this.zoneName = zoneName;
  this.sourceName = dataName + "_" + zoneName + "_source";
  this.layerName = dataName + "_" + zoneName + "_layer";

  this.sourceData;

  (function queryData(){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', mapMenuData.zone_api_base + dataName + "/all/" + zoneName);
    xhr.send();
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
        var json = JSON.parse(xhr.responseText);
        _this.sourceData = json;
        queryShape();
      }  
    }
  })();

  function queryShape(){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/javascript/tool/data/' + _this.zoneName + ".geojson");
      xhr.send();
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
          var json = JSON.parse(xhr.responseText);
          _this.sourceShapes = json;
          _this.addMapboxSource();
          _this.addMapboxLayer();
        }  
      }

  }

  this.addMapboxSource = function(){
    // Check whether the source already exists
    if(map.getSource(this.sourceName)){
      return;
    }
    var sourcePlusShapes = addDataToPolygons(this.sourceShapes, this.sourceData);

    map.addSource(this.sourceName, {
      "type": "geojson",
      "data": sourcePlusShapes
    });

  }

  this.addMapboxLayer = function(){
    // Check whether the layer already exists
    if(map.getLayer(this.layerName)){
      this.show();
      return;
    }

    // for determining upper bound of the data
    var counts = this.sourceData.items.map(function(item){
      return item.count;
    })

    // assigns a random color
    var RGB_RANGE = 75;

    // if we need random colors elsewhere in this app, we could extract a RandomColor constructor and
    // turn 'makeRGB' into a method. For now it's a quick and diryt function.
    function makeRGB(colorValsArray){
      return "rgb(" + colorValsArray.join(",") + ")";
    }

    var highestColorVals = [0,0,0].map(function(el){
      return Math.floor(Math.random() * (255 - RGB_RANGE));
    });

    var lowestColorVals = highestColorVals.map(function(el){
      return el + RGB_RANGE;
    });

    map.addLayer({
      "id": this.layerName,
      "type": "fill",
      "source": this.sourceName,
      "layout": {
        "visibility": "visible"
      },
      "paint": {
        "fill-color": {
          "property": this.dataName,
          "stops": [[0, makeRGB(lowestColorVals)], [Math.max(counts), makeRGB(highestColorVals)]]
        },
        "fill-opacity": .5
      }
    });
  }

  this.show = function() {
    map.clickedZone = this.zoneName;
    var selector = document.querySelectorAll('[data-id=' + this.dataName + ']')[0];
    hideAllOptionalLayers();

    document.querySelectorAll('nav#menu a').forEach(function(item){
      item.className = '';
    });
    selector.className = 'active';
    map.setLayoutProperty(this.layerName, 'visibility', 'visible');
  
  };

  this.hide = function(){
    map.setLayoutProperty(this.layerName, 'visibility', 'none');
  }

}

function prepareInitialMapData(){<commit_msg>Random colors for layer options in the map view display as intended.<commit_after>// Here are some global variables.
// As we refactor the app, let's think about a more appropriate place to put them.  
var map;
var mapMenuData;
var layerOptions = [];
var dataMenu;
// End global variables

function hideAllOptionalLayers(){
  for(var i = 0; i < layerOptions.length; i++){
    layerOptions[i].hide();
  }
}

(function getMenuData(){
  var xhr = new XMLHttpRequest();
  // Currently assumes a server running in the project root directory.
  xhr.open('GET','/javascript/tool/data/datasets_for_browse.json');
  xhr.send();
  xhr.onreadystatechange = function(){
    if(xhr.readyState === 4){
      mapMenuData = JSON.parse(xhr.responseText);
      prepareInitialMapData();
    }
  }
})();

// DataMenu includes the 'map' argument to avoid a scoping/closure issue
// that caused 'map' to be undefined. Let's find a more elegant way to make 
// map available to LayerOption, so we don't have to use an argument for something
// that should be kind of global.
function DataMenu(map){
  var _this = this;
  for (var i = 0, datasets = Object.keys(mapMenuData.optional_datasets); i < datasets.length; i++) {
    var id = datasets[i];
    var link = document.createElement('a');
    link.href = '#';
    link.className = 'inactive';
    link.textContent = id.toTitle();
    link.dataset.id = id;

    var data_sources = document.getElementById('data-menu');
    data_sources.appendChild(link);
    link.addEventListener('click', function(e) {
      e.preventDefault();
      e.stopPropagation();
      _this.emptyZoneOptions();
      _this.makeZoneOptions(this.getAttribute('data-id'));
    });
  }

  this.makeZoneOptions = function(tableName){
    var eligibleZoneIds = mapMenuData['optional_datasets'][tableName];
    for (var i = 0; i < eligibleZoneIds.length; i++) {
      var id = eligibleZoneIds[i];

      var link = document.createElement('a');
      link.href = '#';
      link.textContent = id.toTitle();
      link.dataset.id = id;

      link.onclick = function (e) {
        e.preventDefault();
        e.stopPropagation();
        
        var possibleLayerOptions = layerOptions.filter(function(opt){
          return opt.layerName == link.getAttribute('data-id') && opt.dataName == tableName;
        });

        hideAllOptionalLayers();

        var selectedLayerOption = possibleLayerOptions[0] || new LayerOption(tableName, this.getAttribute('data-id'), map);

        setHeader(map);
        changeZoneType(); // defined in pie.js
      };

      var layers = document.getElementById('menu');
      layers.appendChild(link);
    }
  }

  this.emptyZoneOptions = function(){
    var layers = document.getElementById('menu');
    while(layers.children.length > 0 ){
      layers.removeChild(layers.children[0]);
    }
  }
}

// LayerOption assumes that there is one zones menu and one data menu. 
// It also assumes that the id attribute for each link in the data layers menu 
// corresponds to a table name, and the id attribute for each link in the 
// zones menu corresponds to a zone layer.

// LayerOption includes the third 'map' argument to avoid a scoping/closure issue
// that caused 'map' to be undefined. Let's find a more elegant way to make 
// map available to LayerOption, so we don't have to use an argument for something
// that should be kind of global.
function LayerOption(dataName, zoneName, map){
  var _this = this;
  layerOptions.push(this);
  this.dataName = dataName;
  this.zoneName = zoneName;
  this.sourceName = dataName + "_" + zoneName + "_source";
  this.layerName = dataName + "_" + zoneName + "_layer";

  this.sourceData;

  (function queryData(){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', mapMenuData.zone_api_base + dataName + "/all/" + zoneName);
    xhr.send();
    xhr.onreadystatechange = function(){
      if(xhr.readyState === 4){
        var json = JSON.parse(xhr.responseText);
        _this.sourceData = json;
        queryShape();
      }  
    }
  })();

  function queryShape(){
    var xhr = new XMLHttpRequest();
    xhr.open('GET', '/javascript/tool/data/' + _this.zoneName + ".geojson");
      xhr.send();
      xhr.onreadystatechange = function(){
        if(xhr.readyState === 4){
          var json = JSON.parse(xhr.responseText);
          _this.sourceShapes = json;
          _this.addMapboxSource();
          _this.addMapboxLayer();
        }  
      }

  }

  this.addMapboxSource = function(){
    // Check whether the source already exists
    if(map.getSource(this.sourceName)){
      return;
    }
    var sourcePlusShapes = addDataToPolygons(this.sourceShapes, this.sourceData);

    map.addSource(this.sourceName, {
      "type": "geojson",
      "data": sourcePlusShapes
    });

  }

  this.addMapboxLayer = function(){
    // Check whether the layer already exists
    if(map.getLayer(this.layerName)){
      this.show();
      return;
    }

    // for determining upper bound of the data
    var counts = this.sourceData.items.map(function(item){
      return item.count;
    })

    // assigns a random color
    var RGB_RANGE = 200;

    // if we need random colors elsewhere in this app, we could extract a RandomColor constructor and
    // turn 'makeRGB' into a method. For now it's a quick and diryt function.
    function makeRGB(colorValsArray){
      return "rgb(" + colorValsArray.join(",") + ")";
    }

    var highestColorVals = [0,0,0].map(function(el){
      return Math.floor(Math.random() * (255 - RGB_RANGE));
    });

    var lowestColorVals = highestColorVals.map(function(el){
      return el + RGB_RANGE;
    });

    map.addLayer({
      "id": this.layerName,
      "type": "fill",
      "source": this.sourceName,
      "layout": {
        "visibility": "visible"
      },
      "paint": {
        "fill-color": {
          "property": this.dataName,
          "stops": [[0, makeRGB(lowestColorVals)], [Math.max.apply(null, counts), makeRGB(highestColorVals)]]
        },
        "fill-opacity": .5
      }
    });
  }

  this.show = function() {
    map.clickedZone = this.zoneName;
    var selector = document.querySelectorAll('[data-id=' + this.dataName + ']')[0];
    hideAllOptionalLayers();

    document.querySelectorAll('nav#menu a').forEach(function(item){
      item.className = '';
    });
    selector.className = 'active';
    map.setLayoutProperty(this.layerName, 'visibility', 'visible');
  
  };

  this.hide = function(){
    map.setLayoutProperty(this.layerName, 'visibility', 'none');
  }

}

function prepareInitialMapData(){
{'fraction_non_alphanumeric': 0.13298116674322463, 'fraction_numerical': 0.003138876129229827, 'mean_word_length': 2.9381971661139583, 'pattern_counts': {'":': 24, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7532866', 'n_tokens_mistral': 4002, 'n_tokens_neox': 3896, 'n_words': 1027}