<commit_before>
vizwhiz.viz.tree_map = function() {

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Public Variables with Default Settings
  //-------------------------------------------------------------------

  var margin = {top: 0, right: 0, bottom: 0, left: 0},
    width = window.innerWidth,
    height = window.innerHeight,
    value_var = "value",
    id_var = "id",
    text_var = "name",
    nesting = null,
    filter = [],
    solo = [],
    tooltip_info = [],
    dispatch = d3.dispatch('elementMouseover', 'elementMouseout');

  //===================================================================

  function chart(selection) {
    selection.each(function(data) {

      var cloned_data = JSON.parse(JSON.stringify(data));

      var nested_data = vizwhiz.utils.nest(cloned_data, nesting, false, [{"key":"color"}])
      nested_data.children = nested_data.children.filter(filter_data)

      // Select the svg element, if it exists.
      var svg = d3.select(this).selectAll("svg").data([nested_data]);
      var svg_enter = svg.enter().append("svg")
        .attr('width',width)
        .attr('height',height)

      // Ok, to get started, lets run our heirarchically nested
      // data object through the d3 treemap function to get a
      // flat array of data with X, Y, width and height vars
      var tmap_data = d3.layout.treemap()
        .round(false)
        .size([width, height])
        .children(function(d) { return d.children; })
        .sort(function(a, b) { return a.value - b.value; })
        .value(function(d) { return value_var ? d[value_var] : d.value; })
        .nodes(nested_data)
        .filter(function(d) {
          return !d.children;
        })

      // We'll figure out how many levels of nesting there are to determine
      // the options for which depths to show
      // var max_depth = d3.max(tmap_data, function(d){ return d.depth; });

      // filter the tree map nodes to only the depth requested
      // tmap_data = tmap_data.filter(function(d) { return d.depth === (depth || max_depth); });

      // If it's the first time the app is being built, add group for nodes
      svg_enter.append("clipPath")
        .attr("id","clipping")
        .append("rect")
          .attr("width",width)
          .attr("height",height)

      d3.select("#clipping rect").transition(vizwhiz.timing)
        .attr("width",width)
        .attr("height",height)

      svg_enter.append("g")
        .attr("class", "viz")
        // .attr("transform", function(d){ return "translate("+(stroke_width/2)+", "+height+")"; })
        .attr("clip-path","url(#clipping)")

      var cell = d3.select("g.viz").selectAll("g")
        .data(tmap_data, function(d){ return d[text_var]; })

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Update, for cells that are already in existance
      //-------------------------------------------------------------------

      // need to perform updates in "each" clause so that new data is
      // propogated down to rects and text elements
      cell.transition().duration(vizwhiz.timing)
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .attr("opacity", 1)
        .each(function(g_data) {

          // update rectangles
          d3.select(this).selectAll("rect").transition().duration(vizwhiz.timing)
            .attr('width', function() {
              return g_data.dx+'px'
            })
            .attr('height', function() {
              return g_data.dy+'px'
            })

          // text (name)
          d3.select(this).selectAll("text.name")
            .attr("opacity", function(){
              return 0;
            })
            .transition().duration(vizwhiz.timing)
            .each("end", function(q, i){
              // need to recalculate word wrapping because dimensions have changed
              if(g_data[text_var]){
                var text = g_data[id_var] ? [g_data[text_var],g_data[id_var]] : g_data[text_var]
                vizwhiz.utils.wordwrap({
                  "text": text,
                  "parent": this,
                  "width": g_data.dx,
                  "height": g_data.dy,
                  "resize": true
                })
              }
              d3.select(this).transition().duration(vizwhiz.timing/2).attr("opacity", 1)
            })

          // text (share)
          d3.select(this).selectAll("text.share").transition().duration(vizwhiz.timing)
            .text(function(){
              var root = g_data;
              while(root.parent){ root = root.parent; } // find top most parent ndoe
              return vizwhiz.utils.format_num(g_data.value/root.value, true, 2);
            })
            .attr('font-size',function(){
              var size = (g_data.dx)/7
              if(g_data.dx < g_data.dy) var size = g_data.dx/7
              else var size = g_data.dy/7
              return size
            })
            .attr('x', function(){
              return g_data.dx/2
            })
            .attr('y',function(){
              return g_data.dy-(parseInt(d3.select(this).attr('font-size'),10)*0.10)
            })

        })

      //===================================================================

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // New cells enter, initialize them here
      //-------------------------------------------------------------------

      // cell aka container
      var cell_enter = cell.enter().append("g")
        .attr("opacity", 1)
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        })

      // rectangle
      cell_enter.append("rect")
        .attr("stroke","#ffffff")
        .attr('width', function(d) {
          return d.dx+'px'
        })
        .attr('height', function(d) {
          return d.dy+'px'
        })
        .attr("fill", function(d){
          // in case this depth doesn't have a color but a child of
          // this element DOES... use that color
          while(!d.color && d.children){
            d = d.children[0]
          }
          // if a color cannot be found (at this depth of deeper) use random
          return d.color ? d.color : vizwhiz.utils.rand_color();
        })
        .on(vizwhiz.evt.move, mouseover)
        .on(vizwhiz.evt.out, function(d){
          vizwhiz.tooltip.remove();
        })

      // text (name)
      cell_enter.append("text")
        .attr("opacity", 1)
        .attr("text-anchor","start")
        .style("font-weight","bold")
        .attr("font-family","Helvetica")
        .attr('class','name')
        .attr('x','0.2em')
        .attr('y','0em')
        .attr('dy','1em')
        .attr("fill", function(d){
          if(d.text_color) return d.text_color
          return d3.hsl(d.color).l >= 0.5 ? "#333" : "#fff";
        })
        .on(vizwhiz.evt.move, mouseover)
        .on(vizwhiz.evt.out, function(d){
          vizwhiz.tooltip.remove();
        })
        .each(function(d){
          if(d[text_var]){
            var text = d[id_var] ? [d[text_var],d[id_var]] : d[text_var]
            vizwhiz.utils.wordwrap({
              "text": text,
              "parent": this,
              "width": d.dx,
              "height": d.dy,
              "resize": true
            })
          }
        })

      // text (share)
      cell_enter.append("text")
        .attr('class','share')
        .attr("text-anchor","middle")
        .style("font-weight","bold")
        .attr("font-family","Helvetica")
        .attr("fill", function(d){
          if(d.text_color) return d.text_color
          return d3.hsl(d.color).l >= 0.5 ? "#333" : "#fff";
        })
        .text(function(d) {
          var root = d;
          while(root.parent){ root = root.parent; } // find top most parent ndoe
          return vizwhiz.utils.format_num(d.value/root.value, true, 2);
        })
        .attr('font-size',function(d){
          var size = (d.dx)/7
          if(d.dx < d.dy) var size = d.dx/7
          else var size = d.dy/7
          return size
        })
        .attr('x', function(d){
          return d.dx/2
        })
        .attr('y',function(d){
          return d.dy-(parseInt(d3.select(this).attr('font-size'),10)*0.10)
        })

      //===================================================================

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Exis, get rid of old cells
      //-------------------------------------------------------------------

      cell.exit().transition().duration(vizwhiz.timing)
        .attr("opacity", 0)
        .remove()

      //===================================================================

    });

    return chart;
  }

  function mouseover(d){
    // tooltip
    var svg = d3.select("svg");
    var tooltip_data = {}
    tooltip_info.forEach(function(t){
      if (d[t]) tooltip_data[t] = d[t]
    })
    vizwhiz.tooltip.create({
      "parent": svg,
      "id": d.id,
      "data": tooltip_data,
      "title": d[text_var],
      "x": d3.mouse(svg.node())[0],
      "y": d3.mouse(svg.node())[1],
      "offset": 10,
      "arrow": true
    })
  }

  function filter_data(d){

    // if this items name is in the filter list, remove it
    if(filter.indexOf(d.name) > -1){
      return false
    }

    if(!solo.length){
      return true
    }

    if(solo.indexOf(d.name) > -1){
      return true;
    }

    return false;
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Expose Public Variables
  //-------------------------------------------------------------------

  chart.dispatch = dispatch;

  chart.width = function(x) {
    if (!arguments.length) return width;
    width = x;
    return chart;
  };

  chart.height = function(x) {
    if (!arguments.length) return height;
    height = x;
    return chart;
  };

  chart.value_var = function(x) {
    if (!arguments.length) return value_var;
    value_var = x;
    return chart;
  };

  chart.id_var = function(x) {
    if (!arguments.length) return id_var;
    id_var = x;
    return chart;
  };

  chart.text_var = function(x) {
    if (!arguments.length) return text_var;
    text_var = x;
    return chart;
  };

  chart.nesting = function(x) {
    if (!arguments.length) return nesting;
    nesting = x;
    return chart;
  };

  chart.tooltip_info = function(x) {
    if (!arguments.length) return tooltip_info;
    tooltip_info = x;
    return chart;
  };

  chart.filter = function(x) {
    if (!arguments.length) return filter;
    // if we're given an array then overwrite the current filter var
    if(x instanceof Array){
      filter = x;
    }
    // otherwise add/remove it from array
    else {
      // if element is in the array remove it
      if(filter.indexOf(x) > -1){
        filter.splice(filter.indexOf(x), 1)
      }
      // if element is in the solo array remove it and add to this one
      else if(solo.indexOf(x) > -1){
        solo.splice(solo.indexOf(x), 1)
        filter.push(x)
      }
      // element not in current filter so add it
      else {
        filter.push(x)
      }
    }
    return chart;
  };

  chart.solo = function(x) {
    if (!arguments.length) return solo;
    // if we're given an array then overwrite the current filter var
    if(x instanceof Array){
      solo = x;
    }
    // otherwise add/remove it from array
    else {
      // if element is in the array remove it
      if(solo.indexOf(x) > -1){
        solo.splice(solo.indexOf(x), 1)
      }
      // if element is in the filter array remove it and add to this one
      else if(filter.indexOf(x) > -1){
        filter.splice(filter.indexOf(x), 1)
        solo.push(x)
      }
      // element not in current filter so add it
      else {
        solo.push(x)
      }
    }
    return chart;
  };

  chart.margin = function(x) {
    if (!arguments.length) return margin;
    margin.top    = typeof x.top    != 'undefined' ? x.top    : margin.top;
    margin.right  = typeof x.right  != 'undefined' ? x.right  : margin.right;
    margin.bottom = typeof x.bottom != 'undefined' ? x.bottom : margin.bottom;
    margin.left   = typeof x.left   != 'undefined' ? x.left   : margin.left;
    return chart;
  };

  //===================================================================

  return chart;
};<commit_msg>hovering over share percent no longer removes tooltip<commit_after>
vizwhiz.viz.tree_map = function() {

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Public Variables with Default Settings
  //-------------------------------------------------------------------

  var margin = {top: 0, right: 0, bottom: 0, left: 0},
    width = window.innerWidth,
    height = window.innerHeight,
    value_var = "value",
    id_var = "id",
    text_var = "name",
    nesting = null,
    filter = [],
    solo = [],
    tooltip_info = [],
    dispatch = d3.dispatch('elementMouseover', 'elementMouseout');

  //===================================================================

  function chart(selection) {
    selection.each(function(data) {

      var cloned_data = JSON.parse(JSON.stringify(data));

      var nested_data = vizwhiz.utils.nest(cloned_data, nesting, false, [{"key":"color"}])
      nested_data.children = nested_data.children.filter(filter_data)

      // Select the svg element, if it exists.
      var svg = d3.select(this).selectAll("svg").data([nested_data]);
      var svg_enter = svg.enter().append("svg")
        .attr('width',width)
        .attr('height',height)

      // Ok, to get started, lets run our heirarchically nested
      // data object through the d3 treemap function to get a
      // flat array of data with X, Y, width and height vars
      var tmap_data = d3.layout.treemap()
        .round(false)
        .size([width, height])
        .children(function(d) { return d.children; })
        .sort(function(a, b) { return a.value - b.value; })
        .value(function(d) { return value_var ? d[value_var] : d.value; })
        .nodes(nested_data)
        .filter(function(d) {
          return !d.children;
        })

      // We'll figure out how many levels of nesting there are to determine
      // the options for which depths to show
      // var max_depth = d3.max(tmap_data, function(d){ return d.depth; });

      // filter the tree map nodes to only the depth requested
      // tmap_data = tmap_data.filter(function(d) { return d.depth === (depth || max_depth); });

      // If it's the first time the app is being built, add group for nodes
      svg_enter.append("clipPath")
        .attr("id","clipping")
        .append("rect")
          .attr("width",width)
          .attr("height",height)

      d3.select("#clipping rect").transition(vizwhiz.timing)
        .attr("width",width)
        .attr("height",height)

      svg_enter.append("g")
        .attr("class", "viz")
        // .attr("transform", function(d){ return "translate("+(stroke_width/2)+", "+height+")"; })
        .attr("clip-path","url(#clipping)")

      var cell = d3.select("g.viz").selectAll("g")
        .data(tmap_data, function(d){ return d[text_var]; })

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Update, for cells that are already in existance
      //-------------------------------------------------------------------

      // need to perform updates in "each" clause so that new data is
      // propogated down to rects and text elements
      cell.transition().duration(vizwhiz.timing)
        .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
        .attr("opacity", 1)
        .each(function(g_data) {

          // update rectangles
          d3.select(this).selectAll("rect").transition().duration(vizwhiz.timing)
            .attr('width', function() {
              return g_data.dx+'px'
            })
            .attr('height', function() {
              return g_data.dy+'px'
            })

          // text (name)
          d3.select(this).selectAll("text.name")
            .attr("opacity", function(){
              return 0;
            })
            .transition().duration(vizwhiz.timing)
            .each("end", function(q, i){
              // need to recalculate word wrapping because dimensions have changed
              if(g_data[text_var]){
                var text = g_data[id_var] ? [g_data[text_var],g_data[id_var]] : g_data[text_var]
                vizwhiz.utils.wordwrap({
                  "text": text,
                  "parent": this,
                  "width": g_data.dx,
                  "height": g_data.dy,
                  "resize": true
                })
              }
              d3.select(this).transition().duration(vizwhiz.timing/2).attr("opacity", 1)
            })

          // text (share)
          d3.select(this).selectAll("text.share").transition().duration(vizwhiz.timing)
            .text(function(){
              var root = g_data;
              while(root.parent){ root = root.parent; } // find top most parent ndoe
              return vizwhiz.utils.format_num(g_data.value/root.value, true, 2);
            })
            .attr('font-size',function(){
              var size = (g_data.dx)/7
              if(g_data.dx < g_data.dy) var size = g_data.dx/7
              else var size = g_data.dy/7
              return size
            })
            .attr('x', function(){
              return g_data.dx/2
            })
            .attr('y',function(){
              return g_data.dy-(parseInt(d3.select(this).attr('font-size'),10)*0.10)
            })

        })

      //===================================================================

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // New cells enter, initialize them here
      //-------------------------------------------------------------------

      // cell aka container
      var cell_enter = cell.enter().append("g")
        .attr("opacity", 1)
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        })

      // rectangle
      cell_enter.append("rect")
        .attr("stroke","#ffffff")
        .attr('width', function(d) {
          return d.dx+'px'
        })
        .attr('height', function(d) {
          return d.dy+'px'
        })
        .attr("fill", function(d){
          // in case this depth doesn't have a color but a child of
          // this element DOES... use that color
          while(!d.color && d.children){
            d = d.children[0]
          }
          // if a color cannot be found (at this depth of deeper) use random
          return d.color ? d.color : vizwhiz.utils.rand_color();
        })
        .on(vizwhiz.evt.move, mouseover)
        .on(vizwhiz.evt.out, function(d){
          vizwhiz.tooltip.remove();
        })

      // text (name)
      cell_enter.append("text")
        .attr("opacity", 1)
        .attr("text-anchor","start")
        .style("font-weight","bold")
        .attr("font-family","Helvetica")
        .attr('class','name')
        .attr('x','0.2em')
        .attr('y','0em')
        .attr('dy','1em')
        .attr("fill", function(d){
          if(d.text_color) return d.text_color
          return d3.hsl(d.color).l >= 0.5 ? "#333" : "#fff";
        })
        .on(vizwhiz.evt.move, mouseover)
        .on(vizwhiz.evt.out, function(d){
          vizwhiz.tooltip.remove();
        })
        .each(function(d){
          if(d[text_var]){
            var text = d[id_var] ? [d[text_var],d[id_var]] : d[text_var]
            vizwhiz.utils.wordwrap({
              "text": text,
              "parent": this,
              "width": d.dx,
              "height": d.dy,
              "resize": true
            })
          }
        })

      // text (share)
      cell_enter.append("text")
        .attr('class','share')
        .attr("text-anchor","middle")
        .style("font-weight","bold")
        .attr("font-family","Helvetica")
        .attr("fill", function(d){
          if(d.text_color) return d.text_color
          return d3.hsl(d.color).l >= 0.5 ? "#333" : "#fff";
        })
        .text(function(d) {
          var root = d;
          while(root.parent){ root = root.parent; } // find top most parent ndoe
          return vizwhiz.utils.format_num(d.value/root.value, true, 2);
        })
        .attr('font-size',function(d){
          var size = (d.dx)/7
          if(d.dx < d.dy) var size = d.dx/7
          else var size = d.dy/7
          return size
        })
        .attr('x', function(d){
          return d.dx/2
        })
        .attr('y',function(d){
          return d.dy-(parseInt(d3.select(this).attr('font-size'),10)*0.10)
        })
        .on(vizwhiz.evt.move, mouseover)
        .on(vizwhiz.evt.out, function(d){
          vizwhiz.tooltip.remove();
        })

      //===================================================================

      //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      // Exis, get rid of old cells
      //-------------------------------------------------------------------

      cell.exit().transition().duration(vizwhiz.timing)
        .attr("opacity", 0)
        .remove()

      //===================================================================

    });

    return chart;
  }

  function mouseover(d){
    // tooltip
    var svg = d3.select("svg");
    var tooltip_data = {}
    tooltip_info.forEach(function(t){
      if (d[t]) tooltip_data[t] = d[t]
    })
    vizwhiz.tooltip.create({
      "parent": svg,
      "id": d.id,
      "data": tooltip_data,
      "title": d[text_var],
      "x": d3.mouse(svg.node())[0],
      "y": d3.mouse(svg.node())[1],
      "offset": 10,
      "arrow": true
    })
  }

  function filter_data(d){

    // if this items name is in the filter list, remove it
    if(filter.indexOf(d.name) > -1){
      return false
    }

    if(!solo.length){
      return true
    }

    if(solo.indexOf(d.name) > -1){
      return true;
    }

    return false;
  }

  //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  // Expose Public Variables
  //-------------------------------------------------------------------

  chart.dispatch = dispatch;

  chart.width = function(x) {
    if (!arguments.length) return width;
    width = x;
    return chart;
  };

  chart.height = function(x) {
    if (!arguments.length) return height;
    height = x;
    return chart;
  };

  chart.value_var = function(x) {
    if (!arguments.length) return value_var;
    value_var = x;
    return chart;
  };

  chart.id_var = function(x) {
    if (!arguments.length) return id_var;
    id_var = x;
    return chart;
  };

  chart.text_var = function(x) {
    if (!arguments.length) return text_var;
    text_var = x;
    return chart;
  };

  chart.nesting = function(x) {
    if (!arguments.length) return nesting;
    nesting = x;
    return chart;
  };

  chart.tooltip_info = function(x) {
    if (!arguments.length) return tooltip_info;
    tooltip_info = x;
    return chart;
  };

  chart.filter = function(x) {
    if (!arguments.length) return filter;
    // if we're given an array then overwrite the current filter var
    if(x instanceof Array){
      filter = x;
    }
    // otherwise add/remove it from array
    else {
      // if element is in the array remove it
      if(filter.indexOf(x) > -1){
        filter.splice(filter.indexOf(x), 1)
      }
      // if element is in the solo array remove it and add to this one
      else if(solo.indexOf(x) > -1){
        solo.splice(solo.indexOf(x), 1)
        filter.push(x)
      }
      // element not in current filter so add it
      else {
        filter.push(x)
      }
    }
    return chart;
  };

  chart.solo = function(x) {
    if (!arguments.length) return solo;
    // if we're given an array then overwrite the current filter var
    if(x instanceof Array){
      solo = x;
    }
    // otherwise add/remove it from array
    else {
      // if element is in the array remove it
      if(solo.indexOf(x) > -1){
        solo.splice(solo.indexOf(x), 1)
      }
      // if element is in the filter array remove it and add to this one
      else if(filter.indexOf(x) > -1){
        filter.splice(filter.indexOf(x), 1)
        solo.push(x)
      }
      // element not in current filter so add it
      else {
        solo.push(x)
      }
    }
    return chart;
  };

  chart.margin = function(x) {
    if (!arguments.length) return margin;
    margin.top    = typeof x.top    != 'undefined' ? x.top    : margin.top;
    margin.right  = typeof x.right  != 'undefined' ? x.right  : margin.right;
    margin.bottom = typeof x.bottom != 'undefined' ? x.bottom : margin.bottom;
    margin.left   = typeof x.left   != 'undefined' ? x.left   : margin.left;
    return chart;
  };

  //===================================================================

  return chart;
};<|endoftext|>


<commit_before>// Vuex store getters

export function metadata (state) {
  return state.metadata
}

export function selectedSeason (state) {
  return state.selected.season
}

export function selectedRegion (state) {
  return state.selected.region
}

export function selectedWeekIdx (state) {
  return state.selected.week.idx
}

export function selectedWeekName (state) {
  if (state.selected.week.name)
    return state.selected.week.name
  else
    return 'NA'
}

export function selectedChoropleth (state) {
  return state.selected.choropleth
}

export function selectedModel (state) {
  return state.selected.model
}

export function seasons (state) {
  return state.data[selectedRegion(state)].seasons.map(s => s.id)
}

export function regions (state) {
  return state.data.map(d => d.region)
}

/**
 * Return list of all models for current season
 */
export function models (state) {
  return state.data[selectedRegion(state)]
    .seasons[selectedSeason(state)]
    .models.map(m => m.id)
}

export function choroplethRelative (state) {
  return state.toggles.choroplethRelative
}

/**
 * Scale given (week, data) pairs using the baseline
 */
function baselineScale (values, baseline) {
  return values.map(d => {
    return {
      week: d.week,
      data: baseline ? ((d.data / baseline) - 1) * 100 : -1
    }
  })
}

/**
 * Return choropleth data for model
 */
function modelChoroplethData (state, modelId, predictionType, relative = false) {
  let seasonId = selectedSeason(state)

  let output = {
    data: [],
    type: relative ? 'diverging' : 'sequential'
  }

  state.data.map(r => {
    let preds = {}

    r.seasons[seasonId].models[modelId].predictions
      .forEach(p => {
        preds[p.week] = p[predictionType].point
      })

    let weeks = getMaxLagData(r.seasons[seasonId].actual).map(d => d.week)

    let values = weeks.map(w => {
      return {
        week: w,
        data: preds[w] ? preds[w] : -1
      }
    })

    if (relative)
      values = baselineScale(values, r.seasons[seasonId].baseline)

    output.data.push({
      region: r.subId,
      states: r.states,
      value: values
    })
  })

  output.data = output.data.slice(1) // Remove national data

  return output
}

/**
 * Return data for choropleth using actual values
 */
function actualChoroplethData (state, relative = false) {
  let seasonId = selectedSeason(state)

  let output = {
    data: [],
    type: relative ? 'diverging' : 'sequential',
    decorator: relative ? x => x + ' % (baseline)' : x => x + ' % (wili)'
  }

  state.data.map(r => {
    let values = getMaxLagData(r.seasons[seasonId].actual)

    if (relative)
      values = baselineScale(values, r.seasons[seasonId].baseline)

    output.data.push({
      region: r.subId,
      states: r.states,
      value: values
    })
  })

  output.data = output.data.slice(1) // Remove national data

  return output
}

export function choropleths (state) {
  // TODO: Parse from data

  let actualChoropleths = [
    'Actual Weighted ILI (%)',
    'Relative Weighted ILI (%)'
  ]

  return actualChoropleths
}

export function timeChart (state) {
  return state.timeChart
}

export function choropleth (state) {
  return state.choropleth
}

/**
 * Get data with maximum lag
 * First element of the lag array
 */
function getMaxLagData (actual) {
  return actual.map(d => {
    let dataToReturn = -1
    // Handle zero length values
    if (d.data.length !== 0) {
      dataToReturn = d.data[0].value
    }
    return {
      week: d.week,
      data: dataToReturn
    }
  })
}

/**
 * Trim history data to fit in length 'numWeeks'
 */
function trimHistory (historyActual, numWeeks) {
  let historyTrimmed = historyActual.slice()

  if (numWeeks == 52) {
    // Clip everyone else to remove 53rd week
    historyTrimmed = historyTrimmed.filter(d => d.week % 100 != 53)
  } else if (historyTrimmed.length == 52) {
    // Expand to add 53rd week
    // Adding a dummy year 1000, this will also help identify the adjustment
    historyTrimmed.splice(23, 0, {
      week: 100053,
      data: (historyTrimmed[22].data + historyTrimmed[23].data) / 2.0
    })
  }

  return historyTrimmed
}

/**
 * Return data subset for chart as specified in region/season selected
 */
export function timeChartData (state) {

  let regionSubset = state.data[selectedRegion(state)]
  let currentSeasonId = selectedSeason(state)
  let seasonSubset = regionSubset.seasons[currentSeasonId]

  let selectedWeeksCount = seasonSubset.actual.length

  let historicalData = []

  // Add data from history store
  for (let season of Object.keys(regionSubset.history)) {
    historicalData.push({
      id: season,
      actual: trimHistory(regionSubset.history[season], selectedWeeksCount)
    })
  }

  for (let i = 0; i < currentSeasonId; i++) {
    historicalData.push({
      id: regionSubset.seasons[i].id,
      actual: trimHistory(getMaxLagData(regionSubset.seasons[i].actual),
                          selectedWeeksCount)
    })
  }

  return {
    region: regionSubset.subId, // Submission ids are concise
    observed: seasonSubset.actual,
    actual: getMaxLagData(seasonSubset.actual), // Using this to avoid confusion
    baseline: seasonSubset.baseline,
    models: seasonSubset.models, // All model predictions
    history: historicalData
  }
}

/**
 * Return formatted next week data
 */
export function nextWeek (state) {
  return timeChart(state).getNextWeekData()
}

/**
 * Return formatted previous week data
 */
export function previousWeek (state) {
  return timeChart(state).getPreviousWeekData()
}

/**
 * Return range for choropleth color scale
 */
function choroplethDataRange (state, choroplethId) {
  let maxVals = [],
      minVals = []

  state.data.map(region => {
    region.seasons.map(season => {

      let actual = getMaxLagData(season.actual).map(d => d.data).filter(d => d !== -1)

      if (choroplethId === 1) {
        // Use baseline scaled data
        maxVals.push(Math.max(...actual.map(d => ((d / season.baseline) - 1) * 100)))
        minVals.push(Math.min(...actual.map(d => ((d / season.baseline) - 1) * 100)))
      } else {
        maxVals.push(Math.max(...actual))
        minVals.push(Math.min(...actual))
      }
    })
  })

  return [Math.min(...minVals),
          Math.max(...maxVals)]
}

/**
 * Return actual data for all regions for current selections
 */
export function choroplethData (state) {

  let choroplethId = selectedChoropleth(state),
      seasonId = selectedSeason(state)

  let output = actualChoroplethData(state, choroplethId === 1)

  output.range = choroplethDataRange(state, choroplethId)
  return output
}

export function legendShow (state) {
  return state.toggles.legend
}

// Introduction getters
export function currentIntro (state) {
  return state.intro.data[introStep(state)]
}

export function introStep (state) {
  return state.intro.pointer
}

export function introLength (state) {
  return state.intro.data.length
}

export function introShow (state) {
  return state.toggles.intro
}

export function introAtFirst (state) {
  return introStep(state) === 0
}

export function introAtLast (state) {
  return introStep(state) === introLength(state) - 1
}
<commit_msg>Return shorter region identifier<commit_after>// Vuex store getters

export function metadata (state) {
  return state.metadata
}

export function selectedSeason (state) {
  return state.selected.season
}

export function selectedRegion (state) {
  return state.selected.region
}

export function selectedWeekIdx (state) {
  return state.selected.week.idx
}

export function selectedWeekName (state) {
  if (state.selected.week.name)
    return state.selected.week.name
  else
    return 'NA'
}

export function selectedChoropleth (state) {
  return state.selected.choropleth
}

export function selectedModel (state) {
  return state.selected.model
}

export function seasons (state) {
  return state.data[selectedRegion(state)].seasons.map(s => s.id)
}

export function regions (state) {
  return state.data.map(d => d.subId)
}

/**
 * Return list of all models for current season
 */
export function models (state) {
  return state.data[selectedRegion(state)]
    .seasons[selectedSeason(state)]
    .models.map(m => m.id)
}

export function choroplethRelative (state) {
  return state.toggles.choroplethRelative
}

/**
 * Scale given (week, data) pairs using the baseline
 */
function baselineScale (values, baseline) {
  return values.map(d => {
    return {
      week: d.week,
      data: baseline ? ((d.data / baseline) - 1) * 100 : -1
    }
  })
}

/**
 * Return choropleth data for model
 */
function modelChoroplethData (state, modelId, predictionType, relative = false) {
  let seasonId = selectedSeason(state)

  let output = {
    data: [],
    type: relative ? 'diverging' : 'sequential'
  }

  state.data.map(r => {
    let preds = {}

    r.seasons[seasonId].models[modelId].predictions
      .forEach(p => {
        preds[p.week] = p[predictionType].point
      })

    let weeks = getMaxLagData(r.seasons[seasonId].actual).map(d => d.week)

    let values = weeks.map(w => {
      return {
        week: w,
        data: preds[w] ? preds[w] : -1
      }
    })

    if (relative)
      values = baselineScale(values, r.seasons[seasonId].baseline)

    output.data.push({
      region: r.subId,
      states: r.states,
      value: values
    })
  })

  output.data = output.data.slice(1) // Remove national data

  return output
}

/**
 * Return data for choropleth using actual values
 */
function actualChoroplethData (state, relative = false) {
  let seasonId = selectedSeason(state)

  let output = {
    data: [],
    type: relative ? 'diverging' : 'sequential',
    decorator: relative ? x => x + ' % (baseline)' : x => x + ' % (wili)'
  }

  state.data.map(r => {
    let values = getMaxLagData(r.seasons[seasonId].actual)

    if (relative)
      values = baselineScale(values, r.seasons[seasonId].baseline)

    output.data.push({
      region: r.subId,
      states: r.states,
      value: values
    })
  })

  output.data = output.data.slice(1) // Remove national data

  return output
}

export function choropleths (state) {
  // TODO: Parse from data

  let actualChoropleths = [
    'Actual Weighted ILI (%)',
    'Relative Weighted ILI (%)'
  ]

  return actualChoropleths
}

export function timeChart (state) {
  return state.timeChart
}

export function choropleth (state) {
  return state.choropleth
}

/**
 * Get data with maximum lag
 * First element of the lag array
 */
function getMaxLagData (actual) {
  return actual.map(d => {
    let dataToReturn = -1
    // Handle zero length values
    if (d.data.length !== 0) {
      dataToReturn = d.data[0].value
    }
    return {
      week: d.week,
      data: dataToReturn
    }
  })
}

/**
 * Trim history data to fit in length 'numWeeks'
 */
function trimHistory (historyActual, numWeeks) {
  let historyTrimmed = historyActual.slice()

  if (numWeeks == 52) {
    // Clip everyone else to remove 53rd week
    historyTrimmed = historyTrimmed.filter(d => d.week % 100 != 53)
  } else if (historyTrimmed.length == 52) {
    // Expand to add 53rd week
    // Adding a dummy year 1000, this will also help identify the adjustment
    historyTrimmed.splice(23, 0, {
      week: 100053,
      data: (historyTrimmed[22].data + historyTrimmed[23].data) / 2.0
    })
  }

  return historyTrimmed
}

/**
 * Return data subset for chart as specified in region/season selected
 */
export function timeChartData (state) {

  let regionSubset = state.data[selectedRegion(state)]
  let currentSeasonId = selectedSeason(state)
  let seasonSubset = regionSubset.seasons[currentSeasonId]

  let selectedWeeksCount = seasonSubset.actual.length

  let historicalData = []

  // Add data from history store
  for (let season of Object.keys(regionSubset.history)) {
    historicalData.push({
      id: season,
      actual: trimHistory(regionSubset.history[season], selectedWeeksCount)
    })
  }

  for (let i = 0; i < currentSeasonId; i++) {
    historicalData.push({
      id: regionSubset.seasons[i].id,
      actual: trimHistory(getMaxLagData(regionSubset.seasons[i].actual),
                          selectedWeeksCount)
    })
  }

  return {
    region: regionSubset.subId, // Submission ids are concise
    observed: seasonSubset.actual,
    actual: getMaxLagData(seasonSubset.actual), // Using this to avoid confusion
    baseline: seasonSubset.baseline,
    models: seasonSubset.models, // All model predictions
    history: historicalData
  }
}

/**
 * Return formatted next week data
 */
export function nextWeek (state) {
  return timeChart(state).getNextWeekData()
}

/**
 * Return formatted previous week data
 */
export function previousWeek (state) {
  return timeChart(state).getPreviousWeekData()
}

/**
 * Return range for choropleth color scale
 */
function choroplethDataRange (state, choroplethId) {
  let maxVals = [],
      minVals = []

  state.data.map(region => {
    region.seasons.map(season => {

      let actual = getMaxLagData(season.actual).map(d => d.data).filter(d => d !== -1)

      if (choroplethId === 1) {
        // Use baseline scaled data
        maxVals.push(Math.max(...actual.map(d => ((d / season.baseline) - 1) * 100)))
        minVals.push(Math.min(...actual.map(d => ((d / season.baseline) - 1) * 100)))
      } else {
        maxVals.push(Math.max(...actual))
        minVals.push(Math.min(...actual))
      }
    })
  })

  return [Math.min(...minVals),
          Math.max(...maxVals)]
}

/**
 * Return actual data for all regions for current selections
 */
export function choroplethData (state) {

  let choroplethId = selectedChoropleth(state),
      seasonId = selectedSeason(state)

  let output = actualChoroplethData(state, choroplethId === 1)

  output.range = choroplethDataRange(state, choroplethId)
  return output
}

export function legendShow (state) {
  return state.toggles.legend
}

// Introduction getters
export function currentIntro (state) {
  return state.intro.data[introStep(state)]
}

export function introStep (state) {
  return state.intro.pointer
}

export function introLength (state) {
  return state.intro.data.length
}

export function introShow (state) {
  return state.toggles.intro
}

export function introAtFirst (state) {
  return introStep(state) === 0
}

export function introAtLast (state) {
  return introStep(state) === introLength(state) - 1
}
<|endoftext|>


<commit_before>angular.module("application").controller("MainMenuController", [
   "$scope", "Person", "PersonalAddress", "HelpText", "$rootScope", function ($scope, Person, PersonalAddress, HelpText, $rootScope) {

       HelpText.getAll().$promise.then(function (res) {
           $scope.helpLink = res.InformationHelpLink;
           $rootScope.HelpTexts = res;
       });

       if ($rootScope.CurrentUser == undefined) {
           $rootScope.CurrentUser = Person.GetCurrentUser().$promise.then(function (res) {
               $rootScope.CurrentUser = res;
               $scope.showAdministration = res.IsAdmin;
               $scope.showApproveReports = res.IsLeader || res.IsSubstitute;
               $scope.UserName = res.FullName;
           });
       }

       var reloadPageIfNewCacheIsAvailable = function() {
           // Check if a new cache is available on page load.
           window.addEventListener('load', function (e) {

               window.applicationCache.addEventListener('updateready', function (e) {
                   if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
                       // Browser downloaded a new app cache. Refresh the page to load the new cache.
                           window.location.reload();
                   } else {
                       // Manifest didn't change. Nothing new to server.
                   }
               }, false);

           }, false);
       }

        reloadPageIfNewCacheIsAvailable();

    }
]);<commit_msg>Made MainMenuController load all users and all orgunits, to speed up access to admin and approvereports page.<commit_after>angular.module("application").controller("MainMenuController", [
   "$scope", "Person", "PersonalAddress", "HelpText", "$rootScope", "OrgUnit", "Person", function ($scope, Person, PersonalAddress, HelpText, $rootScope, OrgUnit, Person) {

       HelpText.getAll().$promise.then(function (res) {
           $scope.helpLink = res.InformationHelpLink;
           $rootScope.HelpTexts = res;
       });

       if ($rootScope.CurrentUser == undefined) {
           $rootScope.CurrentUser = Person.GetCurrentUser().$promise.then(function (res) {
               $rootScope.CurrentUser = res;
               $scope.showAdministration = res.IsAdmin;
               $scope.showApproveReports = res.IsLeader || res.IsSubstitute;
               $scope.UserName = res.FullName;
           });
       }

        if ($rootScope.OrgUnits == undefined) {
            $rootScope.OrgUnits = OrgUnit.get({ query: "$select=Id, LongDescription, HasAccessToFourKmRule" }).$promise.then(function(res) {
                $rootScope.OrgUnits = res.value;
            });
        }

        if ($rootScope.People == undefined) {
            $rootScope.People = Person.getAll({ query: "$select=Id,FullName,IsActive" }).$promise.then(function (res) {
                $rootScope.People = res.value;
            });
        }

    }
]);<|endoftext|>


<commit_before>/*!
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
*/

/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
/*global window: false, jQuery: false */

(function ($) {
    var data_click = "unobtrusiveAjaxClick",
        data_validation = "unobtrusiveValidation";

    function getFunction(code, argNames) {
        var fn = window, parts = (code || "").split(".");
        while (fn && parts.length) {
            fn = fn[parts.shift()];
        }
        if (typeof (fn) === "function") {
            return fn;
        }
        argNames.push(code);
        return Function.constructor.apply(null, argNames);
    }

    function isMethodProxySafe(method) {
        return method === "GET" || method === "POST";
    }

    function asyncOnBeforeSend(xhr, method) {
        if (!isMethodProxySafe(method)) {
            xhr.setRequestHeader("X-HTTP-Method-Override", method);
        }
    }

    function asyncOnSuccess(element, data, contentType) {
        var mode;

        if (contentType.indexOf("application/x-javascript") !== -1) {  // jQuery already executes JavaScript for us
            return;
        }

        mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
        $(element.getAttribute("data-ajax-update")).each(function (i, update) {
            var top;

            switch (mode) {
            case "BEFORE":
                top = update.firstChild;
                $("<div />").html(data).contents().each(function () {
                    update.insertBefore(this, top);
                });
                break;
            case "AFTER":
                $("<div />").html(data).contents().each(function () {
                    update.appendChild(this);
                });
                break;
            default:
                $(update).html(data);
                break;
            }
        });
    }

    function asyncRequest(element, options) {
        var confirm, loading, method, duration;

        confirm = element.getAttribute("data-ajax-confirm");
        if (confirm && !window.confirm(confirm)) {
            return;
        }

        loading = $(element.getAttribute("data-ajax-loading"));
        duration = element.getAttribute("data-ajax-loading-duration") || 0;

        $.extend(options, {
            type: element.getAttribute("data-ajax-method") || undefined,
            url: element.getAttribute("data-ajax-url") || undefined,
            beforeSend: function (xhr) {
                var result;
                asyncOnBeforeSend(xhr, method);
                result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
                if (result !== false) {
                    loading.show(duration);
                }
                return result;
            },
            complete: function () {
                loading.hide(duration);
                getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(this, arguments);
            },
            success: function (data, status, xhr) {
                asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
                getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(this, arguments);
            },
            error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
        });

        options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

        method = options.type.toUpperCase();
        if (!isMethodProxySafe(method)) {
            options.type = "POST";
            options.data.push({ name: "X-HTTP-Method-Override", value: method });
        }

        $.ajax(options);
    }

    function validate(form) {
        var validationInfo = $(form).data(data_validation);
        return !validationInfo || !validationInfo.validate || validationInfo.validate();
    }

    $(document).on("click", "a[data-ajax=true]", function (evt) {
        evt.preventDefault();
        asyncRequest(this, {
            url: this.href,
            type: "GET",
            data: []
        });
    });

    $(document).on("click", "form[data-ajax=true] input[type=image]", function (evt) {
        var name = evt.target.name,
            $target = $(evt.target),
            form = $target.parents("form")[0],
            offset = $target.offset();

        $(form).data(data_click, [
            { name: name + ".x", value: Math.round(evt.pageX - offset.left) },
            { name: name + ".y", value: Math.round(evt.pageY - offset.top) }
        ]);

        setTimeout(function () {
            $(form).removeData(data_click);
        }, 0);
    });

    $(document).on("click", "form[data-ajax=true] :submit", function (evt) {
        var name = evt.currentTarget.name,
            form = $(evt.target).parents("form")[0];

        $(form).data(data_click, name ? [{ name: name, value: evt.currentTarget.value }] : []);

        setTimeout(function () {
            $(form).removeData(data_click);
        }, 0);
    });

    $(document).on("submit", "form[data-ajax=true]", function (evt) {
        var clickInfo = $(this).data(data_click) || [];
        evt.preventDefault();
        if (!validate(this)) {
            return;
        }
        asyncRequest(this, {
            url: this.action,
            type: this.method || "GET",
            data: clickInfo.concat($(this).serializeArray())
        });
    });
}(jQuery));<commit_msg>Fix 238, Unobtrusive.Ajax interferes with the cancel convention on validation - https://aspnetwebstack.codeplex.com/workitem/238 - Handle "cancel" class on submit: Submit form data without validation - Also cleanup repeated JQuery object creation for the same DOM element<commit_after>/*!
** Unobtrusive Ajax support library for jQuery
** Copyright (C) Microsoft Corporation. All rights reserved.
*/

/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: false */
/*global window: false, jQuery: false */

(function ($) {
    var data_click = "unobtrusiveAjaxClick",
        data_target = "unobtrusiveAjaxClickTarget"
        data_validation = "unobtrusiveValidation";

    function getFunction(code, argNames) {
        var fn = window, parts = (code || "").split(".");
        while (fn && parts.length) {
            fn = fn[parts.shift()];
        }
        if (typeof (fn) === "function") {
            return fn;
        }
        argNames.push(code);
        return Function.constructor.apply(null, argNames);
    }

    function isMethodProxySafe(method) {
        return method === "GET" || method === "POST";
    }

    function asyncOnBeforeSend(xhr, method) {
        if (!isMethodProxySafe(method)) {
            xhr.setRequestHeader("X-HTTP-Method-Override", method);
        }
    }

    function asyncOnSuccess(element, data, contentType) {
        var mode;

        if (contentType.indexOf("application/x-javascript") !== -1) {  // jQuery already executes JavaScript for us
            return;
        }

        mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
        $(element.getAttribute("data-ajax-update")).each(function (i, update) {
            var top;

            switch (mode) {
            case "BEFORE":
                top = update.firstChild;
                $("<div />").html(data).contents().each(function () {
                    update.insertBefore(this, top);
                });
                break;
            case "AFTER":
                $("<div />").html(data).contents().each(function () {
                    update.appendChild(this);
                });
                break;
            default:
                $(update).html(data);
                break;
            }
        });
    }

    function asyncRequest(element, options) {
        var confirm, loading, method, duration;

        confirm = element.getAttribute("data-ajax-confirm");
        if (confirm && !window.confirm(confirm)) {
            return;
        }

        loading = $(element.getAttribute("data-ajax-loading"));
        duration = element.getAttribute("data-ajax-loading-duration") || 0;

        $.extend(options, {
            type: element.getAttribute("data-ajax-method") || undefined,
            url: element.getAttribute("data-ajax-url") || undefined,
            beforeSend: function (xhr) {
                var result;
                asyncOnBeforeSend(xhr, method);
                result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
                if (result !== false) {
                    loading.show(duration);
                }
                return result;
            },
            complete: function () {
                loading.hide(duration);
                getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(this, arguments);
            },
            success: function (data, status, xhr) {
                asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
                getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(this, arguments);
            },
            error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
        });

        options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });

        method = options.type.toUpperCase();
        if (!isMethodProxySafe(method)) {
            options.type = "POST";
            options.data.push({ name: "X-HTTP-Method-Override", value: method });
        }

        $.ajax(options);
    }

    function validate(form) {
        var validationInfo = $(form).data(data_validation);
        return !validationInfo || !validationInfo.validate || validationInfo.validate();
    }

    $(document).on("click", "a[data-ajax=true]", function (evt) {
        evt.preventDefault();
        asyncRequest(this, {
            url: this.href,
            type: "GET",
            data: []
        });
    });

    $(document).on("click", "form[data-ajax=true] input[type=image]", function (evt) {
        var name = evt.target.name,
            target = $(evt.target),
            form = $(target.parents("form")[0]),
            offset = target.offset();

        form.data(data_click, [
            { name: name + ".x", value: Math.round(evt.pageX - offset.left) },
            { name: name + ".y", value: Math.round(evt.pageY - offset.top) }
        ]);

        setTimeout(function () {
            form.removeData(data_click);
        }, 0);
    });

    $(document).on("click", "form[data-ajax=true] :submit", function (evt) {
        var name = evt.currentTarget.name,
            target = $(evt.target),
            form = $(target.parents("form")[0]);

        form.data(data_click, name ? [{ name: name, value: evt.currentTarget.value }] : []);
        form.data(data_target, target);

        setTimeout(function () {
            form.removeData(data_click);
            form.removeData(data_target);
        }, 0);
    });

    $(document).on("submit", "form[data-ajax=true]", function (evt) {
        var clickInfo = $(this).data(data_click) || [],
            clickTarget = $(this).data(data_target),
            isCancel = clickTarget && clickTarget.hasClass("cancel");
        evt.preventDefault();
        if (!isCancel && !validate(this)) {
            return;
        }
        asyncRequest(this, {
            url: this.action,
            type: this.method || "GET",
            data: clickInfo.concat($(this).serializeArray())
        });
    });
}(jQuery));<|endoftext|>


<commit_before>import _ from 'lodash';

import {updateIn, $eachValue} from '../src';

const users = {
  mary: {
    name: {
      first: 'Mary',
      last: 'Bar'
    },
    friends: [],
    balance: 1000
  },
  joe: {
    name: {
      first: 'Joe',
      last: 'Foo'
    },
    friends: [],
    balance: 100
  }
};

export default [
  {
    name: 'lodash mapValues',
    test: () => (
      _.mapValues(users,
        user => {
          if (user.balance < 500) {
            return user;
          }
          return {
            ...user,
            balance: user.balance + 10
          };
        }
      )
    ),
    key: 'lodashMapValues'
  },
  {
    name: 'qim updateIn',
    test: () => updateIn([$eachValue, 'balance', bal => bal >= 500], bal => bal + 10, users),
    compare: {
      lodashMapValues: .5
    }
  }
];
<commit_msg>add state wrapper in updateIn benchmark<commit_after>import _ from 'lodash';

import {updateIn, $eachValue} from '../src';

const state = {
  users: {
    mary: {
      name: {
        first: 'Mary',
        last: 'Bar'
      },
      friends: [],
      balance: 1000
    },
    joe: {
      name: {
        first: 'Joe',
        last: 'Foo'
      },
      friends: [],
      balance: 100
    }
  }
};

export default [
  {
    name: 'lodash mapValues',
    test: () => (
      _.update(state, 'users', users => _.mapValues(users,
        user => {
          if (user.balance < 500) {
            return user;
          }
          return {
            ...user,
            balance: user.balance + 10
          };
        }
      ))
    ),
    key: 'lodashMapValues'
  },
  {
    name: 'qim updateIn',
    test: () => updateIn(['users', $eachValue, 'balance', bal => bal >= 500], bal => bal + 10, state),
    compare: {
      lodashMapValues: .5
    }
  }
];
<|endoftext|>


<commit_before>// -*- indent-tabs-mode: nil -*-

var repoDataSignal = {};
var taskData = {};
var taskNames = ['build', 'smoketest', 'integrationtest'];

function _loadTask(taskname) {
    var url = 'work/tasks/' + taskname + '/current/meta.json';
    console.log("loading " + url);
    $.getJSON(url, function(data) {
        console.log("load of " + taskname + " complete");
        taskData[taskname] = data;
        $(repoDataSignal).trigger("taskdata-changed", [taskname]);
    });
}

function repowebInit() {
    for (var i = 0; i < taskNames.length; i++) {
        _loadTask(taskNames[i]);
    }
}

function timeago(d, now) {
    var diffSeconds = (now.getTime() - d.getTime()) / 1000;
    if (diffSeconds < 0)
        return "a moment ago";
    var units = [["seconds", 60],
                 ["minutes", 60*60],
                 ["hours", 60*60*24],
                 ["days", -1]];
    for (var i = 0; i < units.length; i++) {
        var unitItem = units[i];
        var divisor = i == 0 ? 1 : units[i-1][1];
        if (unitItem[1] == -1 || diffSeconds < unitItem[1]) {
            return "" + (Math.floor(diffSeconds / divisor)) + " " + unitItem[0] + " ago";
        }
    }
}

function buildDiffAppend(container, buildDiff) {
    if (!buildDiff)
        return document.createTextNode("No changes or new build");
    var added = buildDiff[0];
    var modified = buildDiff[1];
    var removed = buildDiff[2];

    if (added.length > 0)
        buildDiffComponentAppend(container, 'Added', added);
    if (modified.length > 0)
        buildDiffComponentAppend(container, 'Updated', modified);
    if (removed.length > 0)
        buildDiffComponentAppend(container, 'removed', removed);
}

function renderBuild(container, build) {
    var now = new Date();

    var version = build['meta']['version'];

    var divider = document.createElement('li');
    container.appendChild(divider);
    divider.setAttribute('data-role', 'list-divider');
    divider.setAttribute('role', 'heading');
    divider.appendChild(document.createTextNode(version));
    if (build['timestamp']) {
        var endTimestamp = new Date(build['timestamp'] * 1000);
        var span = document.createElement('span');
        divider.appendChild(span);
        $(span).addClass("time");
        span.appendChild(document.createTextNode(timeago(endTimestamp, now)));
    }

    var li = document.createElement('li');
    li.setAttribute('data-theme', '');
    container.appendChild(li);
    var a = document.createElement('a');
    li.appendChild(a);
    a.setAttribute('href', 'work/tasks/build/' + build['v'] + '/log');
    a.setAttribute('rel', 'external');

    var state = build['state'];

    if (state == 'running') {
      a.appendChild(document.createTextNode("Running: "));
    }

    buildDiffAppend(a, build['diff']);

    if (state != 'running') {
        var p = document.createElement('span');
        a.appendChild(p);
        var stateSpan = document.createElement('span');
        p.appendChild(stateSpan);
        if (state == 'success')
            li.setAttribute('data-icon', 'check');
        else if (state == 'failed')
            li.setAttribute('data-icon', 'alert');
    }

}

function _renderTask(taskName) {
    var statusNode = $("#" + taskName + "-link").get(0);
    $(statusNode).empty();
    var spanNode = $("#" + taskName + "-span").get(0);
    $(spanNode).empty();

    var meta = taskData[taskName];
    var ref = 'work/tasks/' + taskName;
    if (meta.success)
        ref += '/successful';
    else
        ref += '/failed';
    ref += '/' + meta.taskVersion;
    statusNode.setAttribute('href', ref);
    statusNode.setAttribute('rel', 'external');
    var text = meta.taskVersion + ': ' + (meta.success ? "success" : "failed ");
    statusNode.appendChild(document.createTextNode(text));
    if (meta['status'])
        spanNode.appendChild(document.createTextNode('  ' + meta['status']));
}

function repowebIndexInit() {
    repowebInit();
    $(repoDataSignal).on("taskdata-changed", function (event, taskName) {
        _renderTask(taskName);
    });
}
<commit_msg>build.gnome.org: Put in a self-enclosed module pattern<commit_after>// -*- indent-tabs-mode: nil -*-

(function($, exports) {

    var repoDataSignal = {};
    var taskData = {};
    var taskNames = ['build', 'smoketest', 'integrationtest'];

    function _loadTask(taskname) {
        var url = 'work/tasks/' + taskname + '/current/meta.json';
        console.log("loading " + url);
        $.getJSON(url, function(data) {
            console.log("load of " + taskname + " complete");
            taskData[taskname] = data;
            $(repoDataSignal).trigger("taskdata-changed", [taskname]);
        });
    }

    function repowebInit() {
        for (var i = 0; i < taskNames.length; i++) {
            _loadTask(taskNames[i]);
        }
    }

    function timeago(d, now) {
        var diffSeconds = (now.getTime() - d.getTime()) / 1000;
        if (diffSeconds < 0)
            return "a moment ago";
        var units = [["seconds", 60],
                     ["minutes", 60*60],
                     ["hours", 60*60*24],
                     ["days", -1]];
        for (var i = 0; i < units.length; i++) {
            var unitItem = units[i];
            var divisor = i == 0 ? 1 : units[i-1][1];
            if (unitItem[1] == -1 || diffSeconds < unitItem[1]) {
                return "" + (Math.floor(diffSeconds / divisor)) + " " + unitItem[0] + " ago";
            }
        }
    }

    function buildDiffAppend(container, buildDiff) {
        if (!buildDiff)
            return document.createTextNode("No changes or new build");
        var added = buildDiff[0];
        var modified = buildDiff[1];
        var removed = buildDiff[2];

        if (added.length > 0)
            buildDiffComponentAppend(container, 'Added', added);
        if (modified.length > 0)
            buildDiffComponentAppend(container, 'Updated', modified);
        if (removed.length > 0)
            buildDiffComponentAppend(container, 'removed', removed);
    }

    function renderBuild(container, build) {
        var now = new Date();

        var version = build['meta']['version'];

        var divider = document.createElement('li');
        container.appendChild(divider);
        divider.setAttribute('data-role', 'list-divider');
        divider.setAttribute('role', 'heading');
        divider.appendChild(document.createTextNode(version));
        if (build['timestamp']) {
            var endTimestamp = new Date(build['timestamp'] * 1000);
            var span = document.createElement('span');
            divider.appendChild(span);
            $(span).addClass("time");
            span.appendChild(document.createTextNode(timeago(endTimestamp, now)));
        }

        var li = document.createElement('li');
        li.setAttribute('data-theme', '');
        container.appendChild(li);
        var a = document.createElement('a');
        li.appendChild(a);
        a.setAttribute('href', 'work/tasks/build/' + build['v'] + '/log');
        a.setAttribute('rel', 'external');

        var state = build['state'];

        if (state == 'running') {
            a.appendChild(document.createTextNode("Running: "));
        }

        buildDiffAppend(a, build['diff']);

        if (state != 'running') {
            var p = document.createElement('span');
            a.appendChild(p);
            var stateSpan = document.createElement('span');
            p.appendChild(stateSpan);
            if (state == 'success')
                li.setAttribute('data-icon', 'check');
            else if (state == 'failed')
                li.setAttribute('data-icon', 'alert');
        }

    }

    function _renderTask(taskName) {
        var statusNode = $("#" + taskName + "-link").get(0);
        $(statusNode).empty();
        var spanNode = $("#" + taskName + "-span").get(0);
        $(spanNode).empty();

        var meta = taskData[taskName];
        var ref = 'work/tasks/' + taskName;
        if (meta.success)
            ref += '/successful';
        else
            ref += '/failed';
        ref += '/' + meta.taskVersion;
        statusNode.setAttribute('href', ref);
        statusNode.setAttribute('rel', 'external');
        var text = meta.taskVersion + ': ' + (meta.success ? "success" : "failed ");
        statusNode.appendChild(document.createTextNode(text));
        if (meta['status'])
            spanNode.appendChild(document.createTextNode('  ' + meta['status']));
    }

    function repowebIndexInit() {
        repowebInit();
        $(repoDataSignal).on("taskdata-changed", function (event, taskName) {
            _renderTask(taskName);
        });
    }

    exports.repowebIndexInit = repowebIndexInit;

})(jQuery, window);
<|endoftext|>


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

var request = require('request');
var pays = require('../config/pays');
var util = require('util');
var async = require('async');
var database = require('./node-mongodb');

// database.dropCollection();
database.ensureIndex();

REQ_TIMEOUT = 300000;
// Max events per page allowed by SK API = 100
MAX_PER_PAGE = 100;
var apiKey = 'apikey=iHuSRRJBhfdVPdJL';
var per_page = '&per_page='+MAX_PER_PAGE;
var api_loc = 'http://api.songkick.com/api/3.0/search/locations.json?'+apiKey+per_page;
var api_eve = 'http://api.songkick.com/api/3.0/events.json?'+apiKey+'&page=%s'+per_page;
var img_cdn = 'http://images.sk-static.com/images/media/profile_images/artists/%s/card_avatar';
var api_geo = 'http://open.mapquestapi.com/nominatim/v1/reverse.php?format=json&lat=%s&lon=%s';
// var api_geo = 'http://nominatim.openstreetmap.org/reverse?format=json&lat=%s&lon=%s';

console.log('\n*** Total number of locations to process : ' + pays.country.length + ' ***\n');
console.log('*** Please be patient while sending requests this may take a while... ****\n');

/*
 * iterating cities
 */
function iterateCityFile(url, pays){
    for(i=0; i<pays.country.length; i++){
        var _url = url + '&query=' + pays.country[i];
        getCityId(_url, pays.country[i]);
    };
}

function getCityId (url, country) {
    options = { uri: url, timeout: REQ_TIMEOUT};
    console.log('Fetching attributes for '+country+' with : '+options+'\n');
    request(options, function(err, res, results){
        if(!err) {
            var r = JSON.parse(results).resultsPage;
            if(r.status=='ok'){
                (r.results.location).forEach(function(item){
                    var city = item.city.displayName;
                    _url = api_eve + '&location=sk:'+ item.metroArea.id;
                    getEventAttr(_url, city);
                });
            }
        }
    });
}

function getEventAttr (url, city_name) {
    // Replace page number placeholder in url
    options = { uri: util.format(url, '1'), timeout: REQ_TIMEOUT};
    request(options, function(err, res, results){
        if(!err){
            var r = (JSON.parse(results)).resultsPage;
            console.log('Fetching attributes for '+city_name+' with : '+options + ' -- totalEntries= '+r.totalEntries);
            if(r.totalEntries > 0){
                // (r.totalEntries/MAX_PER_PAGE)+1 = total pages
                var totalPages = parseInt(r.totalEntries/MAX_PER_PAGE)+1;
                for(p=1; p<totalPages; p++){
                    _url = util.format(url, p);
                    getEventPerCityId(_url, city_name);
                }
            }
        }
    });
}

function getEventPerCityId (url, city_name) {
    options = { uri: url, timeout: REQ_TIMEOUT};
    // console.log('Fetching events for : ' + city_name + ' with : ' + options+'\n');
    request(options, function(err, res, results){
        if(!err){
            var r = JSON.parse(results).resultsPage;
            if(r.status=='ok'){
                async.map(r.results.event, function eachItem (item, callback){
                    var artist = [];
                    (item.performance).forEach(function(a){
                        artist.push(a.displayName);
                    });
                    reverseGeocode(item.location.lat, item.location.lng, function(err, result){
                        if(!err){
                            var eve = {
                                        '_id' : 'sk_'+item.id,
                                        'title' : item.displayName,
                                        'artist' : artist,
                                        'latlng' : [parseFloat(item.location.lat), parseFloat(item.location.lng)],
                                        'address' : result,
                                        'startDate' : ((!item.start.datetime) ?  new Date(item.start.date) : new Date(item.start.datetime)),
                                        'url' : item.uri,
                                        'img' : ((item.performance[0]) ? util.format(img_cdn, item.performance[0].artist.id) : null),
                                        'score' : 0,
                                        'source' : 'songkick'
                                    };
                            callback(null, eve);
                        }
                    });
                }, function done (err, result){
                    console.log('Inserting events for -- ' + city_name + '--');
                    database.insertData(result);
                });
            }
        }
    });
}

function reverseGeocode (lat, lng, callback) {
    url = util.format(api_geo, lat, lng);
    request({uri: url}, function(err, res, result){
        if(!err){
            var r = null;
            try{
                r = JSON.parse(result);
                return callback(null, r.house_number+', '+r.road+' '+r.postcode+', '+r.city+' '+r.country);
            }
            catch (e) {
                return callback(e, null);
            }
        } else return callback(err, null);
    });
}

function startUpdate(){
    iterateCityFile(api_loc, pays);
}

exports.startUpdate = startUpdate;<commit_msg>bug fix : removed address geocoder (limited). minor improvements<commit_after>#!/usr/bin/env node

var request = require('request');
var pays = require('../config/pays');
var util = require('util');
var async = require('async');
var database = require('./node-mongodb');

// database.dropCollection();
database.ensureIndex();

REQ_TIMEOUT = 300000;
// Max events per page allowed by SK API = 100
MAX_PER_PAGE = 100;
var apiKey = 'apikey=iHuSRRJBhfdVPdJL';
var per_page = '&per_page='+MAX_PER_PAGE;
var api_loc = 'http://api.songkick.com/api/3.0/search/locations.json?'+apiKey+per_page;
var api_eve = 'http://api.songkick.com/api/3.0/events.json?'+apiKey+'&page=%s'+per_page;
var img_cdn = 'http://images.sk-static.com/images/media/profile_images/artists/%s/card_avatar';

console.log('\n*** Total number of locations to process : ' + pays.country.length + ' ***\n');
console.log('*** Please be patient while sending requests this may take a while... ****\n');

/*
 * iterating cities
 */
function iterateCityFile(url, pays){
    for(i=0; i<pays.country.length; i++){
        var _url = url + '&query=' + pays.country[i];
        getCityId(_url, pays.country[i]);
    };
}

function getCityId (url, country) {
    options = { uri: url, timeout: REQ_TIMEOUT};
    console.log('Fetching attributes for '+country+' with : '+options+'\n');
    request(options, function(err, res, results){
        if(!err) {
            var r = JSON.parse(results).resultsPage;
            if(r.status=='ok'){
                (r.results.location).forEach(function(item){
                    var city = item.city.displayName;
                    _url = api_eve + '&location=sk:'+ item.metroArea.id;
                    getEventAttr(_url, city);
                });
            }
        }
    });
}

function getEventAttr (url, city_name) {
    // Replace page number placeholder in url
    options = { uri: util.format(url, '1'), timeout: REQ_TIMEOUT};
    request(options, function(err, res, results){
        if(!err){
            var r = (JSON.parse(results)).resultsPage;
            console.log('Fetching attributes for '+city_name+' with : '+options + ' -- totalEntries= '+r.totalEntries);
            if(r.totalEntries > 0){
                var totalPages = parseInt(r.totalEntries/MAX_PER_PAGE)+2;
                for(p=1; p<totalPages; p++){
                    _url = util.format(url, p);
                    getEventPerCityId(_url, city_name);
                }
            }
        }
    });
}

function getEventPerCityId (url, city_name) {
    options = { uri: url, timeout: REQ_TIMEOUT};
    // console.log('Fetching events for : ' + city_name + ' with : ' + options+'\n');
    request(options, function(err, res, results){
        if(!err){
            var r = JSON.parse(results).resultsPage;
            parseJson(results, function(err, res){
                if(!err){
                    var r = res.resultsPage;
                    if(r.status=='ok'){
                        async.map(r.results.event, function eachItem (item, callback){
                            var artist = [];
                            (item.performance).forEach(function(a){
                                artist.push(a.displayName);
                            });
                            var eve = {
                                        '_id' : 'sk_'+item.id,
                                        'title' : item.displayName,
                                        'artist' : artist,
                                        'latlng' : [parseFloat(item.location.lat), parseFloat(item.location.lng)],
                                        'address' : "No address provided",
                                        'startDate' : ((!item.start.datetime) ?  new Date(item.start.date) : new Date(item.start.datetime)),
                                        'url' : item.uri,
                                        'img' : ((item.performance[0]) ? util.format(img_cdn, item.performance[0].artist.id) : null),
                                        'score' : 0,
                                        'source' : 'songkick'
                                    };
                            callback(null, eve);
                        }, function done (err, result){
                            console.log('Inserting events for -- ' + city_name + '--');
                            database.insertData(result);
                        });
                    }
                }
            });
        }
    });
}

function parseJson(obj, fn){
    var r = '';
    try {
        r = JSON.parse(obj);
        return fn(null, r);
    } catch (e) {
        return fn(e, null);
    }
}

function startUpdate(){
    iterateCityFile(api_loc, pays);
}

exports.startUpdate = startUpdate;<|endoftext|>


<commit_before>define(function () {
    "use strict";
    var parse, query, graphPatternRecurser, graphPatternFilter;
    parse = function (text) {
        var graphPatternPattern, graphPatternRegExp, graphPatternMatcher, graphPatternListPattern, sparqlPattern, sparqlRegExp, sparqlMatcher, graphPatternGroup, queryObject;
        // Pattern for graph patterns.
        // Catches subject, predicate, and object of a graph pattern.
        //
        // Documentation:
        // (                                # Subject
        //   <[^>]+>                        #   URI (sloppy!)
        // |
        //   \?\w+                          #   Variable
        // )
        // \s+
        // (                                # Predicate
        //   <[^>]+>                        #   URI (sloppy!)
        // |
        //   \?\w+                          #   Variable
        // )
        // \s+
        // (                                # Object
        //   <[^>]+>                        #   URI (sloppy!)
        // |
        //   "[^"]+"                        #   Literal
        // |
        //   \?\w+                          #   Variable
        // )
        graphPatternPattern = "(<[^>]+>|\\?\\w+)\\s+(<[^>]+>|\\?\\w+)\\s+(<[^>]+>|\"[^\"]+\"|\\?\\w+)";
        graphPatternRegExp = new RegExp("\\s*" + graphPatternPattern + "(?:\\s+\\.)?\\s*");
        // Pattern for lists of graph patterns.
        // Uses the graphPatternPattern defined above, but converts groups to non-catching ones.
        // Catches the whole list of graph patterns.
        //
        // Documentation:
        // (                                # Graph patterns are optional
        //   (?<graphPatternPattern>)             # First graph pattern
        //   (?:                            # More graph patterns
        //     \s+
        //     \.                           # Separated by period
        //     \s+
        //     (?<graphPatternPattern>)
        //   )*
        //   (?:                            # Optional final period
        //     \s+
        //     \.
        //   )?
        // )?
        graphPatternListPattern = "(" + graphPatternPattern.replace("(", "(?:") + "(?:\\s+\\.\\s+" + graphPatternPattern.replace("(", "(?:") + ")*(?:\\s+\\.)?)?";
        // Pattern for SPARQL queries.
        // Right now only the SELECT subset is supported.
        // Uses the graphPatternListPattern defined above.
        // Catches query form, variables, and list of graph patterns.
        //
        // Documentation:
        // (SELECT)                         # Query form
        // \s+
        // (                                # Variables
        //   (?:
        //     \*                           #   Either '*'
        //   )
        // |
        //   (?:                            #   Or a single variable
        //     \?\w+
        //   )
        //   (?:                            #   Or a list of named variables
        //     \s+
        //     \?
        //     \w+
        //   )*
        // )
        // \s+
        // WHERE                            # WHERE clause
        // \s+
        // \{
        // \s*
        // (?<graphPatternListPattern>)
        // \s*
        // \}
        sparqlPattern = "(SELECT)\\s+((?:\\*)|(?:\\?\\w+)(?:\\s+\\?\\w+)*)\\s+WHERE\\s+\\{\\s*" + graphPatternListPattern + "\\s*\\}";
        // Add some anchors for exact matches.
        sparqlRegExp = new RegExp("^\\s*" + sparqlPattern + "\\s*$");
        sparqlMatcher = text.match(sparqlRegExp);
        if (sparqlMatcher !== null) {
            queryObject = {
                type: sparqlMatcher[1],
                graphPatterns: []
            };
            if (sparqlMatcher[2] === "*") {
                queryObject.variables = sparqlMatcher[2];
            } else {
                queryObject.variables = sparqlMatcher[2].split(/\s+/);
            }
            if (sparqlMatcher[3] !== undefined) {
                // It is hard to separate graph patterns by period, because URIs and
                // literals can also contain them. Instead, one pattern is matched at
                // a time and iteratively removed from the original patterns string
                // until there are no patterns left.
                graphPatternGroup = sparqlMatcher[3];
                while (graphPatternGroup !== "") {
                    graphPatternMatcher = graphPatternGroup.match(graphPatternRegExp);
                    queryObject.graphPatterns.push([graphPatternMatcher[1], graphPatternMatcher[2], graphPatternMatcher[3]]);
                    graphPatternGroup = graphPatternGroup.replace(graphPatternMatcher[0], "");
                }
            }
            return queryObject;
        } else {
            throw {
                message: "unrecognized text"
            };
        }
    };
    query = function (queryObject, graphPatternResolver, callback) {
        var variables, graphPatterns, initialBindings;
        variables = queryObject.variables;
        graphPatterns = queryObject.graphPatterns;
        initialBindings = [];
        graphPatternRecurser(graphPatterns, initialBindings, graphPatternResolver, function (bindings) {
            graphPatternFilter(variables, bindings, callback);
        });
    };
    graphPatternRecurser = function (graphPatterns, lastBindings, graphPatternResolver, callback) {
        var graphPatterns, graphPattern, binding, bindingPositions, hasBindings, query, xhr;
        if (graphPatterns.length > 0) {
            // Process only one pattern at a time.
            graphPattern = graphPatterns.pop();
            binding = []; // The binding serves as a template for creating concrete bindings once the triples are retrieved.
            bindingPositions = {}; // Save the position of the variable in the triple (subject: 0, predicate: 1, object: 2) for easy retrieval later.
            hasBindings = false; // There is no point in doing another recursion for an empty graph pattern.
            graphPattern.forEach(function (node, index) {
                var matcher;
                matcher = node.match(/\?(\w+)/);
                if (matcher !== null) {
                    // Store variable names meant for binding without initial "?".
                    binding.push(matcher[1]);
                    bindingPositions[matcher[1]] = index;
                    hasBindings = true;
                }
            });
            // There has to be at least one binding, otherwise the operation is pointless.
            if (hasBindings) {
                // Create Sessel query string.
                query = "_rewrite";
                graphPattern.forEach(function (node, index) {
                    var matcher;
                    matcher = node.match(/\?(\w+)/);
                    if (matcher === null) {
                        if (index === 0) {
                            query += "/s/" + encodeURIComponent(node.substring(1, node.length - 1));
                        } else if (index === 1) {
                            query += "/p/" + encodeURIComponent(node.substring(1, node.length - 1));
                        } else if (index === 2) {
                            if (node.match(/\"[^"]+\"/)) {
                                query += "/o/lit/" + encodeURIComponent(node.substring(1, node.length - 1));
                            } else {
                                query += "/o/uri/" + encodeURIComponent(node.substring(1, node.length - 1));
                            }
                        }
                    }
                });
                query += "?format=json";
                // Resolve query using some mechanism, be it XHR, http.request, or something else.
                graphPatternResolver(query, function (triples) {
                    var newBindings;
                    // Bind triples to binding.
                    newBindings = [];
                    triples.forEach(function (triple) {
                        var newBinding, bindingPosition;
                        newBinding = {};
                        triple.splice(2, 1);
                        // If there are no old bindings, just keep the new ones.
                        if (lastBindings.length === 0) {
                            // Populate bindings based on triple.
                            binding.forEach(function (bindingVariable) {
                                newBinding[bindingVariable] = triple[bindingPositions[bindingVariable]];
                            });
                        } else {
                            lastBindings.forEach(function (lastBinding) {
                                var merge, lastBindingVariable;
                                // If a binding is present in both bindings, they have to match.
                                merge = true;
                                binding.forEach(function (bindingVariable) {
                                    if (binding.hasOwnProperty(bindingVariable)) {
                                        if (lastBinding.hasOwnProperty(bindingVariable)) {
                                            if (lastBinding[bindingVariable] !== triple[bindingPositions[bindingVariable]]) {
                                                merge = false;
                                            } else {
                                                newBinding[bindingVariable] = triple[bindingPositions[bindingVariable]];
                                            }
                                        }
                                    }
                                });
                                // Merge old and new bindings.
                                if (merge) {
                                    for (lastBindingVariable in lastBinding) {
                                        if (lastBinding.hasOwnProperty(lastBindingVariable)) {
                                            // Only copy if it's not already in newBinding.
                                            if (!newBinding.hasOwnProperty(lastBindingVariable)) {
                                                newBinding[lastBindingVariable] = lastBinding[lastBindingVariable];
                                            }
                                        }
                                    }
                                }
                            });
                        }
                        newBindings.push(newBinding);
                    });
                    // Carry over the graphPatterns that are left and newBindings over to the next step.
                    graphPatternRecurser(graphPatterns, newBindings, graphPatternResolver, callback);
                });
            } else {
                // Return the bindings from the last step if there are no bindings.
                callback(lastBindings);
            }
        } else {
            // Return the bindings from the last step if there are no graph patterns (left).
            callback(lastBindings);
        }
    };
    graphPatternFilter = function (variables, bindings, callback) {
        var newBindings = [];
        if (variables === "*") {
            callback(bindings);
        } else {
            bindings.forEach(function (binding) {
                var newBinding;
                newBinding = {};
                variables.forEach(function (variable) {
                    // Strip off question mark.
                    var variableName = variable.substring(1);
                    if (binding.hasOwnProperty(variableName)) {
                        newBinding[variableName] = binding[variableName];
                    } else {
                        newBinding[variableName] = null;
                    }
                });
                newBindings.push(newBinding);
            });
            callback(newBindings);
        }
    };
    return {
        parse: parse,
        query: query
    };
});
<commit_msg>Fixed bugs in merging two bindings.<commit_after>define(function () {
    "use strict";
    var parse, query, graphPatternRecurser, graphPatternFilter;
    parse = function (text) {
        var graphPatternPattern, graphPatternRegExp, graphPatternMatcher, graphPatternListPattern, sparqlPattern, sparqlRegExp, sparqlMatcher, graphPatternGroup, queryObject;
        // Pattern for graph patterns.
        // Catches subject, predicate, and object of a graph pattern.
        //
        // Documentation:
        // (                                # Subject
        //   <[^>]+>                        #   URI (sloppy!)
        // |
        //   \?\w+                          #   Variable
        // )
        // \s+
        // (                                # Predicate
        //   <[^>]+>                        #   URI (sloppy!)
        // |
        //   \?\w+                          #   Variable
        // )
        // \s+
        // (                                # Object
        //   <[^>]+>                        #   URI (sloppy!)
        // |
        //   "[^"]+"                        #   Literal
        // |
        //   \?\w+                          #   Variable
        // )
        graphPatternPattern = "(<[^>]+>|\\?\\w+)\\s+(<[^>]+>|\\?\\w+)\\s+(<[^>]+>|\"[^\"]+\"|\\?\\w+)";
        graphPatternRegExp = new RegExp("\\s*" + graphPatternPattern + "(?:\\s+\\.)?\\s*");
        // Pattern for lists of graph patterns.
        // Uses the graphPatternPattern defined above, but converts groups to non-catching ones.
        // Catches the whole list of graph patterns.
        //
        // Documentation:
        // (                                # Graph patterns are optional
        //   (?<graphPatternPattern>)             # First graph pattern
        //   (?:                            # More graph patterns
        //     \s+
        //     \.                           # Separated by period
        //     \s+
        //     (?<graphPatternPattern>)
        //   )*
        //   (?:                            # Optional final period
        //     \s+
        //     \.
        //   )?
        // )?
        graphPatternListPattern = "(" + graphPatternPattern.replace("(", "(?:") + "(?:\\s+\\.\\s+" + graphPatternPattern.replace("(", "(?:") + ")*(?:\\s+\\.)?)?";
        // Pattern for SPARQL queries.
        // Right now only the SELECT subset is supported.
        // Uses the graphPatternListPattern defined above.
        // Catches query form, variables, and list of graph patterns.
        //
        // Documentation:
        // (SELECT)                         # Query form
        // \s+
        // (                                # Variables
        //   (?:
        //     \*                           #   Either '*'
        //   )
        // |
        //   (?:                            #   Or a single variable
        //     \?\w+
        //   )
        //   (?:                            #   Or a list of named variables
        //     \s+
        //     \?
        //     \w+
        //   )*
        // )
        // \s+
        // WHERE                            # WHERE clause
        // \s+
        // \{
        // \s*
        // (?<graphPatternListPattern>)
        // \s*
        // \}
        sparqlPattern = "(SELECT)\\s+((?:\\*)|(?:\\?\\w+)(?:\\s+\\?\\w+)*)\\s+WHERE\\s+\\{\\s*" + graphPatternListPattern + "\\s*\\}";
        // Add some anchors for exact matches.
        sparqlRegExp = new RegExp("^\\s*" + sparqlPattern + "\\s*$");
        sparqlMatcher = text.match(sparqlRegExp);
        if (sparqlMatcher !== null) {
            queryObject = {
                type: sparqlMatcher[1],
                graphPatterns: []
            };
            if (sparqlMatcher[2] === "*") {
                queryObject.variables = sparqlMatcher[2];
            } else {
                queryObject.variables = sparqlMatcher[2].split(/\s+/);
            }
            if (sparqlMatcher[3] !== undefined) {
                // It is hard to separate graph patterns by period, because URIs and
                // literals can also contain them. Instead, one pattern is matched at
                // a time and iteratively removed from the original patterns string
                // until there are no patterns left.
                graphPatternGroup = sparqlMatcher[3];
                while (graphPatternGroup !== "") {
                    graphPatternMatcher = graphPatternGroup.match(graphPatternRegExp);
                    queryObject.graphPatterns.push([graphPatternMatcher[1], graphPatternMatcher[2], graphPatternMatcher[3]]);
                    graphPatternGroup = graphPatternGroup.replace(graphPatternMatcher[0], "");
                }
            }
            return queryObject;
        } else {
            throw {
                message: "unrecognized text"
            };
        }
    };
    query = function (queryObject, graphPatternResolver, callback) {
        var variables, graphPatterns, initialBindings;
        variables = queryObject.variables;
        graphPatterns = queryObject.graphPatterns;
        initialBindings = [];
        graphPatternRecurser(graphPatterns, initialBindings, graphPatternResolver, function (bindings) {
            graphPatternFilter(variables, bindings, callback);
        });
    };
    graphPatternRecurser = function (graphPatterns, lastBindings, graphPatternResolver, callback) {
        var graphPatterns, graphPattern, binding, bindingPositions, hasBindings, query, xhr;
        if (graphPatterns.length > 0) {
            // Process only one pattern at a time.
            graphPattern = graphPatterns.pop();
            binding = []; // The binding serves as a template for creating concrete bindings once the triples are retrieved.
            bindingPositions = {}; // Save the position of the variable in the triple (subject: 0, predicate: 1, object: 2) for easy retrieval later.
            hasBindings = false; // There is no point in doing another recursion for an empty graph pattern.
            graphPattern.forEach(function (node, index) {
                var matcher;
                matcher = node.match(/\?(\w+)/);
                if (matcher !== null) {
                    // Store variable names meant for binding without initial "?".
                    binding.push(matcher[1]);
                    bindingPositions[matcher[1]] = index;
                    hasBindings = true;
                }
            });
            // There has to be at least one binding, otherwise the operation is pointless.
            if (hasBindings) {
                // Create Sessel query string.
                query = "_rewrite";
                graphPattern.forEach(function (node, index) {
                    var matcher;
                    matcher = node.match(/\?(\w+)/);
                    if (matcher === null) {
                        if (index === 0) {
                            query += "/s/" + encodeURIComponent(node.substring(1, node.length - 1));
                        } else if (index === 1) {
                            query += "/p/" + encodeURIComponent(node.substring(1, node.length - 1));
                        } else if (index === 2) {
                            if (node.match(/\"[^"]+\"/)) {
                                query += "/o/lit/" + encodeURIComponent(node.substring(1, node.length - 1));
                            } else {
                                query += "/o/uri/" + encodeURIComponent(node.substring(1, node.length - 1));
                            }
                        }
                    }
                });
                query += "?format=json";
                // Resolve query using some mechanism, be it XHR, http.request, or something else.
                graphPatternResolver(query, function (triples) {
                    var newBindings;
                    // Bind triples to binding.
                    newBindings = [];
                    triples.forEach(function (triple) {
                        var newBinding, bindingPosition;
                        newBinding = {};
                        triple.splice(2, 1);
                        // If there are no old bindings, just keep the new ones.
                        if (lastBindings.length === 0) {
                            // Populate bindings based on triple.
                            binding.forEach(function (bindingVariable) {
                                newBinding[bindingVariable] = triple[bindingPositions[bindingVariable]];
                            });
                            newBindings.push(newBinding);
                        } else {
                            lastBindings.forEach(function (lastBinding) {
                                var merge, lastBindingVariable;
                                merge = true;
                                // Check if all bindings that are also in the last binding match.
                                binding.forEach(function (bindingVariable) {
                                    if (lastBinding.hasOwnProperty(bindingVariable) && lastBinding[bindingVariable] !== triple[bindingPositions[bindingVariable]]) {
                                        // Discard binding if the values do not match.
                                        merge = false;
                                    }
                                });
                                // Merge new and last binding.
                                if (merge) {
                                    // Copy last bindings.
                                    for (lastBindingVariable in lastBinding) {
                                        if (lastBinding.hasOwnProperty(lastBindingVariable)) {
                                            newBinding[lastBindingVariable] = lastBinding[lastBindingVariable];
                                        }
                                    }
                                    // Add new bindings that are not already in there.
                                    binding.forEach(function (bindingVariable) {
                                        if (!newBinding.hasOwnProperty(bindingVariable)) {
                                            newBinding[bindingVariable] = triple[bindingPositions[bindingVariable]];
                                        }
                                    });
                                    newBindings.push(newBinding);
                                }
                            });
                        }
                    });
                    // Carry over the graphPatterns that are left and newBindings over to the next step.
                    graphPatternRecurser(graphPatterns, newBindings, graphPatternResolver, callback);
                });
            } else {
                // Return the bindings from the last step if there are no bindings.
                callback(lastBindings);
            }
        } else {
            // Return the bindings from the last step if there are no graph patterns (left).
            callback(lastBindings);
        }
    };
    graphPatternFilter = function (variables, bindings, callback) {
        var newBindings = [];
        if (variables === "*") {
            callback(bindings);
        } else {
            bindings.forEach(function (binding) {
                var newBinding;
                newBinding = {};
                variables.forEach(function (variable) {
                    // Strip off question mark.
                    var variableName = variable.substring(1);
                    if (binding.hasOwnProperty(variableName)) {
                        newBinding[variableName] = binding[variableName];
                    } else {
                        newBinding[variableName] = null;
                    }
                });
                newBindings.push(newBinding);
            });
            callback(newBindings);
        }
    };
    return {
        parse: parse,
        query: query
    };
});
<|endoftext|>