<commit_before>var catnumber = 24;
var gridSize = 30;
var margin = { top: 25, right: 0, bottom: 100, left: 125 },
    width = 900 - margin.left - margin.right,
    height = 900 - margin.top - margin.bottom,
    //gridSize = Math.floor(width / catnumber),
    legendElementWidth = gridSize*2,
    buckets = 7,
    //colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"], // alternatively colorbrewer.YlGnBu[9]
    colors = colorbrewer.YlGnBu[7],
    //categorylabels = ["Potholes", "Trash", "Signs", "Health", "Property", "Graffiti", "Policing"],
    nbrhdlabels = ["AMT", "ANX", "BVH", "DXW", "DTN", "DGT", "ERK", "ESH", "EDG", "FHV", "FHH", "HLL", "LWF", "NHL", "OTH", "PSH", "QNP", "WRV", "WRK", "WVL", "WOO"];

var neighborhoods = [
  "Amity",
  "Annex",
  "Beaver Hills",
  "Dixwell",
  "Downtown",
  "Dwight",
  "East Rock",
  "East Shore",
  "Edgewood",
  "Fair Haven",
  "Fair Haven Heights",
  "Hill",
  "Long Wharf",
  "Newhallville",
  "Other",
  "Prospect Hill",
  "Quinnipiac",
  "West River",
  "West Rock",
  "Westville",
  "Wooster Square/Mill River"
]

var categories = [
  "Bins for Trash & Recycling",
  "General Bus Request/Incident",
  "Graffiti",
  "Hangers",
  "Health Complaints",
  "Illegal Dumping",
  "Other",
  "Other - city responsibility",
  "Parking Meter",
  "Parks Request",
  "Policing Issue",
  "Post to Neighbors",
  "Potholes",
  "Private Property Issue",
  "Public Space, Streets and Drains",
  "Request for volunteers",
  "Sidewalks and Curb damage",
  "Signs / Bus Shelters / Pavement Markings",
  "SNOW RELATED",
  "Street Lamp",
  "Traffic/Road Safety",
  "Traffic Signal / Pedestrian Signal",
  "Trash & Recycling",
  "Tree Trimming"
]

var catlabels = ['Bins', 'Bus', 'Graffiti', 'Hangers', 'Health',
                 'Dumping', 'Other', 'City Request', 'Parking',
                 'Parks', 'Policing', 'To Neighbors', 'Potholes',
                 'Private Property', 'Public Space', 'Volunteers',
                 'Sidewalks', 'Signs', 'Snow', 'Street Lamps',
                 'Traffic', 'Signals', 'Trash', 'Trees']

console.log(neighborhoods);
console.log(' ');
console.log(categories);

//setup the nested objects to store the summed data:
var heatdata = new Object();
for (i = 0; i < neighborhoods.length; i++) {
  heatdata[neighborhoods[i]] = {};
  for (j = 0; j < categories.length; j++) {
    heatdata[neighborhoods[i]][categories[j]] = 0;
  }
}

//console.log("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg=Tm-Cvrg-All");

//console.log('start of data:');
/*
$.getJSON("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg=Tm-Cvrg-All", function(issue){
    var idx = 0;
    $.each(issue, function(){
        //console.log(issue[idx]);
        //console.log(issue[idx]['category']);
        //console.log(issue[idx]['neighborhood'] == 'East Rock');
        //document.write(issue+"
");
        heatdata[issue[idx]['neighborhood']][issue[idx]['category']] += 1;
        //document.write(idx+"
");
        idx += 1;

    });
});
*/

//begin d3 section

var nbrhdtip = d3.tip()
  .attr('class', 'd3-nbrhd-tip')
  .offset([-10, 0])
  .html(function(d) {
    return "" + d.fullname + "";
  })

var celltip = d3.tip()
  .attr('class', 'd3-cell-tip')
  .offset([-10, 0])
  .html(function(d) {
    return "Neighborhood: " + d.neighborhoodname + "
"+
           "Category: " + d.categoryname + "
"+
           "Issues: " + d.count + "
";
  })

var svg = d3.select("#heatmap").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.call(nbrhdtip);
svg.call(celltip);

//initialization of the percent complete donut charts:
var percmargin = { top: 0, right: 0, bottom: 0, left: 50 },
    percwidth = 450,
    percheight = 250,
    twoPi = 2 * Math.PI,
    progress = 0,
    total = 1308573, // must be hard-coded if server doesn't report Content-Length
    formatPercent = d3.format(".0%");

var arc = d3.svg.arc()
    .startAngle(0)
    .innerRadius(70)
    .outerRadius(80);

var svg2 = d3.select("#roundissueperc").append("svg")
    .attr("width", percwidth + percmargin.left + percmargin.right)
    .attr("height", percheight + percmargin.top + percmargin.bottom)
    .append("g")
    .attr("transform", "translate(" + (percmargin.left + percwidth / 2) + "," + (percmargin.top + percheight / 2) + ")");

var meter = svg2.append("g")
    .attr("class", "progress-meter");

meter.append("path")
    .attr("class", "background issue-donut")
    .attr("d", arc.endAngle(twoPi));

var foreground = meter.append("path")
    .attr("class", "foreground");

var perctext = meter.append("text")
    .attr("class", 'issue-number-text')
    .attr("text-anchor", "middle")
    .attr("dy", ".0em");

var perctextlabel = meter.append("text")
    .attr("class", 'issue-number-label')
    .attr("text-anchor", "middle")
    .attr("dy", ".85em");

function plotHeatmap() {

console.log("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg="+tmCvrg+"&begDate="+begDate+"&endDate="+endDate);
d3.json("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg="+tmCvrg+"&begDate="+begDate+"&endDate="+endDate, function(error, data) {
  if (error) return console.warn(error);
  //console.log(data);

  var not_acknowledged_count = 0;
  var not_completed_count = 0;

  for (idx = 0; idx < data.length; idx++) {
    heatdata[data[idx]['neighborhood']][data[idx]['category']] += 1;
    //console.log(data[idx]['acknowledged_at'], data[idx]['acknowledged_at'] == null);
    if (data[idx]['acknowledged_at'] == null) {
      not_acknowledged_count++;
    }
    if (data[idx]['closed_at'] == null) {
      not_completed_count++;
    }

  }

  console.log('Number of issues: ' + data.length);
  console.log('Number of issues not acknowledged: ' + not_acknowledged_count);
  console.log('Number of issues not completed: ' + not_completed_count);

  var heatdataout = {};
  var neighborhoodarr = [];
  var categoriesarr = [];
  var issuecountarr = [];
  var heatdataarr = [];

  for (i = 0; i < neighborhoods.length; i++) {
    for (j = 0; j < categories.length; j++) {
      //console.log('neighborhood: '+neighborhoods[i]+' category: '+categories[j]+
      //  ' count: '+heatdata[neighborhoods[i]][categories[j]]);
      neighborhoodarr.push(i);
      categoriesarr.push(j);
      issuecountarr.push(heatdata[neighborhoods[i]][categories[j]]);
      heatdataarr.push({'neighborhoodnum': i,
                        'categorynum': j,
                        'count': heatdata[neighborhoods[i]][categories[j]],
                        'neighborhoodname': neighborhoods[i],
                        'categoryname': categories[j]});
    }
  }
  heatdataout['neighborhoodnum'] = neighborhoodarr;
  heatdataout['categorynum'] = categoriesarr;
  heatdataout['count'] = issuecountarr;
  //heatdataout['neighborhood'] = neighborhoods;
  //heatdataout['category'] = categories;

  //Updated Percentage complete donut plot:
  var acknowledged_frac = 1 - not_acknowledged_count/data.length;
  var completed_frac = 1 - not_completed_count/data.length;
  var angular_rotation = -45 / 180 * Math.PI;

  var arc2 = d3.svg.arc()
      .startAngle(-1 * acknowledged_frac/2. * twoPi + angular_rotation)
      .innerRadius(80)
      .outerRadius(90)
      .endAngle(acknowledged_frac/2. * twoPi + angular_rotation);

  var meter2 = svg2.append("g")
      .attr("class", "progress-meter");

  meter2.append("path")
      .attr("class", "acknowledged-donut")
      .attr("d", arc2);

  var arc3 = d3.svg.arc()
      .startAngle(-1 * completed_frac/2. * twoPi + angular_rotation)
      .innerRadius(90)
      .outerRadius(100)
      .endAngle(completed_frac/2. * twoPi + angular_rotation);

  var meter3 = svg2.append("g")
      .attr("class", "progress-meter");

  meter3.append("path")
      .attr("class", "completed-donut")
      .attr("d", arc3);

  perctext.text(data.length);
  perctextlabel.text("issues");

  var colorScale = d3.scale.quantile()
        .domain([0, buckets - 1, d3.max(heatdataarr, function (d) { return d.count; })])
        .range(colors);
  //document.write('All '+idx+' records have been summed.');
  //console.log(colorScale);

  catlist = [];
  for (i=0; i < categories.length; i++){
    catlist.push({'abbreviation': catlabels[i], 'fullname': categories[i]});
  }

  var catLabels = svg.selectAll(".catLabel")
      .data(catlist)
      .enter().append("text")
        .text(function (d) { return d.abbreviation; })
        .attr("x", 0)
        .attr("y", function (d, i) { return i * gridSize; })
        .style("text-anchor", "end")
        .attr("transform", "translate(-6," + gridSize / 1.5 + ")")
        .attr("class", function (d, i) { return "dayLabel mono axis"; })
        .on('mouseover', nbrhdtip.show)
        .on('mouseout', nbrhdtip.hide);

    nbrhoodlist = [];
    for (i=0; i < nbrhdlabels.length; i++){
      nbrhoodlist.push({'abbreviation': nbrhdlabels[i], 'fullname': neighborhoods[i]});
    }

    var nbrhdLabels = svg.selectAll(".nbrhdLabel")
        .data(nbrhoodlist)
        .enter().append("text")
        .text(function(d) { return d.abbreviation; })
        .attr("x", function(d, i) { return i * gridSize; })
        .attr("y", 0)
        .style("text-anchor", "middle")
        .attr("transform", "translate(" + gridSize / 2 + ", -6)")
        .attr("class", function(d, i) { return "timeLabel mono axis"; })
        .on('mouseover', nbrhdtip.show)
        .on('mouseout', nbrhdtip.hide);

    //console.log(heatdataout);
    //console.log(heatdataarr);
    //console.log('neighborhoodnum: ');
    //console.log(heatdataout.neighborhoodnum);
    //console.log('categorynum: ');
    //console.log(heatdataout.categorynum);

    var heatMap = svg.selectAll(".issuecell")
        .data(heatdataarr)
        .enter().append("rect")
        .attr("x", function(d) {
          return (+d.neighborhoodnum) * gridSize;
        })
        .attr("y", function(d) { return (+d.categorynum) * gridSize; })
        //.attr("rx", 4)
        //.attr("ry", 4)
        .attr("class", "issuecell bordered")
        .attr("width", gridSize)
        .attr("height", gridSize)
        .style("fill", colors[0])
        .on('mouseover', celltip.show)
        .on('mouseout', celltip.hide);

    heatMap.transition().duration(1000)
        .style("fill", function(d) { return colorScale(d.count); });

    //heatMap.append("text").text(function(d) { return d.count; });

    var legend = svg.selectAll(".legend")
        .data([0].concat(colorScale.quantiles()), function(d) { return d; })
        .enter().append("g")
        .attr("class", "legend");

    legend.append("rect")
      .attr("x", function(d, i) { return legendElementWidth * i; })
      .attr("y", height - 20)
      .attr("width", legendElementWidth)
      .attr("height", gridSize / 2)
      .style("fill", function(d, i) { return colors[i]; });

    legend.append("text")
      .attr("class", "mono")
      .text(function(d) { return "≥ " + Math.round(d); })
      .attr("x", function(d, i) { return legendElementWidth * i + 15; })
      .attr("y", height + gridSize - 20);

    legend.append("text")
      .attr("class", "mono")
      .text("Number of Issues")
      .attr("x", legendElementWidth * 2.5)
      .attr("y", height + 25);

});
};

window.onload = plotHeatmap();<commit_msg>worked on acknowledged donut updates<commit_after>var catnumber = 24;
var gridSize = 30;
var margin = { top: 25, right: 0, bottom: 100, left: 125 },
    width = 900 - margin.left - margin.right,
    height = 900 - margin.top - margin.bottom,
    //gridSize = Math.floor(width / catnumber),
    legendElementWidth = gridSize*2,
    buckets = 7,
    //colors = ["#ffffd9","#edf8b1","#c7e9b4","#7fcdbb","#41b6c4","#1d91c0","#225ea8","#253494","#081d58"], // alternatively colorbrewer.YlGnBu[9]
    colors = colorbrewer.YlGnBu[7],
    //categorylabels = ["Potholes", "Trash", "Signs", "Health", "Property", "Graffiti", "Policing"],
    nbrhdlabels = ["AMT", "ANX", "BVH", "DXW", "DTN", "DGT", "ERK", "ESH", "EDG", "FHV", "FHH", "HLL", "LWF", "NHL", "OTH", "PSH", "QNP", "WRV", "WRK", "WVL", "WOO"];

var neighborhoods = [
  "Amity",
  "Annex",
  "Beaver Hills",
  "Dixwell",
  "Downtown",
  "Dwight",
  "East Rock",
  "East Shore",
  "Edgewood",
  "Fair Haven",
  "Fair Haven Heights",
  "Hill",
  "Long Wharf",
  "Newhallville",
  "Other",
  "Prospect Hill",
  "Quinnipiac",
  "West River",
  "West Rock",
  "Westville",
  "Wooster Square/Mill River"
]

var categories = [
  "Bins for Trash & Recycling",
  "General Bus Request/Incident",
  "Graffiti",
  "Hangers",
  "Health Complaints",
  "Illegal Dumping",
  "Other",
  "Other - city responsibility",
  "Parking Meter",
  "Parks Request",
  "Policing Issue",
  "Post to Neighbors",
  "Potholes",
  "Private Property Issue",
  "Public Space, Streets and Drains",
  "Request for volunteers",
  "Sidewalks and Curb damage",
  "Signs / Bus Shelters / Pavement Markings",
  "SNOW RELATED",
  "Street Lamp",
  "Traffic/Road Safety",
  "Traffic Signal / Pedestrian Signal",
  "Trash & Recycling",
  "Tree Trimming"
]

var catlabels = ['Bins', 'Bus', 'Graffiti', 'Hangers', 'Health',
                 'Dumping', 'Other', 'City Request', 'Parking',
                 'Parks', 'Policing', 'To Neighbors', 'Potholes',
                 'Private Property', 'Public Space', 'Volunteers',
                 'Sidewalks', 'Signs', 'Snow', 'Street Lamps',
                 'Traffic', 'Signals', 'Trash', 'Trees']

console.log(neighborhoods);
console.log(' ');
console.log(categories);

//setup the nested objects to store the summed data:
var heatdata = new Object();
for (i = 0; i < neighborhoods.length; i++) {
  heatdata[neighborhoods[i]] = {};
  for (j = 0; j < categories.length; j++) {
    heatdata[neighborhoods[i]][categories[j]] = 0;
  }
}

//console.log("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg=Tm-Cvrg-All");

//console.log('start of data:');
/*
$.getJSON("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg=Tm-Cvrg-All", function(issue){
    var idx = 0;
    $.each(issue, function(){
        //console.log(issue[idx]);
        //console.log(issue[idx]['category']);
        //console.log(issue[idx]['neighborhood'] == 'East Rock');
        //document.write(issue+"
");
        heatdata[issue[idx]['neighborhood']][issue[idx]['category']] += 1;
        //document.write(idx+"
");
        idx += 1;

    });
});
*/

//begin d3 section

var nbrhdtip = d3.tip()
  .attr('class', 'd3-nbrhd-tip')
  .offset([-10, 0])
  .html(function(d) {
    return "" + d.fullname + "";
  })

var celltip = d3.tip()
  .attr('class', 'd3-cell-tip')
  .offset([-10, 0])
  .html(function(d) {
    return "Neighborhood: " + d.neighborhoodname + "
"+
           "Category: " + d.categoryname + "
"+
           "Issues: " + d.count + "
";
  })

var svg = d3.select("#heatmap").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

svg.call(nbrhdtip);
svg.call(celltip);

//initialization of the percent complete donut charts:
var percmargin = { top: 0, right: 0, bottom: 0, left: 50 },
    percwidth = 450,
    percheight = 250,
    twoPi = 2 * Math.PI,
    progress = 0,
    total = 1308573, // must be hard-coded if server doesn't report Content-Length
    formatPercent = d3.format(".0%");

var arc = d3.svg.arc()
    .startAngle(0)
    .innerRadius(70)
    .outerRadius(80);

var svg2 = d3.select("#roundissueperc").append("svg")
    .attr("width", percwidth + percmargin.left + percmargin.right)
    .attr("height", percheight + percmargin.top + percmargin.bottom)
    .append("g")
    .attr("transform", "translate(" + (percmargin.left + percwidth / 2) + "," + (percmargin.top + percheight / 2) + ")");

var meter = svg2.append("g")
    .attr("class", "progress-meter");

meter.append("path")
    .attr("class", "background issue-donut")
    .attr("d", arc.endAngle(twoPi));

var foreground = meter.append("path")
    .attr("class", "foreground");

var perctext = meter.append("text")
    .attr("class", 'issue-number-text')
    .attr("text-anchor", "middle")
    .attr("dy", ".0em");

var perctextlabel = meter.append("text")
    .attr("class", 'issue-number-label')
    .attr("text-anchor", "middle")
    .attr("dy", ".85em");

var angular_rotation = 0 / 180 * Math.PI;

var init_ack_start_angle = angular_rotation,
    init_ack_end_angle = angular_rotation,
    init_cmp_start_angle = angular_rotation,
    init_cmp_end_angle = angular_rotation;

function plotHeatmap() {

console.log("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg="+tmCvrg+"&begDate="+begDate+"&endDate="+endDate);
d3.json("http://localhost/nhrc2/php/HeatmapData.php?tmCovrg="+tmCvrg+"&begDate="+begDate+"&endDate="+endDate, function(error, data) {
  if (error) return console.warn(error);

  /*
    loop through data binning in neighborhood and category.
     also keep track of the number of issues that were not
     acknowledged or completed
  */
  var not_acknowledged_count = 0;
  var not_completed_count = 0;
  for (idx = 0; idx < data.length; idx++) {
    heatdata[data[idx]['neighborhood']][data[idx]['category']] += 1;
    if (data[idx]['acknowledged_at'] == null) {
      not_acknowledged_count++;
    }
    if (data[idx]['closed_at'] == null) {
      not_completed_count++;
    }

  }

  console.log('Number of issues: ' + data.length);
  console.log('Number of issues not acknowledged: ' + not_acknowledged_count);
  console.log('Number of issues not completed: ' + not_completed_count);

  var heatdataout = {};
  var neighborhoodarr = [];
  var categoriesarr = [];
  var issuecountarr = [];
  var heatdataarr = [];

  for (i = 0; i < neighborhoods.length; i++) {
    for (j = 0; j < categories.length; j++) {
      neighborhoodarr.push(i);
      categoriesarr.push(j);
      issuecountarr.push(heatdata[neighborhoods[i]][categories[j]]);
      heatdataarr.push({'neighborhoodnum': i,
                        'categorynum': j,
                        'count': heatdata[neighborhoods[i]][categories[j]],
                        'neighborhoodname': neighborhoods[i],
                        'categoryname': categories[j]});
    }
  }
  heatdataout['neighborhoodnum'] = neighborhoodarr;
  heatdataout['categorynum'] = categoriesarr;
  heatdataout['count'] = issuecountarr;
  //heatdataout['neighborhood'] = neighborhoods;
  //heatdataout['category'] = categories;

  //Updated Percentage complete donut plot:
  var acknowledged_frac = 1 - not_acknowledged_count/data.length;
  var completed_frac = 1 - not_completed_count/data.length;

  var new_ack_start_angle = -1 * acknowledged_frac/2. * twoPi + angular_rotation;
  var new_ack_end_angle = acknowledged_frac/2. * twoPi + angular_rotation;
  var new_cmp_start_angle = -1 * completed_frac/2. * twoPi + angular_rotation;
  var new_cmp_end_angle = completed_frac/2. * twoPi + angular_rotation;

  var arc2 = d3.svg.arc()
      .startAngle(angular_rotation)
      .innerRadius(80)
      .outerRadius(90)
      .endAngle(angular_rotation);

  var meter2 = svg2.append("g")
      .attr("class", "progress-meter");

  //remove the old acknowledged-donut it if exists:
  d3.selectAll('.acknowledged-donut')
    .remove();

  var ackdonut = meter2.append("path")
      .attr("class", "acknowledged-donut")
      .attr("d", arc2);

  ackdonut.transition().duration(750).attrTween("d", arc2sTween);

  //ackdonut.transition().duration(750).attrTween("d", arc2eTween);
  console.log('init ack start angle: ');
  console.log(init_ack_start_angle);
  console.log(init_ack_start_angle * 180 / Math.PI);
  console.log('new ack start angle: ' + new_ack_start_angle * 180 / Math.PI);

  console.log('init ack end angle: ' + init_ack_end_angle * 180 / Math.PI);
  console.log('new ack end angle: ' + new_ack_end_angle * 180 / Math.PI);

  function arc2sTween() {
    var i = d3.interpolate(init_ack_start_angle, new_ack_start_angle);
    var j = d3.interpolate(init_ack_end_angle, new_ack_end_angle);
    return function(t) {
      if (t==1) {
        //now that the transition has finished, update the initial
        //value for next time:
        init_ack_start_angle = new_ack_start_angle;
        init_ack_eng_angle = new_ack_end_angle;
      }
      return arc2.startAngle(i(t))
                .endAngle(j(t))();
    };
  }

  var arc3 = d3.svg.arc()
      .startAngle(new_cmp_start_angle)
      .innerRadius(90)
      .outerRadius(100)
      .endAngle(completed_frac/2. * twoPi + angular_rotation);

  var meter3 = svg2.append("g")
      .attr("class", "progress-meter");

  meter3.append("path")
      .attr("class", "completed-donut")
      .attr("d", arc3);

  perctext.text(data.length);
  perctextlabel.text("issues");

  var colorScale = d3.scale.quantile()
        .domain([0, buckets - 1, d3.max(heatdataarr, function (d) { return d.count; })])
        .range(colors);
  //document.write('All '+idx+' records have been summed.');
  //console.log(colorScale);

  catlist = [];
  for (i=0; i < categories.length; i++){
    catlist.push({'abbreviation': catlabels[i], 'fullname': categories[i]});
  }

  var catLabels = svg.selectAll(".catLabel")
      .data(catlist)
      .enter().append("text")
        .text(function (d) { return d.abbreviation; })
        .attr("x", 0)
        .attr("y", function (d, i) { return i * gridSize; })
        .style("text-anchor", "end")
        .attr("transform", "translate(-6," + gridSize / 1.5 + ")")
        .attr("class", function (d, i) { return "dayLabel mono axis"; })
        .on('mouseover', nbrhdtip.show)
        .on('mouseout', nbrhdtip.hide);

    nbrhoodlist = [];
    for (i=0; i < nbrhdlabels.length; i++){
      nbrhoodlist.push({'abbreviation': nbrhdlabels[i], 'fullname': neighborhoods[i]});
    }

    var nbrhdLabels = svg.selectAll(".nbrhdLabel")
        .data(nbrhoodlist)
        .enter().append("text")
        .text(function(d) { return d.abbreviation; })
        .attr("x", function(d, i) { return i * gridSize; })
        .attr("y", 0)
        .style("text-anchor", "middle")
        .attr("transform", "translate(" + gridSize / 2 + ", -6)")
        .attr("class", function(d, i) { return "timeLabel mono axis"; })
        .on('mouseover', nbrhdtip.show)
        .on('mouseout', nbrhdtip.hide);

    //console.log(heatdataout);
    //console.log(heatdataarr);
    //console.log('neighborhoodnum: ');
    //console.log(heatdataout.neighborhoodnum);
    //console.log('categorynum: ');
    //console.log(heatdataout.categorynum);

    var heatMap = svg.selectAll(".issuecell")
        .data(heatdataarr)
        .enter().append("rect")
        .attr("x", function(d) {
          return (+d.neighborhoodnum) * gridSize;
        })
        .attr("y", function(d) { return (+d.categorynum) * gridSize; })
        //.attr("rx", 4)
        //.attr("ry", 4)
        .attr("class", "issuecell bordered")
        .attr("width", gridSize)
        .attr("height", gridSize)
        .style("fill", colors[0])
        .on('mouseover', celltip.show)
        .on('mouseout', celltip.hide);

    heatMap.transition().duration(1000)
        .style("fill", function(d) { return colorScale(d.count); });

    //heatMap.append("text").text(function(d) { return d.count; });

    var legend = svg.selectAll(".legend")
        .data([0].concat(colorScale.quantiles()), function(d) { return d; })
        .enter().append("g")
        .attr("class", "legend");

    legend.append("rect")
      .attr("x", function(d, i) { return legendElementWidth * i; })
      .attr("y", height - 20)
      .attr("width", legendElementWidth)
      .attr("height", gridSize / 2)
      .style("fill", function(d, i) { return colors[i]; });

    legend.append("text")
      .attr("class", "mono")
      .text(function(d) { return "≥ " + Math.round(d); })
      .attr("x", function(d, i) { return legendElementWidth * i + 15; })
      .attr("y", height + gridSize - 20);

    legend.append("text")
      .attr("class", "mono")
      .text("Number of Issues")
      .attr("x", legendElementWidth * 2.5)
      .attr("y", height + 25);

});
};

window.onload = plotHeatmap();<|endoftext|>


<commit_before>(function(exports) {
    var typecheck = require('./typeinference').typecheck,
        nodes = require('./nodes').nodes,
        types = require('./types'),
        parser = require('./parser').parser,
        lexer = require('./lexer');

    // Assigning the nodes to `parser.yy` allows the grammar to access the nodes from
    // the `yy` namespace.
    parser.yy = nodes;

    parser.lexer =  {
        "lex": function() {
            var token = this.tokens[this.pos] ? this.tokens[this.pos++] : ['EOF'];
            this.yytext = token[1];
            this.yylineno = token[2];
            return token[0];
        },
        "setInput": function(tokens) {
            this.tokens = tokens;
            this.pos = 0;
        },
        "upcomingInput": function() {
            return "";
        }
    };

    // Compile an abstract syntax tree (AST) node to JavaScript.
    var data = {};
    var macros = {};
    var indent = 0;
    var getIndent = function(extra) {
        if(!extra) {
            extra = 0;
        }
        var spacing = "";
        var i;
        for(i = 0; i < indent + extra; i++) {
            spacing += "    ";
        }
        return spacing;
    };
    var joinIndent = function(args, extra) {
        var lineIndent = "\n" + getIndent(extra);
        var argIndent = args.join("\n" + getIndent(extra));
        if(argIndent) {
            return argIndent + lineIndent;
        }
        return "";
    };
    var pushIndent = function() {
        indent++;
        return getIndent();
    };
    var popIndent = function() {
        indent--;
        return getIndent();
    };
    var compileNode = function(n) {
        return n.accept({
            // Function definition to JavaScript function.
            visitFunction: function() {
                var getArgs = function(a) {
                    return a.map(function(v) {
                        return v.name;
                    }).join(", ");
                };
                var compiledNodeBody = n.body.map(compileNode);
                var init = [];
                pushIndent();
                if(compiledNodeBody.length > 1) {
                    init.push(compiledNodeBody.slice(0, compiledNodeBody.length - 1).join(';\n' + getIndent()) + ';');
                }
                var lastString = compiledNodeBody[compiledNodeBody.length - 1];
                var varEquals = "";
                if(n.name) {
                    varEquals = "var " + n.name + " = ";
                }
                return varEquals + "function(" + getArgs(n.args) + ") {\n" +
                    getIndent() + joinIndent(init) + "return " + lastString +
                    ";\n" + popIndent() + "}";
            },
            visitIfThenElse: function() {
                var compiledNodeCondition = compileNode(n.condition);
                var compiledNodeIfTrue = n.ifTrue.map(compileNode).join('');
                var compiledNodeIfFalse = n.ifFalse.map(compileNode).join('');
                return "(function() {\n" + pushIndent() + "if(" +
                    compiledNodeCondition + ") {\n" + pushIndent() + "return " +
                    compiledNodeIfTrue + "\n" + popIndent() + "} else {\n" +
                    pushIndent() + "return " + compiledNodeIfFalse + "\n" +
                    popIndent() + "}})();";
            },
            // Let binding to JavaScript variable.
            visitLet: function() {
                return "var " + n.name + " = " + compileNode(n.value) + ";";
            },
            visitData: function() {
                n.tags.forEach(function(tag) {
                    data[tag.name] = n.name;
                });
                var defs = n.tags.map(compileNode);
                return defs.join("\n");
            },
            visitReplacement: function() {
                return n.value;
            },
            visitQuoted: function() {
                var serializeNode = {
                    visitReplacement: function(v) {
                        return "new nodes.Replacement(" + compileNode(v.value) + ")";
                    },
                    visitIdentifier: function(v) {
                        return "new nodes.Identifier(" + JSON.stringify(v.value) + ")";
                    },
                    visitAccess: function(v) {
                        return "new nodes.Access(" + serialize(v.value) + ", " + JSON.stringify(v.property) + ")";
                    },
                    visitCall: function(v) {
                        return "new nodes.Call(" + serialize(v.func) + ", [" + v.args.map(serialize).join(', ') + "])";
                    }
                };
                var serialize = function(v) {
                    return v.accept(serializeNode);
                };
                return serialize(n.value);
            },
            visitMacro: function() {
                var init = n.body.slice(0, n.body.length - 1);
                var last = n.body[n.body.length - 1];
                var code = init.map(compileNode).join('\n') + '\nreturn ' + compileNode(last) + ';';
                macros[n.name] = 'var nodes = this.nodes; ' + code;
            },
            visitReturn: function() {
                return "return __monad__[\"return\"](" + compileNode(n.value) + ");";
            },
            visitBind: function() {
                return "return __monad__[\"bind\"](" + compileNode(n.value) +
                    ", function(" + n.name + ") {\n" + pushIndent() +
                    n.rest.map(compileNode).join("\n" + getIndent()) + "\n" +
                    popIndent() + "});";
            },
            visitDo: function() {
                var compiledInit = [];
                var firstBind;
                var lastBind;
                var lastBindIndex = 0;
                n.body.forEach(function(node, i) {
                    if(node instanceof nodes.Bind) {
                        if(!lastBind) {
                            firstBind = node;
                        } else {
                            lastBind.rest = n.body.slice(lastBindIndex + 1, i + 1);
                        }
                        lastBindIndex = i;
                        lastBind = node;
                    } else {
                        if(!lastBind) {
                            compiledInit.push(compileNode(node));
                        }
                    }
                });
                lastBind.rest = n.body.slice(lastBindIndex + 1);
                return "(function(){\n" + pushIndent() + "var __monad__ = " +
                    compileNode(n.value) + ";\n" + getIndent() +
                    compiledInit.join('\n' + getIndent()) +
                    compileNode(firstBind) + "\n" + popIndent() + "})()";
            },
            visitTag: function() {
                var args = n.vars.map(function(v) {
                    return v.name;
                });
                var setters = n.vars.map(function(v, i) {
                    return "this._" + i + " = " + v.name;
                });
                return "var " + n.name + " = function(" + args.join(", ") + "){" + setters.join(";") + "};";
            },
            visitMatch: function() {
                var cases = n.cases.map(function(c) {
                    var assignments = c.pattern.vars.map(function(a, i) {
                        return "var " + a + " = " + compileNode(n.value) + "._" + i + ";";
                    });
                    return "if(" + compileNode(n.value) + " instanceof " + c.pattern.tag +
                        ") {\n" + getIndent(3) + joinIndent(assignments, 3) + "return " +
                        compileNode(c.value) + "\n" + getIndent(2) + "}";
                });
                return "(function() {\n" + getIndent(2) + cases.join(" else ") + "\n" + getIndent(1) + "})()";
            },
            // Call to JavaScript call.
            visitCall: function() {
                if(macros[n.func.value]) {
                    // Is a macro
                    var f = new Function(macros[n.func.value]);
                    var tree = f.apply({nodes: nodes}, n.args);
                    // TODO: Give an actual env
                    typecheck([tree], {});
                    return compileNode(tree);
                } else if(data[n.func.value]) {
                    // Is a tag
                    return 'new ' + n.func.value + "(" + n.args.map(compileNode).join(", ") + ")";
                }
                return compileNode(n.func) + "(" + n.args.map(compileNode).join(", ") + ")";
            },
            visitAccess: function() {
                return compileNode(n.value) + "." + n.property;
            },
            visitBinaryGenericOperator: function() {
                return [compileNode(n.left), n.name, compileNode(n.right)].join(" ");
            },
            visitBinaryNumberOperator: function() {
                return [compileNode(n.left), n.name, compileNode(n.right)].join(" ");
            },
            // Print all other nodes directly.
            visitComment: function() {
                return n.value;
            },
            visitIdentifier: function() {
                var prefix = '';
                var suffix = '';
                if(data[n.value]) {
                    prefix = 'new ';
                    suffix = '()';
                }
                return prefix + n.value + suffix;
            },
            visitNumber: function() {
                return n.value;
            },
            visitString: function() {
                return n.value;
            },
            visitBoolean: function() {
                return n.value;
            },
            visitUnit: function() {
                return "null";
            },
            visitArray: function() {
                return '[' + n.values.map(compileNode).join(', ') + ']';
            },
            visitObject: function() {
                var key;
                var pairs = [];
                pushIndent();
                for(key in n.values) {
                    pairs.push("\"" + key + "\": " + compileNode(n.values[key]));
                }
                return "{\n" + getIndent() + pairs.join(",\n" + getIndent()) + "\n" + popIndent() + "}";
            }
        });
    };

    var compile = function(source, env, opts) {
        if(!opts) opts = {};

        // Parse the file to an AST.
        var tokens = lexer.tokenise(source);
        var ast = parser.parse(tokens);

        // Typecheck the AST. Any type errors will throw an exception.
        var typeA = new types.Variable();
        var typeB = new types.Variable();
        var resultType = typecheck(ast, env);

        // Output strict JavaScript.
        var output = [];
        if(opts.strict) {
            output.push('"use strict";');
        }
        ast.forEach(function(v) {
            output.push(compileNode(v));
        });

        return {type: resultType, output: output.join('\n')};
    };
    exports.compile = compile;

    var nodeRepl = function() {
        var readline = require('readline');
        var vm = require('vm');
        var stdout = process.stdout;
        var stdin = process.openStdin();
        var repl = readline.createInterface(stdin, stdout);

        var env = {};
        var sandbox = {};

        var name;
        for(name in global) {
            sandbox[name] = global[name];
        }

        repl.setPrompt('roy> ');
        repl.on('close', function() {
            stdin.destroy();
        });
        repl.on('line', function(line) {
            var compiled;
            var output;
            try {
                compiled = compile(line, env);
                output = vm.runInNewContext(compiled.output, sandbox, 'eval');
                if(typeof output != 'undefined') console.log(output + " : " + compiled.type);
            } catch(e) {
                console.log((e.stack || e.toString()) + '\n\n');
            }
            repl.prompt();
        });
        repl.prompt();
    };

    var main = function() {
        if(process.argv.length < 3) {
            nodeRepl();
        }

        var fs = require('fs');
        var filenames = process.argv.slice(2);

        var env = {};
        filenames.forEach(function(filename) {
            // Read the file content.
            var source = fs.readFileSync(filename, 'utf8');

            // Write the JavaScript output.
            var extension = /\.roy$/;
            console.assert(filename.match(extension), 'Filename must end with ".roy"');
            fs.writeFile(filename.replace(extension, '.js'), compile(source, env).output, 'utf8');
        });
    };
    exports.main = main;

    if(!module.parent) {
        main();
    }
})(typeof exports == 'undefined' ? this['./compile'] = {} : exports);
<commit_msg>Add a newline at end of JS<commit_after>(function(exports) {
    var typecheck = require('./typeinference').typecheck,
        nodes = require('./nodes').nodes,
        types = require('./types'),
        parser = require('./parser').parser,
        lexer = require('./lexer');

    // Assigning the nodes to `parser.yy` allows the grammar to access the nodes from
    // the `yy` namespace.
    parser.yy = nodes;

    parser.lexer =  {
        "lex": function() {
            var token = this.tokens[this.pos] ? this.tokens[this.pos++] : ['EOF'];
            this.yytext = token[1];
            this.yylineno = token[2];
            return token[0];
        },
        "setInput": function(tokens) {
            this.tokens = tokens;
            this.pos = 0;
        },
        "upcomingInput": function() {
            return "";
        }
    };

    // Compile an abstract syntax tree (AST) node to JavaScript.
    var data = {};
    var macros = {};
    var indent = 0;
    var getIndent = function(extra) {
        if(!extra) {
            extra = 0;
        }
        var spacing = "";
        var i;
        for(i = 0; i < indent + extra; i++) {
            spacing += "    ";
        }
        return spacing;
    };
    var joinIndent = function(args, extra) {
        var lineIndent = "\n" + getIndent(extra);
        var argIndent = args.join("\n" + getIndent(extra));
        if(argIndent) {
            return argIndent + lineIndent;
        }
        return "";
    };
    var pushIndent = function() {
        indent++;
        return getIndent();
    };
    var popIndent = function() {
        indent--;
        return getIndent();
    };
    var compileNode = function(n) {
        return n.accept({
            // Function definition to JavaScript function.
            visitFunction: function() {
                var getArgs = function(a) {
                    return a.map(function(v) {
                        return v.name;
                    }).join(", ");
                };
                var compiledNodeBody = n.body.map(compileNode);
                var init = [];
                pushIndent();
                if(compiledNodeBody.length > 1) {
                    init.push(compiledNodeBody.slice(0, compiledNodeBody.length - 1).join(';\n' + getIndent()) + ';');
                }
                var lastString = compiledNodeBody[compiledNodeBody.length - 1];
                var varEquals = "";
                if(n.name) {
                    varEquals = "var " + n.name + " = ";
                }
                return varEquals + "function(" + getArgs(n.args) + ") {\n" +
                    getIndent() + joinIndent(init) + "return " + lastString +
                    ";\n" + popIndent() + "}";
            },
            visitIfThenElse: function() {
                var compiledNodeCondition = compileNode(n.condition);
                var compiledNodeIfTrue = n.ifTrue.map(compileNode).join('');
                var compiledNodeIfFalse = n.ifFalse.map(compileNode).join('');
                return "(function() {\n" + pushIndent() + "if(" +
                    compiledNodeCondition + ") {\n" + pushIndent() + "return " +
                    compiledNodeIfTrue + "\n" + popIndent() + "} else {\n" +
                    pushIndent() + "return " + compiledNodeIfFalse + "\n" +
                    popIndent() + "}})();";
            },
            // Let binding to JavaScript variable.
            visitLet: function() {
                return "var " + n.name + " = " + compileNode(n.value) + ";";
            },
            visitData: function() {
                n.tags.forEach(function(tag) {
                    data[tag.name] = n.name;
                });
                var defs = n.tags.map(compileNode);
                return defs.join("\n");
            },
            visitReplacement: function() {
                return n.value;
            },
            visitQuoted: function() {
                var serializeNode = {
                    visitReplacement: function(v) {
                        return "new nodes.Replacement(" + compileNode(v.value) + ")";
                    },
                    visitIdentifier: function(v) {
                        return "new nodes.Identifier(" + JSON.stringify(v.value) + ")";
                    },
                    visitAccess: function(v) {
                        return "new nodes.Access(" + serialize(v.value) + ", " + JSON.stringify(v.property) + ")";
                    },
                    visitCall: function(v) {
                        return "new nodes.Call(" + serialize(v.func) + ", [" + v.args.map(serialize).join(', ') + "])";
                    }
                };
                var serialize = function(v) {
                    return v.accept(serializeNode);
                };
                return serialize(n.value);
            },
            visitMacro: function() {
                var init = n.body.slice(0, n.body.length - 1);
                var last = n.body[n.body.length - 1];
                var code = init.map(compileNode).join('\n') + '\nreturn ' + compileNode(last) + ';';
                macros[n.name] = 'var nodes = this.nodes; ' + code;
            },
            visitReturn: function() {
                return "return __monad__[\"return\"](" + compileNode(n.value) + ");";
            },
            visitBind: function() {
                return "return __monad__[\"bind\"](" + compileNode(n.value) +
                    ", function(" + n.name + ") {\n" + pushIndent() +
                    n.rest.map(compileNode).join("\n" + getIndent()) + "\n" +
                    popIndent() + "});";
            },
            visitDo: function() {
                var compiledInit = [];
                var firstBind;
                var lastBind;
                var lastBindIndex = 0;
                n.body.forEach(function(node, i) {
                    if(node instanceof nodes.Bind) {
                        if(!lastBind) {
                            firstBind = node;
                        } else {
                            lastBind.rest = n.body.slice(lastBindIndex + 1, i + 1);
                        }
                        lastBindIndex = i;
                        lastBind = node;
                    } else {
                        if(!lastBind) {
                            compiledInit.push(compileNode(node));
                        }
                    }
                });
                lastBind.rest = n.body.slice(lastBindIndex + 1);
                return "(function(){\n" + pushIndent() + "var __monad__ = " +
                    compileNode(n.value) + ";\n" + getIndent() +
                    compiledInit.join('\n' + getIndent()) +
                    compileNode(firstBind) + "\n" + popIndent() + "})()";
            },
            visitTag: function() {
                var args = n.vars.map(function(v) {
                    return v.name;
                });
                var setters = n.vars.map(function(v, i) {
                    return "this._" + i + " = " + v.name;
                });
                return "var " + n.name + " = function(" + args.join(", ") + "){" + setters.join(";") + "};";
            },
            visitMatch: function() {
                var cases = n.cases.map(function(c) {
                    var assignments = c.pattern.vars.map(function(a, i) {
                        return "var " + a + " = " + compileNode(n.value) + "._" + i + ";";
                    });
                    return "if(" + compileNode(n.value) + " instanceof " + c.pattern.tag +
                        ") {\n" + getIndent(3) + joinIndent(assignments, 3) + "return " +
                        compileNode(c.value) + "\n" + getIndent(2) + "}";
                });
                return "(function() {\n" + getIndent(2) + cases.join(" else ") + "\n" + getIndent(1) + "})()";
            },
            // Call to JavaScript call.
            visitCall: function() {
                if(macros[n.func.value]) {
                    // Is a macro
                    var f = new Function(macros[n.func.value]);
                    var tree = f.apply({nodes: nodes}, n.args);
                    // TODO: Give an actual env
                    typecheck([tree], {});
                    return compileNode(tree);
                } else if(data[n.func.value]) {
                    // Is a tag
                    return 'new ' + n.func.value + "(" + n.args.map(compileNode).join(", ") + ")";
                }
                return compileNode(n.func) + "(" + n.args.map(compileNode).join(", ") + ")";
            },
            visitAccess: function() {
                return compileNode(n.value) + "." + n.property;
            },
            visitBinaryGenericOperator: function() {
                return [compileNode(n.left), n.name, compileNode(n.right)].join(" ");
            },
            visitBinaryNumberOperator: function() {
                return [compileNode(n.left), n.name, compileNode(n.right)].join(" ");
            },
            // Print all other nodes directly.
            visitComment: function() {
                return n.value;
            },
            visitIdentifier: function() {
                var prefix = '';
                var suffix = '';
                if(data[n.value]) {
                    prefix = 'new ';
                    suffix = '()';
                }
                return prefix + n.value + suffix;
            },
            visitNumber: function() {
                return n.value;
            },
            visitString: function() {
                return n.value;
            },
            visitBoolean: function() {
                return n.value;
            },
            visitUnit: function() {
                return "null";
            },
            visitArray: function() {
                return '[' + n.values.map(compileNode).join(', ') + ']';
            },
            visitObject: function() {
                var key;
                var pairs = [];
                pushIndent();
                for(key in n.values) {
                    pairs.push("\"" + key + "\": " + compileNode(n.values[key]));
                }
                return "{\n" + getIndent() + pairs.join(",\n" + getIndent()) + "\n" + popIndent() + "}";
            }
        });
    };

    var compile = function(source, env, opts) {
        if(!opts) opts = {};

        // Parse the file to an AST.
        var tokens = lexer.tokenise(source);
        var ast = parser.parse(tokens);

        // Typecheck the AST. Any type errors will throw an exception.
        var typeA = new types.Variable();
        var typeB = new types.Variable();
        var resultType = typecheck(ast, env);

        // Output strict JavaScript.
        var output = [];
        if(opts.strict) {
            output.push('"use strict";');
        }
        ast.forEach(function(v) {
            output.push(compileNode(v));
        });
        // Add a newline at the end
        output.push("");

        return {type: resultType, output: output.join('\n')};
    };
    exports.compile = compile;

    var nodeRepl = function() {
        var readline = require('readline');
        var vm = require('vm');
        var stdout = process.stdout;
        var stdin = process.openStdin();
        var repl = readline.createInterface(stdin, stdout);

        var env = {};
        var sandbox = {};

        var name;
        for(name in global) {
            sandbox[name] = global[name];
        }

        repl.setPrompt('roy> ');
        repl.on('close', function() {
            stdin.destroy();
        });
        repl.on('line', function(line) {
            var compiled;
            var output;
            try {
                compiled = compile(line, env);
                output = vm.runInNewContext(compiled.output, sandbox, 'eval');
                if(typeof output != 'undefined') console.log(output + " : " + compiled.type);
            } catch(e) {
                console.log((e.stack || e.toString()) + '\n\n');
            }
            repl.prompt();
        });
        repl.prompt();
    };

    var main = function() {
        if(process.argv.length < 3) {
            nodeRepl();
        }

        var fs = require('fs');
        var filenames = process.argv.slice(2);

        var env = {};
        filenames.forEach(function(filename) {
            // Read the file content.
            var source = fs.readFileSync(filename, 'utf8');

            // Write the JavaScript output.
            var extension = /\.roy$/;
            console.assert(filename.match(extension), 'Filename must end with ".roy"');
            fs.writeFile(filename.replace(extension, '.js'), compile(source, env).output, 'utf8');
        });
    };
    exports.main = main;

    if(!module.parent) {
        main();
    }
})(typeof exports == 'undefined' ? this['./compile'] = {} : exports);
<|endoftext|>


<commit_before>#!/usr/bin/env node

import 'instapromise';

import _ from 'lodash-node';
import bunyan from 'bunyan';
import crayon from '@ccheever/crayon';
import glob from 'glob';
import simpleSpinner from '@exponent/simple-spinner';
import url from 'url';

import program, { Command } from 'commander';
import {
  Config,
  Logger,
  NotificationCode,
} from 'xdl';

import log from './log';
import update from './update';
import urlOpts from './urlOpts';

Command.prototype.urlOpts = function() {
  urlOpts.addOptions(this);
  return this;
};

Command.prototype.addUrlOption = function() {
  urlOpts.addUrlOption(this);
  return this;
};

Command.prototype.asyncAction = function(asyncFn) {
  return this.action(async (...args) => {
    try {
      let options = _.last(args).parent;
      if (options.output === 'raw') {
        log.config.raw = true;
        process.env['PM2_SILENT'] = true;
      }
      await asyncFn(...args);
    } catch (err) {
      if (err._isCommandError) {
        log.error(err.message);
      } else if (err._isApiError) {
        log.error(crayon.red(err.message));
      } else {
        log.error(err.message);
        crayon.gray.error(err.stack);
      }
    }
  });
};

Command.prototype.asyncActionProjectDir = function(asyncFn) {
  return this.asyncAction(async (projectDir, ...args) => {
    if (!projectDir) {
      projectDir = process.cwd();
    }

    return asyncFn(projectDir, ...args);
  });
};

async function runAsync() {
  try {
    _registerLogs();

    if (process.env.SERVER_URL) {
      let serverUrl = process.env.SERVER_URL;
      if (!serverUrl.startsWith('http')) {
        serverUrl = 'http://' + serverUrl;
      }
      let parsedUrl = url.parse(serverUrl);
      Config.api.host = parsedUrl.hostname;
      Config.api.port = parsedUrl.port;
    }

    Config.developerTool = 'exp';

    program.name = 'exp';
    program
      .version(require('../package.json').version)
      .option('-o, --output [format]', 'Output format. pretty (default), raw');
    glob.sync('commands/*.js', {
      cwd: __dirname,
    }).forEach(file => {
      const commandModule = require('./' + file);
      if (typeof commandModule === 'function') {
        commandModule(program);
      } else if (typeof commandModule.default === 'function') {
        commandModule.default(program);
      } else {
        log.error(`'${file}.js' is not a properly formatted command.`);
      }
    });

    if (process.env.EXPONENT_DEBUG) {
      glob.sync('debug_commands/*.js', {
        cwd: __dirname,
      }).forEach(file => {
        require('./' + file)(program);
      });
    }

    program.parse(process.argv);

    let subCommand = process.argv[2];
    if (subCommand) {
      let commands = [];
      program.commands.forEach(command => {
        commands.push(command['_name']);
        let alias = command['_alias'];
        if (alias) {
          commands.push(alias);
        }
      });
      if (!_.includes(commands, subCommand)) {
        console.log(`"${subCommand}" is not an exp command. See "exp --help" for the full list of commands.`);
      }
    } else {
      program.help();
    }
  } catch (e) {
    console.error(e);
    throw e;
  }
}

async function checkForUpdateAsync() {
  let checkForUpdate = await update.checkForExpUpdateAsync();
  switch (checkForUpdate.state) {
    case 'up-to-date':
      break;
    case 'out-of-date':
      crayon.green.error(checkForUpdate.message);
      break;
    case 'ahead-of-published':
      crayon.cyan.error(checkForUpdate.message);
      break;
    case 'deprecated':
      crayon.yellow.bold.error(checkForUpdate.message);
      break;
    default:
      log.error("Confused about what version of exp you have?");
  }
}

function _registerLogs() {
  let stream = {
    level: 'info',
    stream: {
      write: (chunk) => {
        if (chunk.code) {
          switch (chunk.code) {
            case NotificationCode.START_LOADING:
              simpleSpinner.start();
              return;
            case NotificationCode.STOP_LOADING:
              simpleSpinner.stop();
              return;
          }
        }

        if (chunk.level <= bunyan.INFO) {
          log(chunk.msg);
        } else {
          log.error(chunk.msg);
        }
      },
    },
    type: 'raw',
  };

  Logger.notifications.addStream(stream);
  Logger.global.addStream(stream);
}

if (require.main === module) {
  Promise.all([
    runAsync(),
    checkForUpdateAsync(),
  ]);
}
<commit_msg>More info on new project screen. Fix warnings in exp.<commit_after>#!/usr/bin/env node

import 'instapromise';

import _ from 'lodash-node';
import bunyan from 'bunyan';
import crayon from '@ccheever/crayon';
import glob from 'glob';
import simpleSpinner from '@exponent/simple-spinner';
import url from 'url';

import program, { Command } from 'commander';
import {
  Config,
  Logger,
  NotificationCode,
} from 'xdl';

import log from './log';
import update from './update';
import urlOpts from './urlOpts';

Command.prototype.urlOpts = function() {
  urlOpts.addOptions(this);
  return this;
};

Command.prototype.addUrlOption = function() {
  urlOpts.addUrlOption(this);
  return this;
};

Command.prototype.asyncAction = function(asyncFn) {
  return this.action(async (...args) => {
    try {
      let options = _.last(args).parent;
      if (options.output === 'raw') {
        log.config.raw = true;
        process.env['PM2_SILENT'] = true;
      }
      await asyncFn(...args);
    } catch (err) {
      if (err._isCommandError) {
        log.error(err.message);
      } else if (err._isApiError) {
        log.error(crayon.red(err.message));
      } else {
        log.error(err.message);
        crayon.gray.error(err.stack);
      }
    }
  });
};

Command.prototype.asyncActionProjectDir = function(asyncFn) {
  return this.asyncAction(async (projectDir, ...args) => {
    if (!projectDir) {
      projectDir = process.cwd();
    }

    return asyncFn(projectDir, ...args);
  });
};

async function runAsync() {
  try {
    _registerLogs();

    if (process.env.SERVER_URL) {
      let serverUrl = process.env.SERVER_URL;
      if (!serverUrl.startsWith('http')) {
        serverUrl = 'http://' + serverUrl;
      }
      let parsedUrl = url.parse(serverUrl);
      Config.api.host = parsedUrl.hostname;
      Config.api.port = parsedUrl.port;
    }

    Config.developerTool = 'exp';

    program.name = 'exp';
    program
      .version(require('../package.json').version)
      .option('-o, --output [format]', 'Output format. pretty (default), raw');
    glob.sync('commands/*.js', {
      cwd: __dirname,
    }).forEach(file => {
      const commandModule = require('./' + file);
      if (typeof commandModule === 'function') {
        commandModule(program);
      } else if (typeof commandModule.default === 'function') {
        commandModule.default(program);
      } else {
        log.error(`'${file}.js' is not a properly formatted command.`);
      }
    });

    if (process.env.EXPONENT_DEBUG) {
      glob.sync('debug_commands/*.js', {
        cwd: __dirname,
      }).forEach(file => {
        require('./' + file)(program);
      });
    }

    program.parse(process.argv);

    let subCommand = process.argv[2];
    if (subCommand) {
      let commands = [];
      program.commands.forEach(command => {
        commands.push(command['_name']);
        let alias = command['_alias'];
        if (alias) {
          commands.push(alias);
        }
      });
      if (!_.includes(commands, subCommand)) {
        console.log(`"${subCommand}" is not an exp command. See "exp --help" for the full list of commands.`);
      }
    } else {
      program.help();
    }
  } catch (e) {
    console.error(e);
    throw e;
  }
}

async function checkForUpdateAsync() {
  let checkForUpdate = await update.checkForExpUpdateAsync();
  switch (checkForUpdate.state) {
    case 'up-to-date':
      break;
    case 'out-of-date':
      crayon.green.error(checkForUpdate.message);
      break;
    case 'ahead-of-published':
      crayon.cyan.error(checkForUpdate.message);
      break;
    case 'deprecated':
      crayon.yellow.bold.error(checkForUpdate.message);
      break;
    default:
      log.error("Confused about what version of exp you have?");
  }
}

function _registerLogs() {
  let stream = {
    level: 'info',
    stream: {
      write: (chunk) => {
        if (chunk.code) {
          switch (chunk.code) {
            case NotificationCode.START_LOADING:
              simpleSpinner.start();
              return;
            case NotificationCode.STOP_LOADING:
              simpleSpinner.stop();
              return;
          }
        }

        if (chunk.level <= bunyan.INFO) {
          log(chunk.msg);
        } else if (chunk.level === bunyan.WARN) {
          log.warn(chunk.msg);
        } else {
          log.error(chunk.msg);
        }
      },
    },
    type: 'raw',
  };

  Logger.notifications.addStream(stream);
  Logger.global.addStream(stream);
}

if (require.main === module) {
  Promise.all([
    runAsync(),
    checkForUpdateAsync(),
  ]);
}
<|endoftext|>


<commit_before>define([
  'donation_data/config',
  'donation_data/feed',
  'donation_panel/donation',
  'donation_panel/menu'
], function(config, feed, Donation, menu) {
  var nullOrder = {build: []}
  var unfinished = function(donation) {return !donation.finished()}

  var knownDonations = {}

  var makeModel = function(d) {
    var dm = Donation(d)
    dm.matchPlayers(viewModel.playerNames())
    dm.matchPlanets(viewModel.planetNames())
    dm.matchMatches(config.match_tags(), config.current_match())
    knownDonations[d.id] = dm
    return dm
  }

  var integrateDonations = function(incoming) {
    incoming.forEach(function(d) {
      if (knownDonations[d.id]) {
        var matching = viewModel.donations().filter(function(dm) { console.log(dm.id, d.id); return dm.id == d.id})
        if (matching.length < 1) return
        var dm = matching[0]
        if (dm.selected() || dm.finished()) return
        viewModel.donations.replace(dm, makeModel(d))
      } else {
        viewModel.donations.unshift(makeModel(d))
      }
    })
    viewModel.donations(stableSort(viewModel.donations(), function(a, b) {return a.priority - b.priority}))
  }

  // https://stackoverflow.com/a/45422645/30203
  var stableSort = function(array, compareFunction) {
    'use strict'

    var length = array.length
    var entries = Array(length)
    var index

    // wrap values with initial indices
    for (index = 0; index < length; index++) {
      entries[index] = [index, array[index]]
    }

    // sort with fallback based on initial indices
    entries.sort(function (a, b) {
      var comparison = Number(compareFunction(a[1], b[1]))
      return comparison !== 0 ? comparison : a[0] - b[0]
    })

    // re-map original array to stable sorted values
    for (index = 0; index < length; index++) {
      array[index] = entries[index][1]
    }

    return array
  }

  var autoUpdate = function() {
    if (feed[config.feed()].subscribe) {
      viewModel.update()
      feed[config.feed()].subscribe(integrateDonations)
    } else {
      viewModel.update()
      setTimeout(autoUpdate, 10000)
    }
  }

  var viewModel = {
    visible: ko.observable(true),
    name: ko.observable(config.name()),
    cheatAllowCreateUnit: ko.observable(false).extend({session: 'cheat_allow_create_unit'}),
    playerNames: ko.observableArray([]),
    planetNames: ko.observableArray([]),
    donations: ko.observableArray([]),
    discountLevel: ko.observable(0),
    currentDonation: ko.observable(Donation({})),
    currentOrder: ko.observable(nullOrder),
    select: function(donation) {
      viewModel.currentDonation().selected(false)
      viewModel.currentDonation(donation)
      donation.selected(true)
      api.Panel.message('devmode', 'improved_player_control_change', donation.matchingPlayerIndex)
      if (donation.matchingPlanetIndex != -1) {
        api.Panel.message(api.Panel.parentId, 'planets.click', donation.matchingPlanetIndex)
        api.audio.playSound('/SE/UI/UI_planet_switch_select');
      }

      if (!viewModel.cheatAllowCreateUnit()) {
        donation.finished(true)
        return
      }

      if (donation.unexecutedOrders().length > 0) {
        viewModel.currentOrder(donation.unexecutedOrders.shift())
      } else {
        donation.finished(true)
        viewModel.currentOrder(nullOrder)
      }
    },
    cancel: function(donation) {
      donation.finished(true)
    },
    executeNext: function() {
      donation = viewModel.currentDonation()
      if (donation.unexecutedOrders().length > 0) {
        viewModel.currentOrder(donation.unexecutedOrders.shift())
      } else {
        donation.finished(true)
        viewModel.select(viewModel.donationWithOrders())
      }
    },
    donationWithOrders: function() {
      return viewModel.donations().filter(function(donation) {
        return donation.unexecutedOrders().length > 0 && !donation.insufficient()
      }).reverse()[0] || Donation({})
    },
    update: function() {
      viewModel.name(config.name())
      feed[config.feed()].update().then(integrateDonations)
    },
    reap: function() {
      viewModel.donations(viewModel.donations().filter(unfinished))
    },
    manualUpdate: function() {
      viewModel.reap()
      viewModel.update()
    },
    ready: function() {
      console.log('ready')
      api.Panel.message(api.Panel.parentId, 'request_player_names',
        ['donation_panel', 'player_names'])
      api.Panel.message(api.Panel.parentId, 'request_planet_names',
        ['donation_panel', 'planet_names'])
      setTimeout(autoUpdate, 1000)
    },
  }

  viewModel.currentCode = ko.computed(function() {
    return viewModel.currentOrder().code
  })
  viewModel.currentMin = ko.computed(function() {
    return viewModel.currentOrder().donation
  })
  viewModel.currentOrder.subscribe(function(order) {
    api.Panel.message(api.Panel.parentId, 'sandbox_menu_item', order)
  })
  viewModel.playerNames.subscribe(function(names) {
    viewModel.donations().forEach(function(donation) {
      donation.matchPlayers(names)
    })
  })
  viewModel.planetNames.subscribe(function(names) {
    viewModel.donations().forEach(function(donation) {
      donation.matchPlanets(names)
    })
  })
  viewModel.discountLevel.subscribe(function(level) {
    menu.setDiscountLevel(level)
    viewModel.donations(viewModel.donations().map(Donation))
    viewModel.currentDonation(Donation({}))
    viewModel.currentOrder(nullOrder)
    viewModel.donations(stableSort(viewModel.donations(), function(a, b) {return a.priority - b.priority}))
  })

  return viewModel
})
<commit_msg>remove debug statement, subscribe initial update handled by data<commit_after>define([
  'donation_data/config',
  'donation_data/feed',
  'donation_panel/donation',
  'donation_panel/menu'
], function(config, feed, Donation, menu) {
  var nullOrder = {build: []}
  var unfinished = function(donation) {return !donation.finished()}

  var knownDonations = {}

  var makeModel = function(d) {
    var dm = Donation(d)
    dm.matchPlayers(viewModel.playerNames())
    dm.matchPlanets(viewModel.planetNames())
    dm.matchMatches(config.match_tags(), config.current_match())
    knownDonations[d.id] = dm
    return dm
  }

  var integrateDonations = function(incoming) {
    incoming.forEach(function(d) {
      if (knownDonations[d.id]) {
        var matching = viewModel.donations().filter(function(dm) { return dm.id == d.id})
        if (matching.length < 1) return
        var dm = matching[0]
        if (dm.selected() || dm.finished()) return
        viewModel.donations.replace(dm, makeModel(d))
      } else {
        viewModel.donations.unshift(makeModel(d))
      }
    })
    viewModel.donations(stableSort(viewModel.donations(), function(a, b) {return a.priority - b.priority}))
  }

  // https://stackoverflow.com/a/45422645/30203
  var stableSort = function(array, compareFunction) {
    'use strict'

    var length = array.length
    var entries = Array(length)
    var index

    // wrap values with initial indices
    for (index = 0; index < length; index++) {
      entries[index] = [index, array[index]]
    }

    // sort with fallback based on initial indices
    entries.sort(function (a, b) {
      var comparison = Number(compareFunction(a[1], b[1]))
      return comparison !== 0 ? comparison : a[0] - b[0]
    })

    // re-map original array to stable sorted values
    for (index = 0; index < length; index++) {
      array[index] = entries[index][1]
    }

    return array
  }

  var autoUpdate = function() {
    if (feed[config.feed()].subscribe) {
      feed[config.feed()].subscribe(integrateDonations)
    } else {
      viewModel.update()
      setTimeout(autoUpdate, 10000)
    }
  }

  var viewModel = {
    visible: ko.observable(true),
    name: ko.observable(config.name()),
    cheatAllowCreateUnit: ko.observable(false).extend({session: 'cheat_allow_create_unit'}),
    playerNames: ko.observableArray([]),
    planetNames: ko.observableArray([]),
    donations: ko.observableArray([]),
    discountLevel: ko.observable(0),
    currentDonation: ko.observable(Donation({})),
    currentOrder: ko.observable(nullOrder),
    select: function(donation) {
      viewModel.currentDonation().selected(false)
      viewModel.currentDonation(donation)
      donation.selected(true)
      api.Panel.message('devmode', 'improved_player_control_change', donation.matchingPlayerIndex)
      if (donation.matchingPlanetIndex != -1) {
        api.Panel.message(api.Panel.parentId, 'planets.click', donation.matchingPlanetIndex)
        api.audio.playSound('/SE/UI/UI_planet_switch_select');
      }

      if (!viewModel.cheatAllowCreateUnit()) {
        donation.finished(true)
        return
      }

      if (donation.unexecutedOrders().length > 0) {
        viewModel.currentOrder(donation.unexecutedOrders.shift())
      } else {
        donation.finished(true)
        viewModel.currentOrder(nullOrder)
      }
    },
    cancel: function(donation) {
      donation.finished(true)
    },
    executeNext: function() {
      donation = viewModel.currentDonation()
      if (donation.unexecutedOrders().length > 0) {
        viewModel.currentOrder(donation.unexecutedOrders.shift())
      } else {
        donation.finished(true)
        viewModel.select(viewModel.donationWithOrders())
      }
    },
    donationWithOrders: function() {
      return viewModel.donations().filter(function(donation) {
        return donation.unexecutedOrders().length > 0 && !donation.insufficient()
      }).reverse()[0] || Donation({})
    },
    update: function() {
      viewModel.name(config.name())
      feed[config.feed()].update().then(integrateDonations)
    },
    reap: function() {
      viewModel.donations(viewModel.donations().filter(unfinished))
    },
    manualUpdate: function() {
      viewModel.reap()
      viewModel.update()
    },
    ready: function() {
      console.log('ready')
      api.Panel.message(api.Panel.parentId, 'request_player_names',
        ['donation_panel', 'player_names'])
      api.Panel.message(api.Panel.parentId, 'request_planet_names',
        ['donation_panel', 'planet_names'])
      setTimeout(autoUpdate, 1000)
    },
  }

  viewModel.currentCode = ko.computed(function() {
    return viewModel.currentOrder().code
  })
  viewModel.currentMin = ko.computed(function() {
    return viewModel.currentOrder().donation
  })
  viewModel.currentOrder.subscribe(function(order) {
    api.Panel.message(api.Panel.parentId, 'sandbox_menu_item', order)
  })
  viewModel.playerNames.subscribe(function(names) {
    viewModel.donations().forEach(function(donation) {
      donation.matchPlayers(names)
    })
  })
  viewModel.planetNames.subscribe(function(names) {
    viewModel.donations().forEach(function(donation) {
      donation.matchPlanets(names)
    })
  })
  viewModel.discountLevel.subscribe(function(level) {
    menu.setDiscountLevel(level)
    viewModel.donations(viewModel.donations().map(Donation))
    viewModel.currentDonation(Donation({}))
    viewModel.currentOrder(nullOrder)
    viewModel.donations(stableSort(viewModel.donations(), function(a, b) {return a.priority - b.priority}))
  })

  return viewModel
})
<|endoftext|>


<commit_before>function Viz(src) {
  var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
  var format = options.format === undefined ? "svg" : options.format;
  var engine = options.engine === undefined ? "dot" : options.engine;
  var yInvert = options.yInvert === undefined ? false : options.yInvert;
  var scale = options.scale;
  var totalMemory = options.totalMemory;
  var files = options.files === undefined ? [] : options.files;
  var images = options.images === undefined ? [] : options.images;
  var i;

  for (i = 0; i < images.length; i++) {
    files.push({ path: images[i].path, data: "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg width=\"" + images[i].width + "\" height=\"" + images[i].height + "\"></svg>" });
  }

  if (format == "png-image-element") {
    return Viz.svgXmlToPngImageElement(render(src, "svg", engine, yInvert, totalMemory, files), scale);
  } else {
    return render(src, format, engine, yInvert, totalMemory, files);
  }
}

function render(src, format, engine, yInvert, totalMemory, files) {
  var graphviz = Module({ TOTAL_MEMORY: totalMemory });
  var i;

  for (i = 0; i < files.length; i++) {
    graphviz["ccall"]("vizCreateFile", "number", ["string", "string"], [files[i].path, files[i].data]);
  }

  graphviz["ccall"]("vizSetY_invert", "number", ["number"], [yInvert ? 1 : 0]);

  var resultPointer = graphviz["ccall"]("vizRenderFromString", "number", ["string", "string", "string"], [src, format, engine]);
  var resultString = graphviz["Pointer_stringify"](resultPointer);

  var errorMessagePointer = graphviz["ccall"]("vizLastErrorMessage", "number", [], []);
  var errorMessageString = graphviz["Pointer_stringify"](errorMessagePointer);

  if (errorMessageString != "") {
    throw new Error(errorMessageString);
  }

  return resultString;
}

// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
function b64EncodeUnicode(str) {
  return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
    return String.fromCharCode('0x' + p1);
  }));
}

Viz.svgXmlToPngImageElement = function(svgXml, scale, callback) {
  if (scale === undefined) {
    if ("devicePixelRatio" in window && window.devicePixelRatio > 1) {
      scale = window.devicePixelRatio;
    } else {
      scale = 1;
    }
  }

  var pngImage = new Image();

  try {
    if (typeof fabric === "object" && fabric.loadSVGFromString) {
      fabric.loadSVGFromString(svgXml, function(objects, options) {
        // If there's something wrong with the SVG, Fabric may return an empty array of objects. Graphviz appears to give us at least one <g> element back even given an empty graph, so we will assume an error in this case.
        if (objects.length == 0) {
          if (callback !== undefined) {
            callback(new Error("Error loading SVG with Fabric"));
            return;
          } else {
            throw new Error("Error loading SVG with Fabric");
          }
        }

        var element = document.createElement("canvas");
        element.width = options.width;
        element.height = options.height;

        var canvas = new fabric.Canvas(element, { enableRetinaScaling: false });
        var obj = fabric.util.groupSVGElements(objects, options);
        canvas.add(obj).renderAll();

        pngImage.src = canvas.toDataURL({ multiplier: scale });
        pngImage.width = options.width;
        pngImage.height = options.height;

        if (callback !== undefined) {
          callback(null, pngImage);
        }
      });
    } else {
      var svgImage = new Image();

      svgImage.onload = function() {
        var canvas = document.createElement("canvas");
        canvas.width = svgImage.width * scale;
        canvas.height = svgImage.height * scale;

        var context = canvas.getContext("2d");
        context.drawImage(svgImage, 0, 0, canvas.width, canvas.height);

        pngImage.src = canvas.toDataURL("image/png");
        pngImage.width = svgImage.width;
        pngImage.height = svgImage.height;

        if (callback !== undefined) {
          callback(null, pngImage);
        }
      }

      svgImage.onerror = function(e) {
        var error;

        if ('error' in e) {
          error = e.error;
        } else {
          error = new Error('Error loading SVG');
        }

        if (callback !== undefined) {
          callback(error);
        } else {
          throw error;
        }
      }

      svgImage.src = "data:image/svg+xml;base64," + b64EncodeUnicode(svgXml);
    }
  } catch (e) {
    if (callback !== undefined) {
      callback(e);
    } else {
      throw e;
    }
  }

  if (callback === undefined) {
    return pngImage;
  }
}

Viz.svgXmlToPngBase64 = function(svgXml, scale, callback) {
  Viz.svgXmlToPngImageElement(svgXml, scale, function(err, image) {
    if (err) {
      callback(err);
    } else {
      callback(null, image.src.slice("data:image/png;base64,".length));
    }
  });
}
<commit_msg>Simplify option defaults.<commit_after>var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };

function Viz(src) {
  var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

  options = _extends({ format: "svg", engine: "dot", files: [], images: [] }, options);

  for (var i = 0; i < options.images.length; i++) {
    options.files.push({ path: options.images[i].path, data: "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<svg width=\"" + options.images[i].width + "\" height=\"" + options.images[i].height + "\"></svg>" });
  }

  if (options.format == "png-image-element") {
    var result = render(src, _extends({}, options, { format: "svg" }));
    return Viz.svgXmlToPngImageElement(result, options.scale);
  } else {
    return render(src, options);
  }
}

function render(src, options) {
  var graphviz = Module({ TOTAL_MEMORY: options.totalMemory });

  for (var i = 0; i < options.files.length; i++) {
    graphviz["ccall"]("vizCreateFile", "number", ["string", "string"], [options.files[i].path, options.files[i].data]);
  }

  if (options.yInvert) {
    graphviz["ccall"]("vizSetY_invert", "number", ["number"], [1]);
  }

  var resultPointer = graphviz["ccall"]("vizRenderFromString", "number", ["string", "string", "string"], [src, options.format, options.engine]);
  var resultString = graphviz["Pointer_stringify"](resultPointer);

  var errorMessagePointer = graphviz["ccall"]("vizLastErrorMessage", "number", [], []);
  var errorMessageString = graphviz["Pointer_stringify"](errorMessagePointer);

  if (errorMessageString != "") {
    throw new Error(errorMessageString);
  }

  return resultString;
}

// https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding
function b64EncodeUnicode(str) {
  return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
    return String.fromCharCode('0x' + p1);
  }));
}

Viz.svgXmlToPngImageElement = function(svgXml, scale, callback) {
  if (scale === undefined) {
    if ("devicePixelRatio" in window && window.devicePixelRatio > 1) {
      scale = window.devicePixelRatio;
    } else {
      scale = 1;
    }
  }

  var pngImage = new Image();

  try {
    if (typeof fabric === "object" && fabric.loadSVGFromString) {
      fabric.loadSVGFromString(svgXml, function(objects, options) {
        // If there's something wrong with the SVG, Fabric may return an empty array of objects. Graphviz appears to give us at least one <g> element back even given an empty graph, so we will assume an error in this case.
        if (objects.length == 0) {
          if (callback !== undefined) {
            callback(new Error("Error loading SVG with Fabric"));
            return;
          } else {
            throw new Error("Error loading SVG with Fabric");
          }
        }

        var element = document.createElement("canvas");
        element.width = options.width;
        element.height = options.height;

        var canvas = new fabric.Canvas(element, { enableRetinaScaling: false });
        var obj = fabric.util.groupSVGElements(objects, options);
        canvas.add(obj).renderAll();

        pngImage.src = canvas.toDataURL({ multiplier: scale });
        pngImage.width = options.width;
        pngImage.height = options.height;

        if (callback !== undefined) {
          callback(null, pngImage);
        }
      });
    } else {
      var svgImage = new Image();

      svgImage.onload = function() {
        var canvas = document.createElement("canvas");
        canvas.width = svgImage.width * scale;
        canvas.height = svgImage.height * scale;

        var context = canvas.getContext("2d");
        context.drawImage(svgImage, 0, 0, canvas.width, canvas.height);

        pngImage.src = canvas.toDataURL("image/png");
        pngImage.width = svgImage.width;
        pngImage.height = svgImage.height;

        if (callback !== undefined) {
          callback(null, pngImage);
        }
      }

      svgImage.onerror = function(e) {
        var error;

        if ('error' in e) {
          error = e.error;
        } else {
          error = new Error('Error loading SVG');
        }

        if (callback !== undefined) {
          callback(error);
        } else {
          throw error;
        }
      }

      svgImage.src = "data:image/svg+xml;base64," + b64EncodeUnicode(svgXml);
    }
  } catch (e) {
    if (callback !== undefined) {
      callback(e);
    } else {
      throw e;
    }
  }

  if (callback === undefined) {
    return pngImage;
  }
}

Viz.svgXmlToPngBase64 = function(svgXml, scale, callback) {
  Viz.svgXmlToPngImageElement(svgXml, scale, function(err, image) {
    if (err) {
      callback(err);
    } else {
      callback(null, image.src.slice("data:image/png;base64,".length));
    }
  });
}
<|endoftext|>


<commit_before>import * as types from "constants/action_types";
import * as modalTypes from "constants/modal_types";

const { remote } = require("electron");
const application = remote.app;
const win = remote.BrowserWindow.getFocusedWindow();

const reducers = {};
const defaultState = {
  isLoaded: false,
  modal: null,
  modalProps: {},
  platform: process.platform,
  upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
  daemonVersionMatched: null,
  daemonReady: false,
  hasSignature: false,
  badgeNumber: 0,
  volume: sessionStorage.getItem("volume") || 1,
};

reducers[types.DAEMON_READY] = function(state, action) {
  return Object.assign({}, state, {
    daemonReady: true,
  });
};

reducers[types.DAEMON_VERSION_MATCH] = function(state, action) {
  return Object.assign({}, state, {
    daemonVersionMatched: true,
  });
};

reducers[types.DAEMON_VERSION_MISMATCH] = function(state, action) {
  return Object.assign({}, state, {
    daemonVersionMatched: false,
    modal: modalTypes.INCOMPATIBLE_DAEMON,
  });
};

reducers[types.UPGRADE_CANCELLED] = function(state, action) {
  return Object.assign({}, state, {
    downloadProgress: null,
    upgradeDownloadComplete: false,
    modal: null,
  });
};

reducers[types.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
  return Object.assign({}, state, {
    downloadPath: action.data.path,
    upgradeDownloading: false,
    upgradeDownloadCompleted: true,
  });
};

reducers[types.UPGRADE_DOWNLOAD_STARTED] = function(state, action) {
  return Object.assign({}, state, {
    upgradeDownloading: true,
  });
};

reducers[types.SKIP_UPGRADE] = function(state, action) {
  sessionStorage.setItem("upgradeSkipped", true);

  return Object.assign({}, state, {
    upgradeSkipped: true,
    modal: null,
  });
};

reducers[types.UPDATE_VERSION] = function(state, action) {
  return Object.assign({}, state, {
    version: action.data.version,
  });
};

reducers[types.OPEN_MODAL] = function(state, action) {
  return Object.assign({}, state, {
    modal: action.data.modal,
    modalProps: action.data.modalProps || {},
  });
};

reducers[types.CLOSE_MODAL] = function(state, action) {
  return Object.assign({}, state, {
    modal: undefined,
    modalProps: {},
  });
};

reducers[types.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
  return Object.assign({}, state, {
    downloadProgress: action.data.percent,
  });
};

reducers[types.SHOW_SNACKBAR] = function(state, action) {
  const { message, linkText, linkTarget, isError } = action.data;
  const snackBar = Object.assign({}, state.snackBar);
  const snacks = Object.assign([], snackBar.snacks);
  snacks.push({
    message,
    linkText,
    linkTarget,
    isError,
  });
  const newSnackBar = Object.assign({}, snackBar, {
    snacks,
  });

  return Object.assign({}, state, {
    snackBar: newSnackBar,
  });
};

reducers[types.REMOVE_SNACKBAR_SNACK] = function(state, action) {
  const snackBar = Object.assign({}, state.snackBar);
  const snacks = Object.assign([], snackBar.snacks);
  snacks.shift();

  const newSnackBar = Object.assign({}, snackBar, {
    snacks,
  });

  return Object.assign({}, state, {
    snackBar: newSnackBar,
  });
};

reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
  const badgeNumber = state.badgeNumber;

  // Don't update the badge number if the window is focused
  if (win.isFocused()) return Object.assign({}, state);

  return Object.assign({}, state, {
    badgeNumber: badgeNumber + 1,
  });
};

reducers[types.WINDOW_FOCUSED] = function(state, action) {
  return Object.assign({}, state, {
    badgeNumber: 0,
  });
};

reducers[types.VOLUME_CHANGED] = function(state, action) {
  return Object.assign({}, state, {
    volume: action.data.volume,
  });
};

export default function reducer(state = defaultState, action) {
  const handler = reducers[action.type];
  if (handler) return handler(state, action);
  return state;
}
<commit_msg>win can sometimes be null (not sure how?)<commit_after>import * as types from "constants/action_types";
import * as modalTypes from "constants/modal_types";

const { remote } = require("electron");
const application = remote.app;
const win = remote.BrowserWindow.getFocusedWindow();

const reducers = {};
const defaultState = {
  isLoaded: false,
  modal: null,
  modalProps: {},
  platform: process.platform,
  upgradeSkipped: sessionStorage.getItem("upgradeSkipped"),
  daemonVersionMatched: null,
  daemonReady: false,
  hasSignature: false,
  badgeNumber: 0,
  volume: sessionStorage.getItem("volume") || 1,
};

reducers[types.DAEMON_READY] = function(state, action) {
  return Object.assign({}, state, {
    daemonReady: true,
  });
};

reducers[types.DAEMON_VERSION_MATCH] = function(state, action) {
  return Object.assign({}, state, {
    daemonVersionMatched: true,
  });
};

reducers[types.DAEMON_VERSION_MISMATCH] = function(state, action) {
  return Object.assign({}, state, {
    daemonVersionMatched: false,
    modal: modalTypes.INCOMPATIBLE_DAEMON,
  });
};

reducers[types.UPGRADE_CANCELLED] = function(state, action) {
  return Object.assign({}, state, {
    downloadProgress: null,
    upgradeDownloadComplete: false,
    modal: null,
  });
};

reducers[types.UPGRADE_DOWNLOAD_COMPLETED] = function(state, action) {
  return Object.assign({}, state, {
    downloadPath: action.data.path,
    upgradeDownloading: false,
    upgradeDownloadCompleted: true,
  });
};

reducers[types.UPGRADE_DOWNLOAD_STARTED] = function(state, action) {
  return Object.assign({}, state, {
    upgradeDownloading: true,
  });
};

reducers[types.SKIP_UPGRADE] = function(state, action) {
  sessionStorage.setItem("upgradeSkipped", true);

  return Object.assign({}, state, {
    upgradeSkipped: true,
    modal: null,
  });
};

reducers[types.UPDATE_VERSION] = function(state, action) {
  return Object.assign({}, state, {
    version: action.data.version,
  });
};

reducers[types.OPEN_MODAL] = function(state, action) {
  return Object.assign({}, state, {
    modal: action.data.modal,
    modalProps: action.data.modalProps || {},
  });
};

reducers[types.CLOSE_MODAL] = function(state, action) {
  return Object.assign({}, state, {
    modal: undefined,
    modalProps: {},
  });
};

reducers[types.UPGRADE_DOWNLOAD_PROGRESSED] = function(state, action) {
  return Object.assign({}, state, {
    downloadProgress: action.data.percent,
  });
};

reducers[types.SHOW_SNACKBAR] = function(state, action) {
  const { message, linkText, linkTarget, isError } = action.data;
  const snackBar = Object.assign({}, state.snackBar);
  const snacks = Object.assign([], snackBar.snacks);
  snacks.push({
    message,
    linkText,
    linkTarget,
    isError,
  });
  const newSnackBar = Object.assign({}, snackBar, {
    snacks,
  });

  return Object.assign({}, state, {
    snackBar: newSnackBar,
  });
};

reducers[types.REMOVE_SNACKBAR_SNACK] = function(state, action) {
  const snackBar = Object.assign({}, state.snackBar);
  const snacks = Object.assign([], snackBar.snacks);
  snacks.shift();

  const newSnackBar = Object.assign({}, snackBar, {
    snacks,
  });

  return Object.assign({}, state, {
    snackBar: newSnackBar,
  });
};

reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
  const badgeNumber = state.badgeNumber;

  // Don't update the badge number if the window is focused
  if (win && win.isFocused()) return Object.assign({}, state);

  return Object.assign({}, state, {
    badgeNumber: badgeNumber + 1,
  });
};

reducers[types.WINDOW_FOCUSED] = function(state, action) {
  return Object.assign({}, state, {
    badgeNumber: 0,
  });
};

reducers[types.VOLUME_CHANGED] = function(state, action) {
  return Object.assign({}, state, {
    volume: action.data.volume,
  });
};

export default function reducer(state = defaultState, action) {
  const handler = reducers[action.type];
  if (handler) return handler(state, action);
  return state;
}
<|endoftext|>