<commit_before>import { prettyNumber } from '../../utils/pretty-number';
import { escapeHtml } from '../../utils/escape-html';
import copyToClipboard from '../../utils/copy-to-clipboard';

function formatExtractionData(res, prop) {
  const { query, result } = res;
  const formattedResult = [];
  if (query.analysis_type !== 'extraction') throw new Error('Analysis type is not an extraction!');
  if (Array.isArray(result)) {
    result.forEach((item) => {
      const obj = {
        query,
        result: item[prop],
      };
      formattedResult.push(obj);
    });
  }
  return formattedResult;
}

export default {
  render: function(){
    let color = this.config.colors[0];
    const theme = this.config.theme;
    const title = this.config.title;
    const subtitle = this.config.subtitle;
    const opts = this.config;
    let value = '-';
    let prevValue= '';
    let html = '';
    let prefix = '';
    let suffix = '';
    let formattedNum;
    let flexDifferenceStyle = '';
    let resultDifference = '';
    let differenceStyle = '';
    let smallerValue = '';
    const { results, previousResults, isExtraction, comparedProp } = this.config;
    if(results && previousResults){
      flexDifferenceStyle = previousResults ? ' metric-comparison' : '';
      smallerValue = previousResults && title ? '-smaller' : '';
      resultDifference = results.result - previousResults.result;
      differenceStyle = (resultDifference > 0) ? '-green' : '-red';
      resultDifference = Math.abs(resultDifference);
      color = '';
    }

    if (this.data() && this.data()[1] && this.data()[1][1] && typeof this.data()[1][1] === 'number') {
      value = this.data()[1][1];
    }

    if (isExtraction && comparedProp) {
      const formattedResult = formatExtractionData(results, comparedProp);

      value = formattedResult[0].result;
      prevValue = formattedResult[1].result;

      flexDifferenceStyle = prevValue ? ' metric-comparison' : '';
      smallerValue = prevValue && title ? '-smaller' : '';
      resultDifference = value - prevValue;
      differenceStyle = (resultDifference > 0) ? '-green' : '-red';
      resultDifference = Math.abs(resultDifference);
      color = '';
    }

    formattedNum = value;
    if ( (typeof opts['prettyNumber'] === 'undefined' || opts['prettyNumber'] === true)
      && !isNaN(parseInt(value)) ) {
        formattedNum = prettyNumber(value);
        if(results && previousResults){
          resultDifference = prettyNumber(resultDifference);
        }
    }

    if (opts['prefix']) {
      prefix = '' + opts['prefix'] + '';
    }
    if (opts['suffix']) {
      suffix = '' + opts['suffix'] + '';
    }
    html += '
';
    html +=   '
';
    if (results && previousResults) {
      html +=   '

' + escapeHtml(resultDifference) + '
';
    }
    if (isExtraction && comparedProp) {
      if (value && prevValue) {
        html +=   '

' + escapeHtml(resultDifference) + '
';
        html += '' + prevValue + '';
      } else {
        html += '
No data to display
';
      }
    }
    html +=     '
' + prefix + escapeHtml(formattedNum) + suffix + '
';
    if (title) {
      html +=   '
' + escapeHtml(title) + '
';
    }
    if (subtitle) {
      html +=   '
' + escapeHtml(subtitle) + '
';
    }
    html +=   '
';
    html += '
';

    this.el().innerHTML = html;

    if (this.config.onrendered) {
      this.config.onrendered();
    }

    if (this.config.utils && this.config.utils.clickToCopyToClipboard) {
      const resultClassName = `.${theme}-metric-value${smallerValue}`;
      document.querySelector(resultClassName).addEventListener('click', e => copyToClipboard(e.target.innerText, e));

      if (results && previousResults) {
        const previousResultsClassName = `.${theme}-metric${differenceStyle}`;
        document.querySelector(previousResultsClassName).addEventListener('click', e => copyToClipboard(e.target.innerText, e));
      }
    }
  },
  update: function(){
    // no special update handling
    this.render();
  },
  destroy: function(){
    // no special clean-up
  }
};
<commit_msg>support nested comparedProp<commit_after>import { prettyNumber } from '../../utils/pretty-number';
import { escapeHtml } from '../../utils/escape-html';
import copyToClipboard from '../../utils/copy-to-clipboard';

function formatExtractionData(res, prop) {
  const { query, result } = res;
  const formattedResult = [];
  if (query.analysis_type !== 'extraction') throw new Error('Analysis type is not an extraction!');
  if (Array.isArray(result)) {
    result.forEach((item) => {
      const itemProp = prop.split('.').reduce((acc, val) => (acc && acc[val]) || null, item);
      const obj = {
        query,
        result: itemProp,
      };
      formattedResult.push(obj);
    });
  }
  return formattedResult;
}

export default {
  render: function(){
    let color = this.config.colors[0];
    const theme = this.config.theme;
    const title = this.config.title;
    const subtitle = this.config.subtitle;
    const opts = this.config;
    let value = '-';
    let prevValue= '';
    let html = '';
    let prefix = '';
    let suffix = '';
    let formattedNum;
    let flexDifferenceStyle = '';
    let resultDifference = '';
    let differenceStyle = '';
    let smallerValue = '';
    const { results, previousResults, isExtraction, comparedProp } = this.config;
    if(results && previousResults){
      flexDifferenceStyle = previousResults ? ' metric-comparison' : '';
      smallerValue = previousResults && title ? '-smaller' : '';
      resultDifference = results.result - previousResults.result;
      differenceStyle = (resultDifference > 0) ? '-green' : '-red';
      resultDifference = Math.abs(resultDifference);
      color = '';
    }

    if (this.data() && this.data()[1] && this.data()[1][1] && typeof this.data()[1][1] === 'number') {
      value = this.data()[1][1];
    }

    if (isExtraction && comparedProp) {
      const formattedResult = formatExtractionData(results, comparedProp);

      value = formattedResult[0].result;
      prevValue = formattedResult[1].result;

      flexDifferenceStyle = prevValue ? ' metric-comparison' : '';
      smallerValue = prevValue && title ? '-smaller' : '';
      resultDifference = value - prevValue;
      differenceStyle = (resultDifference > 0) ? '-green' : '-red';
      resultDifference = Math.abs(resultDifference);
      color = '';
    }

    formattedNum = value;
    if ( (typeof opts['prettyNumber'] === 'undefined' || opts['prettyNumber'] === true)
      && !isNaN(parseInt(value)) ) {
        formattedNum = prettyNumber(value);
        if(results && previousResults){
          resultDifference = prettyNumber(resultDifference);
        }
    }

    if (opts['prefix']) {
      prefix = '' + opts['prefix'] + '';
    }
    if (opts['suffix']) {
      suffix = '' + opts['suffix'] + '';
    }
    html += '
';
    html +=   '
';
    if (results && previousResults) {
      html +=   '

' + escapeHtml(resultDifference) + '
';
    }
    if (isExtraction && comparedProp) {
      if (value && prevValue) {
        html +=   '

' + escapeHtml(resultDifference) + '
';
        html += '' + prevValue + '';
      } else {
        html += '
No data to display
';
      }
    }
    html +=     '
' + prefix + escapeHtml(formattedNum) + suffix + '
';
    if (title) {
      html +=   '
' + escapeHtml(title) + '
';
    }
    if (subtitle) {
      html +=   '
' + escapeHtml(subtitle) + '
';
    }
    html +=   '
';
    html += '
';

    this.el().innerHTML = html;

    if (this.config.onrendered) {
      this.config.onrendered();
    }

    if (this.config.utils && this.config.utils.clickToCopyToClipboard) {
      const resultClassName = `.${theme}-metric-value${smallerValue}`;
      document.querySelector(resultClassName).addEventListener('click', e => copyToClipboard(e.target.innerText, e));

      if (results && previousResults) {
        const previousResultsClassName = `.${theme}-metric${differenceStyle}`;
        document.querySelector(previousResultsClassName).addEventListener('click', e => copyToClipboard(e.target.innerText, e));
      }
    }
  },
  update: function(){
    // no special update handling
    this.render();
  },
  destroy: function(){
    // no special clean-up
  }
};
<|endoftext|>


<commit_before>//Author: Ming Qin at Yahoo! Inc.

d3.drawSankey = function (canvas, inputdata, options) {

  function drawNode(nodes) {
    var node = graph.insert("g", ":first-child").selectAll(".node")
      .data(nodes)
      .enter().append("g")
      .attr("class", "node")
      .attr("transform", function (d) {
        return "translate(" + d.x + "," + d.y + ")";
      })
      .call(d3.behavior.drag()
        .origin(function (d) {
          return d;
        })
        .on("dragstart", function () {
          d3.event.sourceEvent.stopPropagation();
          this.parentNode.appendChild(this);
        })
        .on("drag", function dragmove(d) {
          d3.select(this).attr("transform",
            "translate(" + (
              d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
            ) + "," + (
              d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
            ) + ")");
          sankey.relayout();
          link.attr("d", sankey.link());
          dlink.attr("d", sankey.link());
        })
      );

    node.append("rect")
      .attr("height", function (d) {
        return d.dy;
      })
      .attr("width", sankey.nodeWidth())
      .style("fill", function (d) {
        d.color = color(d.name.replace(/ .*/, ""));
        return d.color;
      })
      .style("stroke", function (d) {
        return d3.rgb(d.color).darker(2);
      });

    node.append("text")
      .attr("x", -6)
      .attr("y", function (d) {
        return d.dy / 2;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", "end")
      .attr("transform", null)
      .text(function (d) {
        return d.disp;
      })
      .filter(function (d) {
        return d.x < width / 2;
      })
      .attr("x", 6 + sankey.nodeWidth())
      .attr("text-anchor", "start");

    return node;
  }

  function drawLink(data, opt) {
    var link = graph.insert("g", ":first-child")
      .attr('id', opt || 'normal')
      .selectAll(".link")
      .data(data)
      .enter().append("path")
      .attr("class", "link" + (" " + opt || ""))
      .attr("d", sankey.link())
      .style("stroke-width", function (d) {
        return Math.max(1, d.dy);
      })
      .sort(function (a, b) {
        return b.dy - a.dy;
      });
    return link;
  }

  var formatNumber = d3.format(",.2f");
  var color = d3.scale.category20();

  var margin = {
      top: 10,
      right: 10,
      bottom: 10,
      left: 10,
    },
    width = options.width - margin.left - margin.right - 15,
    height = options.height - margin.top - margin.bottom - 15;

  var graph = canvas
    .append('svg')
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var sankey = d3.sankey()
    .nodeWidth(15)
    .nodePadding(10)
    .size([width, height]);

  sankey.nodes(inputdata.nodes)
    .flows(inputdata.flows)
    .layout(32);

  var dlink;
  var node = drawNode(sankey.nodes());
  var link = drawLink(sankey.links());
  // .on("mouseover", function (d) {
  //   sankey.dflows(d.flows);
  //   dlink = drawLink(sankey.dlinks(), 'highlight');
  // })
  // .on("mouseout", function (d) {
  //   graph.selectAll("g#highlight").remove();
  // });
  node
    .on("mouseover", function (d) {
      sankey.dflows(d.flows);
      dlink = drawLink(sankey.dlinks(), 'highlight');

      updateTooltip(d);
      canvas.select('#tooltip-container')
        .style('display', 'block');
    })
    .on("mouseout", function (d) {
      graph.selectAll("g#highlight").remove();

      canvas.select('#tooltip-container')
        .style('display', 'none');
    })
    .on('mousemove', function (d) {
      canvas.select('#tooltip-container')
        .style('top', d3.event.pageY + 'px')
        .style('left', d3.event.pageX + 'px');
    });

  link
    .on("mouseover", function (d) {
      sankey.dflows(d.flows);
      dlink = drawLink(sankey.dlinks(), 'highlight');

      updateTooltip(d);
      canvas.select('#tooltip-container')
        .style('display', 'block');
    })
    .on("mouseout", function (d) {
      graph.selectAll("g#highlight").remove();

      canvas.select('#tooltip-container')
        .style('display', 'none');
    })
    .on('mousemove', function (d) {
      canvas.select('#tooltip-container')
        .style('top', d3.event.pageY + 'px')
        .style('left', d3.event.pageX + 'px');
    });

  ///////////////////////
  //// Tooltips

  sankey.nodes().forEach(function(n){
    n.tooltip = {
      name: n.disp,
      value: formatNumber(n.value),
      head: true,
    };
  });
  sankey.links().forEach(function(l){
    l.tooltip = {
      name: l.source.disp + " → " + l.target.disp,
      value: formatNumber(l.value),
      head: true,
    };
  });
  sankey.flows().forEach(function(f){
    var name = '';
    f.thru.forEach(function (n, ind) {
      if (ind !== 0) name += ' → ';
      name += n.disp || n;
    });
    f.tooltip = {
      name: name,
      value: formatNumber(f.value),
    };
  });

  var tooltips = [];
  var tbody = canvas
    .append('div')
      .attr('id', 'tooltip-container')
    .append('table')
      .attr('class', 'tooltip')
    .append('tbody');

  // var tooltipRow = tbody.selectAll('tr')
  //     .data(tooltips);

  //@input d: data, could be node or link
  function updateTooltip(d){
    tooltips = [d.tooltip];
    d.flows.forEach(function(f){
      tooltips.push(f.tooltip);
    });

    tbody.selectAll('*').remove();
    tooltips.forEach(function(tip){
      var tr = tbody.append('tr');
      tr.append('td')
        .attr('class', 'name')
        .classed('head', 'head' in tip)
        .text(tip.name);

      tr.append('td')
        .attr('class', 'value')
        .classed('head', 'head' in tip)
        .text(tip.value);
    });
  }
};

// function flowTooltips(text, d) {
//   var len = d.flows.map(function (f) {
//     return formatNumber(f.value).length;
//   });
//   var maxlen = Math.max.apply(null, len);
//   var fmt = d3.format('<' + maxlen + ',.2f');
//   d.flows.forEach(function (f) {
//     text += '\n' + fmt(f.value) + '\t';
//     f.thru.forEach(function (n, ind) {
//       if (ind !== 0) text += ' → ';
//       text += n.disp || n;
//     });
//   });
//   return text;
// }
<commit_msg>very good. work for nodes as well<commit_after>//Author: Ming Qin at Yahoo! Inc.

d3.drawSankey = function (canvas, inputdata, options) {

  function drawNode(nodes) {
    var node = graph.insert("g", ":first-child").selectAll(".node")
      .data(nodes)
      .enter().append("g")
      .attr("class", "node")
      .attr("transform", function (d) {
        return "translate(" + d.x + "," + d.y + ")";
      })
      .call(d3.behavior.drag()
        .origin(function (d) {
          return d;
        })
        .on("dragstart", function () {
          d3.event.sourceEvent.stopPropagation();
          this.parentNode.appendChild(this);
        })
        .on("drag", function dragmove(d) {
          d3.select(this).attr("transform",
            "translate(" + (
              d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
            ) + "," + (
              d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
            ) + ")");
          sankey.relayout();
          link.attr("d", sankey.link());
          dlink.attr("d", sankey.link());
        })
      );

    node.append("rect")
      .attr("height", function (d) {
        return d.dy;
      })
      .attr("width", sankey.nodeWidth())
      .style("fill", function (d) {
        d.color = color(d.name.replace(/ .*/, ""));
        return d.color;
      })
      .style("stroke", function (d) {
        return d3.rgb(d.color).darker(2);
      });

    node.append("text")
      .attr("x", -6)
      .attr("y", function (d) {
        return d.dy / 2;
      })
      .attr("dy", ".35em")
      .attr("text-anchor", "end")
      .attr("transform", null)
      .text(function (d) {
        return d.disp;
      })
      .filter(function (d) {
        return d.x < width / 2;
      })
      .attr("x", 6 + sankey.nodeWidth())
      .attr("text-anchor", "start");

    return node;
  }

  function drawLink(data, opt) {
    var link = graph.insert("g", ":first-child")
      .attr('id', opt || 'normal')
      .selectAll(".link")
      .data(data)
      .enter().append("path")
      .attr("class", "link" + (" " + opt || ""))
      .attr("d", sankey.link())
      .style("stroke-width", function (d) {
        return Math.max(1, d.dy);
      })
      .sort(function (a, b) {
        return b.dy - a.dy;
      });
    return link;
  }

  var formatNumber = d3.format(",.2f");
  var color = d3.scale.category20();

  var margin = {
      top: 10,
      right: 10,
      bottom: 10,
      left: 10,
    },
    width = options.width - margin.left - margin.right - 15,
    height = options.height - margin.top - margin.bottom - 15;

  var graph = canvas
    .append('svg')
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
    .append("g")
      .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

  var sankey = d3.sankey()
    .nodeWidth(15)
    .nodePadding(10)
    .size([width, height]);

  sankey.nodes(inputdata.nodes)
    .flows(inputdata.flows)
    .layout(32);

  var dlink;
  var node = drawNode(sankey.nodes());
  var link = drawLink(sankey.links());
  node
    .on("mouseover", funcMouseover)
    .on("mouseout", funcMouseout)
    .on('mousemove', funcMousemove);

  link
    .on("mouseover", funcMouseover)
    .on("mouseout", funcMouseout)
    .on('mousemove', funcMousemove);

  function funcMouseover(d) {
    sankey.dflows(d.flows);
    dlink = drawLink(sankey.dlinks(), 'highlight');
    updateTooltip(d);
    canvas.select('#tooltip-container')
      .style('display', 'block');
  }
  function funcMouseout(d) {
    graph.selectAll("g#highlight").remove();
    canvas.select('#tooltip-container')
      .style('display', 'none');
  }
  function funcMousemove(d) {
    canvas.select('#tooltip-container')
      .style('top', d3.event.pageY + 'px')
      .style('left', d3.event.pageX + 'px');
  }

  ///////////////////////
  //// Tooltips

  sankey.nodes().forEach(function(n){
    n.tooltip = {
      name: n.disp,
      value: formatNumber(n.value),
      head: true,
    };
  });
  sankey.links().forEach(function(l){
    l.tooltip = {
      name: l.source.disp + " → " + l.target.disp,
      value: formatNumber(l.value),
      head: true,
    };
  });
  sankey.flows().forEach(function(f){
    var name = '';
    f.thru.forEach(function (n, ind) {
      if (ind !== 0) name += ' → ';
      name += n.disp || n;
    });
    f.tooltip = {
      name: name,
      value: formatNumber(f.value),
    };
  });

  var tooltips = [];
  var tbody = canvas
    .append('div')
      .attr('id', 'tooltip-container')
    .append('table')
      .attr('class', 'tooltip')
    .append('tbody');

  // var tooltipRow = tbody.selectAll('tr')
  //     .data(tooltips);

  //@input d: data, could be node or link
  function updateTooltip(d){
    tooltips = [d.tooltip];
    d.flows.forEach(function(f){
      tooltips.push(f.tooltip);
    });

    tbody.selectAll('*').remove();
    tooltips.forEach(function(tip){
      var tr = tbody.append('tr');
      tr.append('td')
        .attr('class', 'name')
        .classed('head', 'head' in tip)
        .text(tip.name);

      tr.append('td')
        .attr('class', 'value')
        .classed('head', 'head' in tip)
        .text(tip.value);
    });
  }
};

// function flowTooltips(text, d) {
//   var len = d.flows.map(function (f) {
//     return formatNumber(f.value).length;
//   });
//   var maxlen = Math.max.apply(null, len);
//   var fmt = d3.format('<' + maxlen + ',.2f');
//   d.flows.forEach(function (f) {
//     text += '\n' + fmt(f.value) + '\t';
//     f.thru.forEach(function (n, ind) {
//       if (ind !== 0) text += ' → ';
//       text += n.disp || n;
//     });
//   });
//   return text;
// }
<|endoftext|>


<commit_before>;(function (global) {
  'use strict'

  var GOVUK = global.GOVUK || {}

  // For usage and initialisation see:
  // https://github.com/alphagov/govuk_frontend_toolkit/blob/master/docs/analytics.md#create-an-analytics-tracker

  var Analytics = function (config) {
    this.trackers = []
    if (typeof config.universalId !== 'undefined') {
      var universalId = config.universalId
      delete config.universalId
      this.trackers.push(new GOVUK.GoogleAnalyticsUniversalTracker(universalId, config))
    }
    if (typeof config.govukTrackerGifUrl !== 'undefined') {
      var govukTrackerGifUrl = config.govukTrackerGifUrl
      delete config.govukTrackerGifUrl
      this.trackers.push(new GOVUK.GOVUKTracker(govukTrackerGifUrl))
    }
  }

  Analytics.prototype.sendToTrackers = function (method, args) {
    for (var i = 0, l = this.trackers.length; i < l; i++) {
      var tracker = this.trackers[i]
      var fn = tracker[method]

      if (typeof fn === 'function') {
        fn.apply(tracker, args)
      }
    }
  }

  Analytics.load = function () {
    GOVUK.GoogleAnalyticsUniversalTracker.load()
    GOVUK.GOVUKTracker.load()
  }

  Analytics.prototype.trackPageview = function (path, title, options) {
    this.sendToTrackers('trackPageview', arguments)
  }

  /*
    https://developers.google.com/analytics/devguides/collection/analyticsjs/events
    options.label – Useful for categorizing events (eg nav buttons)
    options.value – Values must be non-negative. Useful to pass counts
    options.nonInteraction – Prevent event from impacting bounce rate
  */
  Analytics.prototype.trackEvent = function (category, action, options) {
    this.sendToTrackers('trackEvent', arguments)
  }

  Analytics.prototype.trackShare = function (network) {
    this.sendToTrackers('trackSocial', [network, 'share', global.location.pathname])
  }

  /*
    The custom dimension index must be configured within the
    Universal Analytics profile
   */
  Analytics.prototype.setDimension = function (index, value) {
    this.sendToTrackers('setDimension', arguments)
  }

  /*
   Add a beacon to track a page in another GA account on another domain.
   */
  Analytics.prototype.addLinkedTrackerDomain = function (trackerId, name, domain) {
    this.sendToTrackers('addLinkedTrackerDomain', arguments)
  }

  GOVUK.Analytics = Analytics

  global.GOVUK = GOVUK
})(window)
<commit_msg>Use the more sensibly named govukTrackerUrl config name<commit_after>;(function (global) {
  'use strict'

  var GOVUK = global.GOVUK || {}

  // For usage and initialisation see:
  // https://github.com/alphagov/govuk_frontend_toolkit/blob/master/docs/analytics.md#create-an-analytics-tracker

  var Analytics = function (config) {
    this.trackers = []
    if (typeof config.universalId !== 'undefined') {
      var universalId = config.universalId
      delete config.universalId
      this.trackers.push(new GOVUK.GoogleAnalyticsUniversalTracker(universalId, config))
    }
    if (typeof config.govukTrackerUrl !== 'undefined') {
      var govukTrackerUrl = config.govukTrackerUrl
      delete config.govukTrackerUrl
      this.trackers.push(new GOVUK.GOVUKTracker(govukTrackerUrl))
    }
  }

  Analytics.prototype.sendToTrackers = function (method, args) {
    for (var i = 0, l = this.trackers.length; i < l; i++) {
      var tracker = this.trackers[i]
      var fn = tracker[method]

      if (typeof fn === 'function') {
        fn.apply(tracker, args)
      }
    }
  }

  Analytics.load = function () {
    GOVUK.GoogleAnalyticsUniversalTracker.load()
    GOVUK.GOVUKTracker.load()
  }

  Analytics.prototype.trackPageview = function (path, title, options) {
    this.sendToTrackers('trackPageview', arguments)
  }

  /*
    https://developers.google.com/analytics/devguides/collection/analyticsjs/events
    options.label – Useful for categorizing events (eg nav buttons)
    options.value – Values must be non-negative. Useful to pass counts
    options.nonInteraction – Prevent event from impacting bounce rate
  */
  Analytics.prototype.trackEvent = function (category, action, options) {
    this.sendToTrackers('trackEvent', arguments)
  }

  Analytics.prototype.trackShare = function (network) {
    this.sendToTrackers('trackSocial', [network, 'share', global.location.pathname])
  }

  /*
    The custom dimension index must be configured within the
    Universal Analytics profile
   */
  Analytics.prototype.setDimension = function (index, value) {
    this.sendToTrackers('setDimension', arguments)
  }

  /*
   Add a beacon to track a page in another GA account on another domain.
   */
  Analytics.prototype.addLinkedTrackerDomain = function (trackerId, name, domain) {
    this.sendToTrackers('addLinkedTrackerDomain', arguments)
  }

  GOVUK.Analytics = Analytics

  global.GOVUK = GOVUK
})(window)
<|endoftext|>


<commit_before>//coding start 6/21
function RenderingEngine(dontRender, defaultRendererID){
  this.dontRender = dontRender; //AB for example doesn't want you to render
  this.defaultRendererID = defaultRendererID;
  this.renderers = [null, new HomestuckRenderer(this), new EggRenderer(this)]; //if they try to render with "null", use defaultRendererID index instead.

  //need to be kept as high up as possible so that rest of sim can access these convinience methods
  this.hexToRgbA = function(hex){
    var c;
    if(/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
        c= hex.substring(1).split('');
        if(c.length== 3){
            c= [c[0], c[0], c[1], c[1], c[2], c[2]];
        }
        c= '0x'+c.join('');
        //return 'rgba('+[(c>>16)&255, (c>>8)&255, c&255].join(',')+',1)';
        return [(c>>16)&255, (c>>8)&255, c&255]
    }
    throw new Error('Bad Hex ' + hex);
  }

  //if speed becomes an issue, take in array of color pairs to swap out
  //rather than call this method once for each color
  //swaps one hex color with another.
  //wait no, would be same amount of things. just would have nested for loops instead of
  //multiple calls
  this.swapColors = function(canvas, color1, color2,opacity){ //for wings, opacity = 0.5
    if(checkSimMode() == true){
      return;
    }
    var oldc = hexToRgbA(color1);
    var newc= hexToRgbA(color2);
    // console.log("replacing: " + oldc  + " with " + newc);
    var ctx = canvas.getContext('2d');
    var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
    //4 byte color array
    for(var i = 0; i<img_data.data.length; i += 4){
      if(img_data.data[i] == oldc[0] && img_data.data[i+1] == oldc[1] &&img_data.data[i+2] == oldc[2]&&img_data.data[i+3] == 255){
        img_data.data[i] = newc[0];
        img_data.data[i+1] = newc[1];
        img_data.data[i+2] = newc[2];
        img_data.data[i+3] = Math.Round(opacity * 255);
      }
    }
    ctx.putImageData(img_data, 0, 0);

  }

  //how translucent are we talking, here?  number between 0 and 1, not a gradient like ghostSwap
  this.voidSwap=function(canvas,alphaPercent){
    if(this.dontRender == true){
      return;
    }
    // console.log("replacing: " + oldc  + " with " + newc);
    var ctx = canvas.getContext('2d');
    var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
    //4 byte color array
    for(var i = 0; i<img_data.data.length; i += 4){
      if(img_data.data[i+3] > 50){
        img_data.data[i+3] = Math.floor(img_data.data[i+3]*alphaPercent);  //keeps wings at relative transparency
      }
    }
    ctx.putImageData(img_data, 0, 0);
  }

  //work once again gives me inspiration for this sim. thanks, bob!!!
  //this looks idential to drained sghost swap....
  this.ghostSwap = function(canvas){
  	if(dontRender == true){
  		return;
  	}
  	var ctx = canvas.getContext('2d');
  	var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
  	var imageString = "ghostGradient.png";
      var img=document.getElementById(imageString);
      var width = img.width;
    	var height = img.height;
  	var print= true;
  	 var rainbow_canvas = getBufferCanvas(document.getElementById("rainbow_template"));
  	var rctx = rainbow_canvas.getContext('2d');
    	rctx.drawImage(img,0,0,width,height);
  	var img_data_rainbow =rctx.getImageData(0, 0,width, height);
  	//4 *Math.floor(i/(4000)) is because 1/(width*4) get me the row number (*4 'cause there are 4 elements per pixel). then, when i have the row number, *4 again because first row is 0,1,2,3 and second is 4,5,6,7 and third is 8,9,10,11
  	for(var i = 0; i<img_data.data.length; i += 4){
  		if(img_data.data[i+3] >= 128){
  			img_data.data[i+3] = img_data_rainbow.data[4 *Math.floor(i/(4000))+3]*2 //only mimic transparency.

  		}
  	}
  	ctx.putImageData(img_data, 0, 0);
  	//ctx.putImageData(img_data_rainbow, 0, 0);
  }

  //work once again gives me inspiration for this sim. thanks, bob!!!
  this.rainbowSwap = function(canvas){
  	if(checkSimMode() == true){
  		return;
  	}
  	var ctx = canvas.getContext('2d');
  	var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
  	var imageString = "rainbow.png";
      var img=document.getElementById(imageString);
      var width = img.width;
    	var height = img.height;
  	var print= true;
  	 var rainbow_canvas = getBufferCanvas(document.getElementById("rainbow_template"));
  	var rctx = rainbow_canvas.getContext('2d');
    	rctx.drawImage(img,0,0,width,height);
  	var img_data_rainbow =rctx.getImageData(0, 0,width, height);
  	//4 *Math.floor(i/(4000)) is because 1/(width*4) get me the row number (*4 'cause there are 4 elements per pixel). then, when i have the row number, *4 again because first row is 0,1,2,3 and second is 4,5,6,7 and third is 8,9,10,11
  	for(var i = 0; i<img_data.data.length; i += 4){
  		if(img_data.data[i+3] >= 128){
  			img_data.data[i] =img_data_rainbow.data[4 *Math.floor(i/(4000))]
  			img_data.data[i+1] = img_data_rainbow.data[4 *Math.floor(i/(4000))+1]
  			img_data.data[i+2] =img_data_rainbow.data[4 *Math.floor(i/(4000))+2]
  			img_data.data[i+3] = getRandomIntNoSeed(100,255); //make it look speckled.

  		}
  	}
  	ctx.putImageData(img_data, 0, 0);
  	//ctx.putImageData(img_data_rainbow, 0, 0);
  }

  //sharpens the image so later pixel swapping doesn't work quite right.
  //https://www.html5rocks.com/en/tutorials/canvas/imagefilters/
  this.sbahjifier=function(canvas){
    var opaque = false;
    var ctx = canvas.getContext('2d');
    ctx.rotate(getRandomIntNoSeed(0,10)*Math.PI/180);
  	var pixels =ctx.getImageData(0, 0, canvas.width, canvas.height);
    //var weights = [  0, -1,  0, -1,  5, -1, 0, -1,  0 ];
    var weights = [  -1, -1,  -1, -1,  9, -1, -1, -1,  -1 ];
    //var weights = [  1, 1, 1, 1,  5, -1, -1, -1, -1 ];
    var side = Math.round(Math.sqrt(weights.length));
    var halfSide = Math.floor(side/2);
    var src = pixels.data;
    var sw = pixels.width;
    var sh = pixels.height;
    // pad output by the convolution matrix
    var w = sw;
    var h = sh;
    var output = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var dst = output.data;
    // go through the destination image pixels
    var alphaFac = opaque ? 1 : 0;
    for (var y=0; y<h; y++) {
      for (var x=0; x<w; x++) {
        var sy = y;
        var sx = x;
        var dstOff = (y*w+x)*4;
        // calculate the weighed sum of the source image pixels that
        // fall under the convolution matrix
        var r=0, g=0, b=0, a=0;
        for (var cy=0; cy<side; cy++) {
          for (var cx=0; cx<side; cx++) {
            var scy = sy + cy - halfSide;
            var scx = sx + cx - halfSide;
            if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
              var srcOff = (scy*sw+scx)*4;
              var wt = weights[cy*side+cx];
              r += src[srcOff] * wt;
              g += src[srcOff+1] * wt;
              b += src[srcOff+2] * wt;
              a += src[srcOff+3] * wt;
            }
          }
        }
        dst[dstOff] = r;
        dst[dstOff+1] = g;
        dst[dstOff+2] = b;
        dst[dstOff+3] = a + alphaFac*(255-a);
      }
    }
    //sligtly offset each time
    ctx.putImageData(output, getRandomIntNoSeed(0,10), getRandomIntNoSeed(0,10));
  }
}

/*******************************************************************************

          And now we start defining our renderers. If they get too beefy, move to new file.

********************************************************************************/

//calls either HomestuckHumanRenderer or HomestuckTrollRenderer depending on isTroll.
//only SBURBSim will call this function, others will Human or Troll directly, maybe.
function HomestuckRenderer(rh){
  this.rendererHelper = rh;
}

//homestuck has one of 3 sprites
function HomestuckHumanRenderer(rh){
  this.rendererHelper = rh;
}

function HomestuckTrollRenderer(rh){
  this.rendererHelper = rh;
}

function EggRenderer(rh){
  this.rendererHelper = rh;
}
<commit_msg>alright, all generic helper methods are out<commit_after>//coding start 6/21
function RenderingEngine(dontRender, defaultRendererID){
  this.dontRender = dontRender; //AB for example doesn't want you to render
  this.defaultRendererID = defaultRendererID;
  this.renderers = [null, new HomestuckRenderer(this), new EggRenderer(this)]; //if they try to render with "null", use defaultRendererID index instead.

  //need to be kept as high up as possible so that rest of sim can access these convinience methods
  this.hexToRgbA = function(hex){
    var c;
    if(/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)){
        c= hex.substring(1).split('');
        if(c.length== 3){
            c= [c[0], c[0], c[1], c[1], c[2], c[2]];
        }
        c= '0x'+c.join('');
        //return 'rgba('+[(c>>16)&255, (c>>8)&255, c&255].join(',')+',1)';
        return [(c>>16)&255, (c>>8)&255, c&255]
    }
    throw new Error('Bad Hex ' + hex);
  }

  //takes in url, not just x.png.
  //THIS IS A BACKUP FOR THE LOADING SCRIPT. IF ANYTHING ACTUALLY GETS ADDED TO IMAGE-STAGING I KNOW I FUCKED UP
  //this will cause a sprite to not render the first few times, but eventually render in l8r canvases.
  this.addImageTag = function(url){
    //console.log(url);
  	//only do it if image hasn't already been added.
  	if(document.getElementById(url) == null) {
  		var tag = '<img id ="' + url + '" src = "' + url + '" style="display:none">';
  		$("#image_staging").append(tag);
  	}

  }

  this.drawSolidBG=function(canvas, color){
    var ctx = canvas.getContext("2d");
  	ctx.fillStyle = color;
  	ctx.fillRect(0, 0, canvas.width, canvas.height);
  }

  this.drawBG = function(canvas, color1, color2){
  	//var c = document.getElementById(canvasId);
  	var ctx = canvas.getContext("2d");

  	var grd = ctx.createLinearGradient(0, 0, 170, 0);
  	grd.addColorStop(0, color1);
  	grd.addColorStop(1, color2);

  	ctx.fillStyle = grd;
  	ctx.fillRect(0, 0, canvas.width, canvas.height);
  }

  this.drawBGRadialWithWidth = function(canvas, startX, endX, width, color1, color2){
  	//var c = document.getElementById(canvasId);
  	var ctx = canvas.getContext("2d");

  	var grd = ctx.createRadialGradient(width/2,canvas.height/2,5,width,canvas.height,width);
  	grd.addColorStop(0, color1);
  	grd.addColorStop(1, color2);

  	ctx.fillStyle = grd;
  	ctx.fillRect(startX, 0, endX, canvas.height);
  }

  this.fillTextMultiLine = function(canvas, text1, text2, color2, x, y) {
  	var ctx = canvas.getContext("2d");
  	var lineHeight = ctx.measureText("M").width * 1.2;
      var lines = text1.split("\n");
   	for (var i = 0; i < lines.length; ++i) {
     		ctx.fillText(lines[i], x, y);
    		y += lineHeight;
    	}
  	//word wrap these
  	ctx.fillStyle = color2
   	this.wrap_text(ctx, text2, x, y, lineHeight, 3*canvas.width/4, "left");
  	ctx.fillStyle = "#000000"
  }

  //http://stackoverflow.com/questions/5026961/html5-canvas-ctx-filltext-wont-do-line-breaks
  this.wrap_text = function(ctx, text, x, y, lineHeight, maxWidth, textAlign) {
    if(!textAlign) textAlign = 'center'
    ctx.textAlign = textAlign
    var words = text.split(' ')
    var lines = []
    var sliceFrom = 0
    for(var i = 0; i < words.length; i++) {
      var chunk = words.slice(sliceFrom, i).join(' ')
      var last = i === words.length - 1
      var bigger = ctx.measureText(chunk).width > maxWidth
      if(bigger) {
        lines.push(words.slice(sliceFrom, i).join(' '))
        sliceFrom = i
      }
      if(last) {
        lines.push(words.slice(sliceFrom, words.length).join(' '))
        sliceFrom = i
      }
    }
    var offsetY = 0
    var offsetX = 0
    if(textAlign === 'center') offsetX = maxWidth / 2
    for(var i = 0; i < lines.length; i++) {
      ctx.fillText(lines[i], x + offsetX, y + offsetY)
      offsetY = offsetY + lineHeight
    }
    //need to return how many lines i created so that whatever called me knows where to put ITS next line.
    return lines.length;
  }

  this.getBufferCanvas = function(canvas){
  	var tmp_canvas = document.createElement('canvas');
  	tmp_canvas.height = canvas.height;
  	tmp_canvas.width = canvas.width;
  	return tmp_canvas;
  }

  this.copyTmpCanvasToRealCanvasAtPos = function(canvas, tmp_canvas, x, y){
    var ctx = canvas.getContext('2d');
  	ctx.drawImage(tmp_canvas, x, y);
  }

  this.drawWhateverTurnways = function(){
    var ctx = canvas.getContext('2d');
    if(turnWays){
      ctx.translate(canvas.width, 0);
      ctx.scale(-1, 1);
    }
    this.drawWhatever(canvas,imageString);
  }

  //have i really been too lazy to make this until now
   this.drawWhatever=function(canvas, imageString){
    if(checkSimMode() == true){
      return;
    }
  	var ctx = canvas.getContext('2d');
    ctx.imageSmoothingEnabled = false;  //should get rid of orange halo in certain browsers.
  	addImageTag(imageString)
  	var img=document.getElementById(imageString);
  	var width = img.width;
  	var height = img.height;
  	ctx.drawImage(img,0,0,width,height);
  }

  //if speed becomes an issue, take in array of color pairs to swap out
  //rather than call this method once for each color
  //swaps one hex color with another.
  //wait no, would be same amount of things. just would have nested for loops instead of
  //multiple calls
  this.swapColors = function(canvas, color1, color2,opacity){ //for wings, opacity = 0.5
    if(checkSimMode() == true){
      return;
    }
    var oldc = hexToRgbA(color1);
    var newc= hexToRgbA(color2);
    // console.log("replacing: " + oldc  + " with " + newc);
    var ctx = canvas.getContext('2d');
    var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
    //4 byte color array
    for(var i = 0; i<img_data.data.length; i += 4){
      if(img_data.data[i] == oldc[0] && img_data.data[i+1] == oldc[1] &&img_data.data[i+2] == oldc[2]&&img_data.data[i+3] == 255){
        img_data.data[i] = newc[0];
        img_data.data[i+1] = newc[1];
        img_data.data[i+2] = newc[2];
        img_data.data[i+3] = Math.Round(opacity * 255);
      }
    }
    ctx.putImageData(img_data, 0, 0);

  }

  //how translucent are we talking, here?  number between 0 and 1, not a gradient like ghostSwap
  this.voidSwap=function(canvas,alphaPercent){
    if(this.dontRender == true){
      return;
    }
    // console.log("replacing: " + oldc  + " with " + newc);
    var ctx = canvas.getContext('2d');
    var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
    //4 byte color array
    for(var i = 0; i<img_data.data.length; i += 4){
      if(img_data.data[i+3] > 50){
        img_data.data[i+3] = Math.floor(img_data.data[i+3]*alphaPercent);  //keeps wings at relative transparency
      }
    }
    ctx.putImageData(img_data, 0, 0);
  }

  //work once again gives me inspiration for this sim. thanks, bob!!!
  //this looks idential to drained sghost swap....
  this.ghostSwap = function(canvas){
  	if(dontRender == true){
  		return;
  	}
  	var ctx = canvas.getContext('2d');
  	var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
  	var imageString = "ghostGradient.png";
      var img=document.getElementById(imageString);
      var width = img.width;
    	var height = img.height;
  	var print= true;
  	 var rainbow_canvas = getBufferCanvas(document.getElementById("rainbow_template"));
  	var rctx = rainbow_canvas.getContext('2d');
    	rctx.drawImage(img,0,0,width,height);
  	var img_data_rainbow =rctx.getImageData(0, 0,width, height);
  	//4 *Math.floor(i/(4000)) is because 1/(width*4) get me the row number (*4 'cause there are 4 elements per pixel). then, when i have the row number, *4 again because first row is 0,1,2,3 and second is 4,5,6,7 and third is 8,9,10,11
  	for(var i = 0; i<img_data.data.length; i += 4){
  		if(img_data.data[i+3] >= 128){
  			img_data.data[i+3] = img_data_rainbow.data[4 *Math.floor(i/(4000))+3]*2 //only mimic transparency.

  		}
  	}
  	ctx.putImageData(img_data, 0, 0);
  	//ctx.putImageData(img_data_rainbow, 0, 0);
  }

  //work once again gives me inspiration for this sim. thanks, bob!!!
  this.rainbowSwap = function(canvas){
  	if(checkSimMode() == true){
  		return;
  	}
  	var ctx = canvas.getContext('2d');
  	var img_data =ctx.getImageData(0, 0, canvas.width, canvas.height);
  	var imageString = "rainbow.png";
      var img=document.getElementById(imageString);
      var width = img.width;
    	var height = img.height;
  	var print= true;
  	 var rainbow_canvas = getBufferCanvas(document.getElementById("rainbow_template"));
  	var rctx = rainbow_canvas.getContext('2d');
    	rctx.drawImage(img,0,0,width,height);
  	var img_data_rainbow =rctx.getImageData(0, 0,width, height);
  	//4 *Math.floor(i/(4000)) is because 1/(width*4) get me the row number (*4 'cause there are 4 elements per pixel). then, when i have the row number, *4 again because first row is 0,1,2,3 and second is 4,5,6,7 and third is 8,9,10,11
  	for(var i = 0; i<img_data.data.length; i += 4){
  		if(img_data.data[i+3] >= 128){
  			img_data.data[i] =img_data_rainbow.data[4 *Math.floor(i/(4000))]
  			img_data.data[i+1] = img_data_rainbow.data[4 *Math.floor(i/(4000))+1]
  			img_data.data[i+2] =img_data_rainbow.data[4 *Math.floor(i/(4000))+2]
  			img_data.data[i+3] = getRandomIntNoSeed(100,255); //make it look speckled.

  		}
  	}
  	ctx.putImageData(img_data, 0, 0);
  	//ctx.putImageData(img_data_rainbow, 0, 0);
  }

  //sharpens the image so later pixel swapping doesn't work quite right.
  //https://www.html5rocks.com/en/tutorials/canvas/imagefilters/
  this.sbahjifier=function(canvas){
    var opaque = false;
    var ctx = canvas.getContext('2d');
    ctx.rotate(getRandomIntNoSeed(0,10)*Math.PI/180);
  	var pixels =ctx.getImageData(0, 0, canvas.width, canvas.height);
    //var weights = [  0, -1,  0, -1,  5, -1, 0, -1,  0 ];
    var weights = [  -1, -1,  -1, -1,  9, -1, -1, -1,  -1 ];
    //var weights = [  1, 1, 1, 1,  5, -1, -1, -1, -1 ];
    var side = Math.round(Math.sqrt(weights.length));
    var halfSide = Math.floor(side/2);
    var src = pixels.data;
    var sw = pixels.width;
    var sh = pixels.height;
    // pad output by the convolution matrix
    var w = sw;
    var h = sh;
    var output = ctx.getImageData(0, 0, canvas.width, canvas.height);
    var dst = output.data;
    // go through the destination image pixels
    var alphaFac = opaque ? 1 : 0;
    for (var y=0; y<h; y++) {
      for (var x=0; x<w; x++) {
        var sy = y;
        var sx = x;
        var dstOff = (y*w+x)*4;
        // calculate the weighed sum of the source image pixels that
        // fall under the convolution matrix
        var r=0, g=0, b=0, a=0;
        for (var cy=0; cy<side; cy++) {
          for (var cx=0; cx<side; cx++) {
            var scy = sy + cy - halfSide;
            var scx = sx + cx - halfSide;
            if (scy >= 0 && scy < sh && scx >= 0 && scx < sw) {
              var srcOff = (scy*sw+scx)*4;
              var wt = weights[cy*side+cx];
              r += src[srcOff] * wt;
              g += src[srcOff+1] * wt;
              b += src[srcOff+2] * wt;
              a += src[srcOff+3] * wt;
            }
          }
        }
        dst[dstOff] = r;
        dst[dstOff+1] = g;
        dst[dstOff+2] = b;
        dst[dstOff+3] = a + alphaFac*(255-a);
      }
    }
    //sligtly offset each time
    ctx.putImageData(output, getRandomIntNoSeed(0,10), getRandomIntNoSeed(0,10));
  }
}

/*******************************************************************************

          And now we start defining our renderers. If they get too beefy, move to new file.

********************************************************************************/

//calls either HomestuckHumanRenderer or HomestuckTrollRenderer depending on isTroll.
//only SBURBSim will call this function, others will Human or Troll directly, maybe.
function HomestuckRenderer(rh){
  this.rendererHelper = rh;
}

//homestuck has one of 3 sprites
function HomestuckHumanRenderer(rh){
  this.rendererHelper = rh;
}

function HomestuckTrollRenderer(rh){
  this.rendererHelper = rh;
}

function EggRenderer(rh){
  this.rendererHelper = rh;
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import _ from 'lodash';
import FaMapMarker from 'react-icons/lib/fa/map-marker';

import styles from './EventLocationFilter.sass';

// Value in meters (what server expects)
const distanceRange = [
  { value: 8047, label: '5 miles' },
  { value: 16093, label: '10 miles' },
  { value: 40234, label: '25 miles' },
  { value: 80468, label: '50 miles' },
  { value: 160934, label: '100 miles' },
  { value: 804672, label: '500 miles' }
];
const defaultRangeIndex = 3;

function renderDistanceOptions() {
  return distanceRange.map(distance =>
    <option key={distance.value} value={distance.value}>{distance.label}</option>
  );
}

class EventLocationFilter extends Component {
  constructor(props) {
    super(props);

    // If URL location params, set them as defaults
    const { location: initialLocation, range: initialRange } = this.props;

    // match URL value up with dropdown option object
    const initialRangeOption = _.find(distanceRange, { value: initialRange });

    this.state = {
      menuOpen: false,
      location: initialLocation || '',
      range: initialRangeOption || distanceRange[defaultRangeIndex],
      locationErrorMsg: null
    };

    this.state.activeFilterMsg = initialLocation ? this.getActiveMessage(this.state.location, this.state.range) : '';

    this._handleDocumentClick = this.handleDocumentClick.bind(this);
    this._onLocationBlur = this.onLocationBlur.bind(this);
    this._handleRangeChange = this.handleRangeChange.bind(this);
    this._handleOnChangeLocation = this.handleOnChangeLocation.bind(this);
  }

  componentDidMount() {
    window.addEventListener('click', this._handleDocumentClick);
  }

  componentWillReceiveProps(nextProps) {
    // Set display based off of what current location and range props are
    if (nextProps.location && (nextProps.location !== this.props.location || nextProps.range !== this.props.range)) {  // eslint-disable-line max-len
      this.setState({
        activeFilterMsg: this.getActiveMessage(nextProps.location, this.state.range)
      });
    }

    // In case where location is determined by geoLocation API or by query string, set it the first time here
    if (!this.state.location && nextProps.location) {
      this.setState({ location: nextProps.location });
    }
  }

  componentWillUnmount() {
    window.removeEventListener('click', this._handleDocumentClick);
  }

  onLocationBlur() {
    this.validateLocation(this.state.location);
  }

  getActiveMessage(location, range) {
    return (

Location

{location}

Distance

{range.label}

    );
  }

  validateLocation(location) {
    const zipcodeRegex = /^\b\d{5}\b$/;

    if (zipcodeRegex.test(location)) {
      this.setState({ locationErrorMsg: null });
    } else {
      this.setState({ locationErrorMsg: 'You must enter a 5-digit zipcode' });
    }
  }

  // Close menu if clicking on the document (outside of the menu)
  handleDocumentClick() {
    this.setState({ menuOpen: false });
  }

  toggleMenu(e) {
    e.stopPropagation();
    this.setState({ menuOpen: !this.state.menuOpen });
  }

  handleRangeChange(e) {
    const range = distanceRange.find((set) => set.value === parseInt(e.target.value, 10));
    this.setState({ range });
  }

  clearForm() {
    this.setState({
      location: '',
      range: distanceRange[defaultRangeIndex],
      activeFilterMsg: false,
      menuOpen: false,
      locationErrorMsg: null
    });

    // For service request to update
    this.props.updateFilters({ location: null, range: distanceRange[defaultRangeIndex].value });
    this.props.disableGeoLocation();
  }

  handleOnChangeLocation(e) {
    // If user has entered exactly 5 digits, check to see if they're valid
    // this avoids user having to trigger input blur to have field validated
    const location = e.target.value;

    this.setState({ location }, () => {
      if (location.length === 5) {
        this.validateLocation(location);
      }
    });
  }

  submitForm(e) {
    if (e) e.preventDefault();
    const { location, range } = this.state;
    this.props.updateFilters({ location, range: range.value });  // For service request to use
    const activeMessage = this.getActiveMessage(location, range);
    this.props.disableGeoLocation();
    this.setState({ menuOpen: false, activeMessage });  // close menu
  }

  render() {
    const { menuOpen, location, range, locationErrorMsg, activeFilterMsg } = this.state;
    const invalidForm = !!locationErrorMsg || location === '';

    /* eslint-disable jsx-a11y/no-static-element-interactions */

    return (

 e.stopPropagation()}>
        <button
          type="button"
          className={styles.toolbarBtn}
          onClick={this.toggleMenu.bind(this)}
        >
          {
            activeFilterMsg || FILTER BY LOCATION
          }
          <FaMapMarker size={25} />
        </button>

        <form
          style={{ visibility: menuOpen ? 'visible' : 'hidden' }}
          className={styles.popoverWrapper}
        >
          <fieldset>

              ENTER ZIPCODE
              <input
                type="text"
                name="location"
                value={location}
                onChange={this._handleOnChangeLocation}
                onBlur={this._onLocationBlur}
                required
                placeholder="Enter zipcode"
              />

{locationErrorMsg}

          </fieldset>

            SET RANGE
            <select
              name="range"
              value={range.value}
              onChange={this._handleRangeChange}
            >
              {renderDistanceOptions()}
            </select>

            <input
              type="button"
              value="CLEAR"
              className={styles.clearBtn}
              onClick={this.clearForm.bind(this)}
            />
            <input
              type="submit"
              value="SET LOCATION"
              className={styles.submitBtn}
              disabled={invalidForm}
              onClick={this.submitForm.bind(this)}
            />

        </form>

    );

    /* eslint-enable jsx-a11y/no-static-element-interactions */
  }
}

EventLocationFilter.propTypes = {
  disableGeoLocation: PropTypes.func,
  updateFilters: PropTypes.func.isRequired,
  location: PropTypes.string,
  range: PropTypes.number
};

export default EventLocationFilter;
export { distanceRange, defaultRangeIndex };
<commit_msg>change default distance<commit_after>import React, { Component, PropTypes } from 'react';
import _ from 'lodash';
import FaMapMarker from 'react-icons/lib/fa/map-marker';

import styles from './EventLocationFilter.sass';

// Value in meters (what server expects)
const distanceRange = [
  { value: 8047, label: '5 miles' },
  { value: 16093, label: '10 miles' },
  { value: 40234, label: '25 miles' },
  { value: 80468, label: '50 miles' },
  { value: 160934, label: '100 miles' },
  { value: 804672, label: '500 miles' }
];
const defaultRangeIndex = 4;

function renderDistanceOptions() {
  return distanceRange.map(distance =>
    <option key={distance.value} value={distance.value}>{distance.label}</option>
  );
}

class EventLocationFilter extends Component {
  constructor(props) {
    super(props);

    // If URL location params, set them as defaults
    const { location: initialLocation, range: initialRange } = this.props;

    // match URL value up with dropdown option object
    const initialRangeOption = _.find(distanceRange, { value: initialRange });

    this.state = {
      menuOpen: false,
      location: initialLocation || '',
      range: initialRangeOption || distanceRange[defaultRangeIndex],
      locationErrorMsg: null
    };

    this.state.activeFilterMsg = initialLocation ? this.getActiveMessage(this.state.location, this.state.range) : '';

    this._handleDocumentClick = this.handleDocumentClick.bind(this);
    this._onLocationBlur = this.onLocationBlur.bind(this);
    this._handleRangeChange = this.handleRangeChange.bind(this);
    this._handleOnChangeLocation = this.handleOnChangeLocation.bind(this);
  }

  componentDidMount() {
    window.addEventListener('click', this._handleDocumentClick);
  }

  componentWillReceiveProps(nextProps) {
    // Set display based off of what current location and range props are
    if (nextProps.location && (nextProps.location !== this.props.location || nextProps.range !== this.props.range)) {  // eslint-disable-line max-len
      this.setState({
        activeFilterMsg: this.getActiveMessage(nextProps.location, this.state.range)
      });
    }

    // In case where location is determined by geoLocation API or by query string, set it the first time here
    if (!this.state.location && nextProps.location) {
      this.setState({ location: nextProps.location });
    }
  }

  componentWillUnmount() {
    window.removeEventListener('click', this._handleDocumentClick);
  }

  onLocationBlur() {
    this.validateLocation(this.state.location);
  }

  getActiveMessage(location, range) {
    return (

Location

{location}

Distance

{range.label}

    );
  }

  validateLocation(location) {
    const zipcodeRegex = /^\b\d{5}\b$/;

    if (zipcodeRegex.test(location)) {
      this.setState({ locationErrorMsg: null });
    } else {
      this.setState({ locationErrorMsg: 'You must enter a 5-digit zipcode' });
    }
  }

  // Close menu if clicking on the document (outside of the menu)
  handleDocumentClick() {
    this.setState({ menuOpen: false });
  }

  toggleMenu(e) {
    e.stopPropagation();
    this.setState({ menuOpen: !this.state.menuOpen });
  }

  handleRangeChange(e) {
    const range = distanceRange.find((set) => set.value === parseInt(e.target.value, 10));
    this.setState({ range });
  }

  clearForm() {
    this.setState({
      location: '',
      range: distanceRange[defaultRangeIndex],
      activeFilterMsg: false,
      menuOpen: false,
      locationErrorMsg: null
    });

    // For service request to update
    this.props.updateFilters({ location: null, range: distanceRange[defaultRangeIndex].value });
    this.props.disableGeoLocation();
  }

  handleOnChangeLocation(e) {
    // If user has entered exactly 5 digits, check to see if they're valid
    // this avoids user having to trigger input blur to have field validated
    const location = e.target.value;

    this.setState({ location }, () => {
      if (location.length === 5) {
        this.validateLocation(location);
      }
    });
  }

  submitForm(e) {
    if (e) e.preventDefault();
    const { location, range } = this.state;
    this.props.updateFilters({ location, range: range.value });  // For service request to use
    const activeMessage = this.getActiveMessage(location, range);
    this.props.disableGeoLocation();
    this.setState({ menuOpen: false, activeMessage });  // close menu
  }

  render() {
    const { menuOpen, location, range, locationErrorMsg, activeFilterMsg } = this.state;
    const invalidForm = !!locationErrorMsg || location === '';

    /* eslint-disable jsx-a11y/no-static-element-interactions */

    return (

 e.stopPropagation()}>
        <button
          type="button"
          className={styles.toolbarBtn}
          onClick={this.toggleMenu.bind(this)}
        >
          {
            activeFilterMsg || FILTER BY LOCATION
          }
          <FaMapMarker size={25} />
        </button>

        <form
          style={{ visibility: menuOpen ? 'visible' : 'hidden' }}
          className={styles.popoverWrapper}
        >
          <fieldset>

              ENTER ZIPCODE
              <input
                type="text"
                name="location"
                value={location}
                onChange={this._handleOnChangeLocation}
                onBlur={this._onLocationBlur}
                required
                placeholder="Enter zipcode"
              />

{locationErrorMsg}

          </fieldset>

            SET RANGE
            <select
              name="range"
              value={range.value}
              onChange={this._handleRangeChange}
            >
              {renderDistanceOptions()}
            </select>

            <input
              type="button"
              value="CLEAR"
              className={styles.clearBtn}
              onClick={this.clearForm.bind(this)}
            />
            <input
              type="submit"
              value="SET LOCATION"
              className={styles.submitBtn}
              disabled={invalidForm}
              onClick={this.submitForm.bind(this)}
            />

        </form>

    );

    /* eslint-enable jsx-a11y/no-static-element-interactions */
  }
}

EventLocationFilter.propTypes = {
  disableGeoLocation: PropTypes.func,
  updateFilters: PropTypes.func.isRequired,
  location: PropTypes.string,
  range: PropTypes.number
};

export default EventLocationFilter;
export { distanceRange, defaultRangeIndex };
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import I18n from 'i18n-js';
import Link from '../link';
import tagComponent from '../../utils/helpers/tags';
import { StyledActionToolbar, StyledActionToolbarTotal, StyledActionToolbarActions } from './action-toolbar.style';

/**
 * A ActionToolbar widget.
 *
 * == How to use a ActionToolbar in a component:
 *
 * In your file
 *
 *   import ActionToolbar from 'carbon-react/lib/components/action-toolbar';
 *
 * To render an ActionToolbar:
 *
 *   let actions = {
 *     subscription: {
 *       text: "Add Subscriptions",
 *       icon: "basket",
 *       onClick: onClickHandler(event, selected) => {}
 *     },
 *     delete: {
 *       text: "Delete",
 *       icon: "bin",
 *       onClick: onClickHandler(event, selected) => {}
 *     }
 *   };
 *
 *   <ActionToolbar total={ count } actions={ actions } />
 *
 *  Additional props for Link or Icon can be passed in the action object.
 *
 */
class ActionToolbar extends React.Component {
  // TODO This component needs to be freestanding - we need to provide an api that allows it be used independently.
  // https://github.com/Sage/carbon/issues/1070

  static propTypes = {
    /** The actions to display in the toolbar */
    actions: PropTypes.object.isRequired,
    /** A custom class name for the component. */
    className: PropTypes.string,
    /** A function to return child components for the action toolbar. */
    children: PropTypes.func
  }

  static contextTypes = {
    attachActionToolbar: PropTypes.func, // tracks the action toolbar component
    detachActionToolbar: PropTypes.func // tracks the action toolbar component
  }

  constructor(...args) {
    super(...args);

    this.actions = this.actions.bind(this);
    this.isActive = this.isActive.bind(this);
    this.buildAction = this.buildAction.bind(this);
  }

  state = {
    total: 0,
    selected: {}
  };

  componentWillMount() {
    if (this.context.attachActionToolbar) {
      this.context.attachActionToolbar(this);
    }
  }

  componentWillUnmount() {
    if (this.context.detachActionToolbar) {
      this.context.detachActionToolbar(this);
    }
  }

  actions() {
    const actions = [];

    for (const key in this.props.actions) {
      const action = this.props.actions[key];
      actions.push(this.buildAction(action, key));
    }

    return actions;
  }

  handleOnClick = (onClick, selected) => {
    if (!onClick) { return null; }
    return event => onClick(selected, event);
  };

  isActive() {
    return this.state.total > 0;
  }

  linkClasses(className) {
    return classNames(className);
  }

  buildAction(action, index) {
    const {
      onClick, className, text, ...props
    } = action;

    return (
      <Link
        className={ this.linkClasses(className) }
        data-element='action'
        disabled={ !this.isActive() }
        key={ index }
        onClick={ this.handleOnClick(onClick, this.state.selected) }
        { ...props }
      >
        {text}
      </Link>
    );
  }

  propsForChildren = () => {
    return {
      disabled: !this.isActive(),
      selected: this.state.selected,
      total: this.state.total
    };
  };

  render() {
    return (

      </StyledActionToolbar>
    );
  }
}

ActionToolbar.propTypes = {
  /** The actions to display in the toolbar */
  actions: PropTypes.object.isRequired,
  /** A custom class name for the component. */
  className: PropTypes.string,
  /** A function to return child components for the action toolbar. */
  children: PropTypes.func
};

export default ActionToolbar;
<commit_msg>fix props position<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import I18n from 'i18n-js';
import Link from '../link';
import tagComponent from '../../utils/helpers/tags';
import { StyledActionToolbar, StyledActionToolbarTotal, StyledActionToolbarActions } from './action-toolbar.style';

/**
 * A ActionToolbar widget.
 *
 * == How to use a ActionToolbar in a component:
 *
 * In your file
 *
 *   import ActionToolbar from 'carbon-react/lib/components/action-toolbar';
 *
 * To render an ActionToolbar:
 *
 *   let actions = {
 *     subscription: {
 *       text: "Add Subscriptions",
 *       icon: "basket",
 *       onClick: onClickHandler(event, selected) => {}
 *     },
 *     delete: {
 *       text: "Delete",
 *       icon: "bin",
 *       onClick: onClickHandler(event, selected) => {}
 *     }
 *   };
 *
 *   <ActionToolbar total={ count } actions={ actions } />
 *
 *  Additional props for Link or Icon can be passed in the action object.
 *
 */
class ActionToolbar extends React.Component {
  // TODO This component needs to be freestanding - we need to provide an api that allows it be used independently.
  // https://github.com/Sage/carbon/issues/1070

  static contextTypes = {
    attachActionToolbar: PropTypes.func, // tracks the action toolbar component
    detachActionToolbar: PropTypes.func // tracks the action toolbar component
  };

  constructor(...args) {
    super(...args);

    this.actions = this.actions.bind(this);
    this.isActive = this.isActive.bind(this);
    this.buildAction = this.buildAction.bind(this);
  }

  state = {
    total: 0,
    selected: {}
  };

  componentWillMount() {
    if (this.context.attachActionToolbar) {
      this.context.attachActionToolbar(this);
    }
  }

  componentWillUnmount() {
    if (this.context.detachActionToolbar) {
      this.context.detachActionToolbar(this);
    }
  }

  actions() {
    const actions = [];

    for (const key in this.props.actions) {
      const action = this.props.actions[key];
      actions.push(this.buildAction(action, key));
    }

    return actions;
  }

  handleOnClick = (onClick, selected) => {
    if (!onClick) { return null; }
    return event => onClick(selected, event);
  };

  isActive() {
    return this.state.total > 0;
  }

  linkClasses(className) {
    return classNames(className);
  }

  buildAction(action, index) {
    const {
      onClick, className, text, ...props
    } = action;

    return (
      <Link
        className={ this.linkClasses(className) }
        data-element='action'
        disabled={ !this.isActive() }
        key={ index }
        onClick={ this.handleOnClick(onClick, this.state.selected) }
        { ...props }
      >
        {text}
      </Link>
    );
  }

  propsForChildren = () => {
    return {
      disabled: !this.isActive(),
      selected: this.state.selected,
      total: this.state.total
    };
  };

  render() {
    return (

      </StyledActionToolbar>
    );
  }
}

ActionToolbar.propTypes = {
  /** The actions to display in the toolbar */
  actions: PropTypes.object.isRequired,
  /** A custom class name for the component. */
  className: PropTypes.string,
  /** A function to return child components for the action toolbar. */
  children: PropTypes.func
};

export default ActionToolbar;
<|endoftext|>


<commit_before>'use strict';

////////////////////////////////////////////////////////////////////////////////
/// @brief JavaScript base module
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var internal = require("internal");

var fs = require("fs");

var mimetypes = require("org/arangodb/mimetypes").mimeTypes;

// -----------------------------------------------------------------------------
// --SECTION--                                                 module "arangodb"
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                                  public constants
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief errors
////////////////////////////////////////////////////////////////////////////////

Object.keys(internal.errors).forEach(function (key) {
  exports[key] = internal.errors[key].code;
});

exports.errors = internal.errors;

// -----------------------------------------------------------------------------
// --SECTION--                                                      public types
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief ArangoError
////////////////////////////////////////////////////////////////////////////////

exports.ArangoError = internal.ArangoError;

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief defines a module
////////////////////////////////////////////////////////////////////////////////

exports.defineModule = function (path, file) {
  var content;
  var m;
  var mc;

  content = fs.read(file);

  mc = internal.db._collection("_modules");

  if (mc === null) {
    mc = internal.db._create("_modules", { isSystem: true });
  }

  path = module.normalize(path);
  m = mc.firstExample({ path: path });

  if (m === null) {
    mc.save({ path: path, content: content });
  }
  else {
    mc.replace(m, { path: path, content: content });
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief guessContentType
////////////////////////////////////////////////////////////////////////////////

exports.guessContentType = function (filename, defaultValue) {
  var re = /\.([a-zA-Z0-9]+)$/;
  var match = re.exec(filename);

  if (match !== null) {
    var extension = match[1];

    if (mimetypes.hasOwnProperty(extension)) {
      var type = mimetypes[extension];

      if (type[1]) {
        // append charset
        return type[0] + "; charset=utf-8";
      }

      return type[0];
    }
    // fall-through intentional
  }

  // default mimetype
  if (defaultValue) {
    return defaultValue;
  }
  return "text/plain; charset=utf-8";
};

////////////////////////////////////////////////////////////////////////////////
/// @brief normalizeURL
///
/// If @FA{path} starts with "." or "..", then it is a relative path.
/// Otherwise it is an absolute path. Normalizing will remove `//`,
/// `/./`, `/../` from the url - expect in the beginning, where it keeps
/// `../` and or at most one `./`.
///
/// If @FA{path} is empty, the url `./` will be returned.
////////////////////////////////////////////////////////////////////////////////

exports.normalizeURL = function (path) {
  var i;
  var n;
  var p;
  var q;
  var r;
  var x;

  if (path === "") {
    return "./";
  }

  p = path.split('/');

  // relative path
  if (p[0] === "." || p[0] === "..") {
    r = p[0] + "/";
    p.shift();
    q = p;
  }

  // absolute path
  else if (p[0] === "") {
    r = "/";
    p.shift();
    q = p;
  }

  // assume that the path is relative
  else {
    r = "./";
    q = p;
  }

  // normalize path
  n = [];

  for (i = 0;  i < q.length;  ++i) {
    x = q[i];

    if (x === "..") {
      if (n.length === 0) {
        if (r === "../") {
          n.push(x);
        }
        else if (r === "./") {
          r = "../";
        }
        else {
          throw "cannot use '..' to escape top-level-directory";
        }
      }
      else if (n[n.length - 1] === "..") {
        n.push(x);
      }
      else {
        n.pop();
      }
    }
    else if (x !== "" && x !== ".") {
      n.push(x);
    }
  }

  return r + n.join('/');
};

////////////////////////////////////////////////////////////////////////////////
/// @brief inspect
////////////////////////////////////////////////////////////////////////////////

exports.inspect = internal.inspect;

////////////////////////////////////////////////////////////////////////////////
/// @brief output
///
/// In order to allow "capture" output to work, we cannot assigne the
/// function here.
////////////////////////////////////////////////////////////////////////////////

exports.output = function () {
  internal.output.apply(internal.output, arguments);
};

////////////////////////////////////////////////////////////////////////////////
/// @brief print
////////////////////////////////////////////////////////////////////////////////

exports.print = internal.print;

////////////////////////////////////////////////////////////////////////////////
/// @brief printf
////////////////////////////////////////////////////////////////////////////////

exports.printf = internal.printf;

////////////////////////////////////////////////////////////////////////////////
/// @brief sprintf
////////////////////////////////////////////////////////////////////////////////

exports.sprintf = internal.sprintf;

////////////////////////////////////////////////////////////////////////////////
/// @brief printObject
////////////////////////////////////////////////////////////////////////////////

exports.printObject = internal.printObject;

////////////////////////////////////////////////////////////////////////////////
/// @brief 2D ASCII table printing
////////////////////////////////////////////////////////////////////////////////

exports.printTable = function  (list, columns, options) {
  options = options || { };
  if (options.totalString === undefined) {
    options.totalString = "%s document(s)\n";
  }

  var pad = '...';
  var descriptions, matrix, col, what, j;

  if (columns === undefined) {
    what = list[0];
  }
  else if (Array.isArray(columns)) {
    what = { };

    columns.forEach(function (col) {
      what[col] = null;
    });
  }
  else {
    what = columns;
  }

  j = 0;
  descriptions = [ ];
  matrix = [ [ ] ];

  for (col in what) {
    if (what.hasOwnProperty(col)) {
      var fixedLength = null;

      if (columns && columns.hasOwnProperty(col) && columns[col] > 0) {
        fixedLength = columns[col] >= pad.length ? columns[col] : pad.length;
      }

      // header
      var name = col;

      // rename header?
      if (options.hasOwnProperty("rename")) {
        if (options.rename.hasOwnProperty(col)) {
          name = options.rename[col];
        }
      }

      descriptions.push({
        id: col,
        fixedLength: fixedLength,
        length: fixedLength || name.length
      });

      matrix[0][j++] = name;
    }
  }

  // determine values & max widths
  list.forEach(function (row, i) {
    matrix[i + 1] = [ ];
    descriptions.forEach(function (col) {

      if (row.hasOwnProperty(col.id)) {
        var value;
        if (options.prettyStrings && typeof row[col.id] === 'string') {
          value = row[col.id];
        }
        else {
          value = JSON.stringify(row[col.id]) || "";
        }

        matrix[i + 1].push(value);

        if (value.length > col.length && ! col.fixedLength) {
          col.length = Math.min(value.length, 100);
        }
      }
      else {
        // undefined
        matrix[i + 1].push('');
      }
    });
  });

  var divider = function () {
    var parts = [ ];
    descriptions.forEach(function (desc) {
      parts.push(exports.stringPadding('', desc.length, '-', 'r'));
    });

    if (options.framed) {
      return '+-' + parts.join('-+-') + '-+\n';
    }

    return parts.join('   ') + '\n';
  };

  var compose = function () {
    var result = '';

    if (options.framed) {
      result += divider();
    }
    matrix.forEach(function (row, i) {
      var parts = [ ];

      row.forEach(function (col, j) {

        var len = descriptions[j].length, value = row[j];
        if (value.length > len) {
          value = value.substr(0, len - pad.length) + pad;
        }
        parts.push(exports.stringPadding(value, len, ' ', 'r'));
      });

      if (options.framed) {
        result += '| ' + parts.join(' | ') + ' |\n';
      }
      else {
        result += parts.join('   ') + '\n';
      }

      if (i === 0) {
        result += divider();
      }
    });

    result += divider();

    if (! options.hideTotal) {
      result += internal.sprintf(options.totalString, String(list.length));
    }
    return result;
  };

  if (! Array.isArray(list)) {
    // not an array
    return;
  }

  if (list.length === 0) {
    exports.print(options.emptyString || "no document(s)");
  }
  else {
    exports.print(compose());
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief stringPadding
////////////////////////////////////////////////////////////////////////////////

exports.stringPadding = function (str, len, pad, dir) {
  // yes, this is more code than new Array(length).join(chr), but it makes jslint happy
  function fill (length, chr) {
    var result = '', i;
    for (i = 0; i < length; ++i) {
      result += chr;
    }
    return result;
  }

  if (typeof(len) === "undefined") {
    len = 0;
  }
  if (typeof(pad) === "undefined") {
    pad = ' ';
  }

  if (len + 1 >= str.length) {
    switch (dir || "r"){

      // LEFT
      case 'l':
        str = fill(len + 1 - str.length, pad) + str;
        break;

      // BOTH
      case 'b':
        var padlen = len - str.length;
        var right = Math.ceil(padlen / 2);
        var left = padlen - right;
        str = fill(left + 1, pad) + str + fill(right + 1, pad);
        break;

      default:
         str = str + fill(len + 1 - str.length, pad);
         break;
    }
  }

  return str;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief throws an error in case a download failed
////////////////////////////////////////////////////////////////////////////////

exports.throwDownloadError = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_APPLICATION_DOWNLOAD_FAILED.code,
    errorMessage: exports.errors.ERROR_APPLICATION_DOWNLOAD_FAILED.message + ': ' + String(msg)
  });
};

////////////////////////////////////////////////////////////////////////////////
/// @brief throws an error in case of missing file
////////////////////////////////////////////////////////////////////////////////

exports.throwFileNotFound = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_FILE_NOT_FOUND.code,
    errorMessage: exports.errors.ERROR_FILE_NOT_FOUND.message + ': ' + String(msg)
  });
};

////////////////////////////////////////////////////////////////////////////////
/// @brief throws an error in case of a bad parameter
////////////////////////////////////////////////////////////////////////////////

exports.throwBadParameter = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_BAD_PARAMETER.code,
    errorMessage: exports.errors.ERROR_BAD_PARAMETER.message + ': ' + String(msg)
  });
};

////////////////////////////////////////////////////////////////////////////////
/// @brief checks parameter, throws an error if missing
////////////////////////////////////////////////////////////////////////////////

exports.checkParameter = function (usage, descs, vars) {
  var i;

  for (i = 0;  i < descs.length;  ++i) {
    var desc = descs[i];

    if (typeof vars[i] === "undefined") {
      exports.throwBadParameter(desc[0] + " missing, usage: " + usage);
    }

    if (typeof vars[i] !== desc[1]) {
      exports.throwBadParameter(desc[0] + " should be a '" + desc[1] + "', "
                              + "not '" + (typeof vars[i]) + "'");
    }
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief generate info message for newer version(s) available
////////////////////////////////////////////////////////////////////////////////

exports.checkAvailableVersions = function (version) {
  var console = require("console");
  var log;

  if (require("org/arangodb").isServer) {
    log = console.info;
  }
  else {
    log = internal.print;
  }

  if (version === undefined) {
    version = internal.version;
  }

  if (version.match(/beta|alpha|preview|devel/) !== null) {
    log("You are using an alpha/beta/preview version ('" + version + "') of ArangoDB");
    return;
  }

  try {
    var u = "https://www.arangodb.com/repositories/versions.php?version=";
    var d = internal.download(u + version, "", {timeout: 300});
    var v = JSON.parse(d.body);

    if (v.hasOwnProperty("bugfix")) {
      log("Please note that a new bugfix version '" + v.bugfix.version + "' is available");
    }

    if (v.hasOwnProperty("minor")) {
      log("Please note that a new minor version '" + v.minor.version + "' is available");
    }

    if (v.hasOwnProperty("major")) {
      log("Please note that a new major version '" + v.major.version + "' is available");
    }
  }
  catch (err) {
    if (console && console.debug) {
      console.debug("cannot check for newer version: ", err.stack);
    }
  }
};

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
// End:
<commit_msg>added OS to version check to catch new packages<commit_after>'use strict';

////////////////////////////////////////////////////////////////////////////////
/// @brief JavaScript base module
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 triAGENS GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
///     http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
/// Copyright holder is triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Copyright 2012-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////

var internal = require("internal");

var fs = require("fs");

var mimetypes = require("org/arangodb/mimetypes").mimeTypes;

// -----------------------------------------------------------------------------
// --SECTION--                                                 module "arangodb"
// -----------------------------------------------------------------------------

// -----------------------------------------------------------------------------
// --SECTION--                                                  public constants
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief errors
////////////////////////////////////////////////////////////////////////////////

Object.keys(internal.errors).forEach(function (key) {
  exports[key] = internal.errors[key].code;
});

exports.errors = internal.errors;

// -----------------------------------------------------------------------------
// --SECTION--                                                      public types
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief ArangoError
////////////////////////////////////////////////////////////////////////////////

exports.ArangoError = internal.ArangoError;

// -----------------------------------------------------------------------------
// --SECTION--                                                  public functions
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////
/// @brief defines a module
////////////////////////////////////////////////////////////////////////////////

exports.defineModule = function (path, file) {
  var content;
  var m;
  var mc;

  content = fs.read(file);

  mc = internal.db._collection("_modules");

  if (mc === null) {
    mc = internal.db._create("_modules", { isSystem: true });
  }

  path = module.normalize(path);
  m = mc.firstExample({ path: path });

  if (m === null) {
    mc.save({ path: path, content: content });
  }
  else {
    mc.replace(m, { path: path, content: content });
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief guessContentType
////////////////////////////////////////////////////////////////////////////////

exports.guessContentType = function (filename, defaultValue) {
  var re = /\.([a-zA-Z0-9]+)$/;
  var match = re.exec(filename);

  if (match !== null) {
    var extension = match[1];

    if (mimetypes.hasOwnProperty(extension)) {
      var type = mimetypes[extension];

      if (type[1]) {
        // append charset
        return type[0] + "; charset=utf-8";
      }

      return type[0];
    }
    // fall-through intentional
  }

  // default mimetype
  if (defaultValue) {
    return defaultValue;
  }
  return "text/plain; charset=utf-8";
};

////////////////////////////////////////////////////////////////////////////////
/// @brief normalizeURL
///
/// If @FA{path} starts with "." or "..", then it is a relative path.
/// Otherwise it is an absolute path. Normalizing will remove `//`,
/// `/./`, `/../` from the url - expect in the beginning, where it keeps
/// `../` and or at most one `./`.
///
/// If @FA{path} is empty, the url `./` will be returned.
////////////////////////////////////////////////////////////////////////////////

exports.normalizeURL = function (path) {
  var i;
  var n;
  var p;
  var q;
  var r;
  var x;

  if (path === "") {
    return "./";
  }

  p = path.split('/');

  // relative path
  if (p[0] === "." || p[0] === "..") {
    r = p[0] + "/";
    p.shift();
    q = p;
  }

  // absolute path
  else if (p[0] === "") {
    r = "/";
    p.shift();
    q = p;
  }

  // assume that the path is relative
  else {
    r = "./";
    q = p;
  }

  // normalize path
  n = [];

  for (i = 0;  i < q.length;  ++i) {
    x = q[i];

    if (x === "..") {
      if (n.length === 0) {
        if (r === "../") {
          n.push(x);
        }
        else if (r === "./") {
          r = "../";
        }
        else {
          throw "cannot use '..' to escape top-level-directory";
        }
      }
      else if (n[n.length - 1] === "..") {
        n.push(x);
      }
      else {
        n.pop();
      }
    }
    else if (x !== "" && x !== ".") {
      n.push(x);
    }
  }

  return r + n.join('/');
};

////////////////////////////////////////////////////////////////////////////////
/// @brief inspect
////////////////////////////////////////////////////////////////////////////////

exports.inspect = internal.inspect;

////////////////////////////////////////////////////////////////////////////////
/// @brief output
///
/// In order to allow "capture" output to work, we cannot assigne the
/// function here.
////////////////////////////////////////////////////////////////////////////////

exports.output = function () {
  internal.output.apply(internal.output, arguments);
};

////////////////////////////////////////////////////////////////////////////////
/// @brief print
////////////////////////////////////////////////////////////////////////////////

exports.print = internal.print;

////////////////////////////////////////////////////////////////////////////////
/// @brief printf
////////////////////////////////////////////////////////////////////////////////

exports.printf = internal.printf;

////////////////////////////////////////////////////////////////////////////////
/// @brief sprintf
////////////////////////////////////////////////////////////////////////////////

exports.sprintf = internal.sprintf;

////////////////////////////////////////////////////////////////////////////////
/// @brief printObject
////////////////////////////////////////////////////////////////////////////////

exports.printObject = internal.printObject;

////////////////////////////////////////////////////////////////////////////////
/// @brief 2D ASCII table printing
////////////////////////////////////////////////////////////////////////////////

exports.printTable = function  (list, columns, options) {
  options = options || { };
  if (options.totalString === undefined) {
    options.totalString = "%s document(s)\n";
  }

  var pad = '...';
  var descriptions, matrix, col, what, j;

  if (columns === undefined) {
    what = list[0];
  }
  else if (Array.isArray(columns)) {
    what = { };

    columns.forEach(function (col) {
      what[col] = null;
    });
  }
  else {
    what = columns;
  }

  j = 0;
  descriptions = [ ];
  matrix = [ [ ] ];

  for (col in what) {
    if (what.hasOwnProperty(col)) {
      var fixedLength = null;

      if (columns && columns.hasOwnProperty(col) && columns[col] > 0) {
        fixedLength = columns[col] >= pad.length ? columns[col] : pad.length;
      }

      // header
      var name = col;

      // rename header?
      if (options.hasOwnProperty("rename")) {
        if (options.rename.hasOwnProperty(col)) {
          name = options.rename[col];
        }
      }

      descriptions.push({
        id: col,
        fixedLength: fixedLength,
        length: fixedLength || name.length
      });

      matrix[0][j++] = name;
    }
  }

  // determine values & max widths
  list.forEach(function (row, i) {
    matrix[i + 1] = [ ];
    descriptions.forEach(function (col) {

      if (row.hasOwnProperty(col.id)) {
        var value;
        if (options.prettyStrings && typeof row[col.id] === 'string') {
          value = row[col.id];
        }
        else {
          value = JSON.stringify(row[col.id]) || "";
        }

        matrix[i + 1].push(value);

        if (value.length > col.length && ! col.fixedLength) {
          col.length = Math.min(value.length, 100);
        }
      }
      else {
        // undefined
        matrix[i + 1].push('');
      }
    });
  });

  var divider = function () {
    var parts = [ ];
    descriptions.forEach(function (desc) {
      parts.push(exports.stringPadding('', desc.length, '-', 'r'));
    });

    if (options.framed) {
      return '+-' + parts.join('-+-') + '-+\n';
    }

    return parts.join('   ') + '\n';
  };

  var compose = function () {
    var result = '';

    if (options.framed) {
      result += divider();
    }
    matrix.forEach(function (row, i) {
      var parts = [ ];

      row.forEach(function (col, j) {

        var len = descriptions[j].length, value = row[j];
        if (value.length > len) {
          value = value.substr(0, len - pad.length) + pad;
        }
        parts.push(exports.stringPadding(value, len, ' ', 'r'));
      });

      if (options.framed) {
        result += '| ' + parts.join(' | ') + ' |\n';
      }
      else {
        result += parts.join('   ') + '\n';
      }

      if (i === 0) {
        result += divider();
      }
    });

    result += divider();

    if (! options.hideTotal) {
      result += internal.sprintf(options.totalString, String(list.length));
    }
    return result;
  };

  if (! Array.isArray(list)) {
    // not an array
    return;
  }

  if (list.length === 0) {
    exports.print(options.emptyString || "no document(s)");
  }
  else {
    exports.print(compose());
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief stringPadding
////////////////////////////////////////////////////////////////////////////////

exports.stringPadding = function (str, len, pad, dir) {
  // yes, this is more code than new Array(length).join(chr), but it makes jslint happy
  function fill (length, chr) {
    var result = '', i;
    for (i = 0; i < length; ++i) {
      result += chr;
    }
    return result;
  }

  if (typeof(len) === "undefined") {
    len = 0;
  }
  if (typeof(pad) === "undefined") {
    pad = ' ';
  }

  if (len + 1 >= str.length) {
    switch (dir || "r"){

      // LEFT
      case 'l':
        str = fill(len + 1 - str.length, pad) + str;
        break;

      // BOTH
      case 'b':
        var padlen = len - str.length;
        var right = Math.ceil(padlen / 2);
        var left = padlen - right;
        str = fill(left + 1, pad) + str + fill(right + 1, pad);
        break;

      default:
         str = str + fill(len + 1 - str.length, pad);
         break;
    }
  }

  return str;
};

////////////////////////////////////////////////////////////////////////////////
/// @brief throws an error in case a download failed
////////////////////////////////////////////////////////////////////////////////

exports.throwDownloadError = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_APPLICATION_DOWNLOAD_FAILED.code,
    errorMessage: exports.errors.ERROR_APPLICATION_DOWNLOAD_FAILED.message + ': ' + String(msg)
  });
};

////////////////////////////////////////////////////////////////////////////////
/// @brief throws an error in case of missing file
////////////////////////////////////////////////////////////////////////////////

exports.throwFileNotFound = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_FILE_NOT_FOUND.code,
    errorMessage: exports.errors.ERROR_FILE_NOT_FOUND.message + ': ' + String(msg)
  });
};

////////////////////////////////////////////////////////////////////////////////
/// @brief throws an error in case of a bad parameter
////////////////////////////////////////////////////////////////////////////////

exports.throwBadParameter = function (msg) {
  throw new exports.ArangoError({
    errorNum: exports.errors.ERROR_BAD_PARAMETER.code,
    errorMessage: exports.errors.ERROR_BAD_PARAMETER.message + ': ' + String(msg)
  });
};

////////////////////////////////////////////////////////////////////////////////
/// @brief checks parameter, throws an error if missing
////////////////////////////////////////////////////////////////////////////////

exports.checkParameter = function (usage, descs, vars) {
  var i;

  for (i = 0;  i < descs.length;  ++i) {
    var desc = descs[i];

    if (typeof vars[i] === "undefined") {
      exports.throwBadParameter(desc[0] + " missing, usage: " + usage);
    }

    if (typeof vars[i] !== desc[1]) {
      exports.throwBadParameter(desc[0] + " should be a '" + desc[1] + "', "
                              + "not '" + (typeof vars[i]) + "'");
    }
  }
};

////////////////////////////////////////////////////////////////////////////////
/// @brief generate info message for newer version(s) available
////////////////////////////////////////////////////////////////////////////////

exports.checkAvailableVersions = function (version) {
  var console = require("console");
  var log;

  if (require("org/arangodb").isServer) {
    log = console.info;
  }
  else {
    log = internal.print;
  }

  if (version === undefined) {
    version = internal.version;
  }

  if (version.match(/beta|alpha|preview|devel/) !== null) {
    log("You are using an alpha/beta/preview version ('" + version + "') of ArangoDB");
    return;
  }

  try {
    var u = "https://www.arangodb.com/repositories/versions.php?version=" + version +
            "&os=" + internal.platform;
    var d = internal.download(u, "", {timeout: 300});
    var v = JSON.parse(d.body);

    if (v.hasOwnProperty("bugfix")) {
      log("Please note that a new bugfix version '" + v.bugfix.version + "' is available");
    }

    if (v.hasOwnProperty("minor")) {
      log("Please note that a new minor version '" + v.minor.version + "' is available");
    }

    if (v.hasOwnProperty("major")) {
      log("Please note that a new major version '" + v.major.version + "' is available");
    }
  }
  catch (err) {
    if (console && console.debug) {
      console.debug("cannot check for newer version: ", err.stack);
    }
  }
};

// -----------------------------------------------------------------------------
// --SECTION--                                                       END-OF-FILE
// -----------------------------------------------------------------------------

// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// @addtogroup\\|// --SECTION--\\|/// @}\\|/\\*jslint"
// End:
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import { Spinner } from 'native-base';
import { connect } from 'react-redux';
import AuthenticationScreen from '../authentication-screen';

/*
  Renders children components if the user is authenticated.
  Otherwise an authentication screen is displayed.
*/
class Secure extends Component {
  static propTypes = {
    authenticated: PropTypes.bool.isRequired,
    loading: PropTypes.bool.isRequired,
    children: PropTypes.node.isRequired,
  }

  render() {
    const { authenticated, loading, children } = this.props;

    // Render children component(s) if the user is authenticated
    if (authenticated) {
      return children;
    }

    // Display loading spinner while authenticating
    if (loading) {
      return <Spinner color="blue" />;
    }

    // Else open the authentication screen
    return (
      <AuthenticationScreen />
    );
  }
}

// Props tied together with Redux state
const mapStateToProps = state => ({
  authenticated: state.session.token != null,
  loading: state.session.sessionLoading || state.user.userLoading,
});

// Connect class with Redux and export
export default connect(mapStateToProps)(Secure);
<commit_msg>Make Secure component use JASpinner<commit_after>import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import AuthenticationScreen from '../authentication-screen';
import JASpinner from '../../common/spinner/JASpinner';

/*
  Renders children components if the user is authenticated.
  Otherwise an authentication screen is displayed.
*/
class Secure extends Component {
  static propTypes = {
    authenticated: PropTypes.bool.isRequired,
    loading: PropTypes.bool.isRequired,
    children: PropTypes.node.isRequired,
  }

  render() {
    const { authenticated, loading, children } = this.props;

    // Render children component(s) if the user is authenticated
    if (authenticated) {
      return children;
    }

    // Display loading spinner while authenticating
    if (loading) {
      return (
        <JASpinner />
      );
    }

    // Else open the authentication screen
    return (
      <AuthenticationScreen />
    );
  }
}

// Props tied together with Redux state
const mapStateToProps = state => ({
  authenticated: state.session.token != null,
  loading: state.session.sessionLoading || state.user.userLoading,
});

// Connect class with Redux and export
export default connect(mapStateToProps)(Secure);
<|endoftext|>