<commit_before>/*global module:true*/
'use strict';

Math.log2 = Math.log2 || function(x) {
  return Math.log(x) / Math.LN2;
};

Math.log10 = Math.log10 || function(x) {
  return Math.log(x) / Math.LN10;
};

(function() {
  var Helpers = {
    avg: function(arr) {
      var v = 0;
      for (var index = 0; index < arr.length; ++index) {
        v += arr[index];
      }
      return v / arr.length;
    },
    min: function(arr) {
      if (arr.length === 0) return 0;
      var v = arr[0];
      for (var index = 1; index < arr.length; ++index) {
        var v2 = arr[index];
        if (Array.isArray(v2)) v2 = Helpers.avg(v2);
        if (v2 < v) v = v2;
      }
      return Math.max(0, v);
    },
    max: function(arr) {
      var v = 0;
      for (var index = 0; index < arr.length; ++index) {
        var v2 = arr[index];
        if (Array.isArray(v2)) v2 = Helpers.avg(v2);
        if (v2 > v) v = v2;
      }
      return Math.max(0, v);
    },
    upperMax: function(arr) {
      var v = 0;
      for (var index = 0; index < arr.length; ++index) {
        var v2 = arr[index];
        if (Array.isArray(v2)) v2 = Helpers.max(v2);
        if (v2 > v) v = v2;
      }
      return Math.max(0, v);
    },
    niceNumbers: function(range, round) {
      var exponent = Math.floor(Math.log10(range));
      var fraction = range / Math.pow(10, exponent);
      var niceFraction;
      if (round) {
        if (fraction < 1.5) niceFraction = 1;
        else if (fraction < 3) niceFraction = 2;
        else if (fraction < 7) niceFraction = 5;
        else niceFraction = 10;
      } else {
        if (fraction <= 1.0) niceFraction = 1;
        else if (fraction <= 2) niceFraction = 2;
        else if (fraction <= 5) niceFraction = 5;
        else niceFraction = 10;
      }
      return niceFraction * Math.pow(10, exponent);
    },
    getLinearTicks: function(min, max, maxTicks) {
      var range = Helpers.niceNumbers(max - min, false);
      var tickSpacing = Helpers.niceNumbers(range / (maxTicks - 1), true);
      return [
        Math.floor(min / tickSpacing) * tickSpacing,
        Math.ceil(max / tickSpacing) * tickSpacing,
        tickSpacing
      ];
    }
  };

  var BarChart = (function() {
    function BarChart(ctx, options) {
      this.options = {
        font: 'Helvetica',
        fontSize_title: 24,
        fontSize_axis: 20,
        fontSize_ticks: 18,
        fontSize_labels: 18,
        barPaddingPercent: 0.10,
        tickPaddingPercent: 0.10,
        padding_vertical: 10,
        padding_horizontal: 10,
        padding_ticks: 10,
        fillColor_background: 'rgb(220, 220, 220)',
        strokeColor_bar: 'rgb(0, 0, 0)',
        fillColor_bar: 'rgb(180, 180, 180)',
        scaleStyle: 'linear',
        errorBars: 'none'
      };
      options = options || { };
      for (var key in this.options) {
        if (options.hasOwnProperty(key)) this.options[key] = options[key];
      }
      this.ctx = ctx;
      this.content = { };
    }

    BarChart.prototype.update = function(content) {
      if (typeof content !== 'object') {
        throw new Error('Collections must be objects.');
      } else if (!(content.hasOwnProperty('labels') && content.hasOwnProperty('data'))) {
        throw new Error('Collection must specify labels and data.');
      } else if (!(Array.isArray(content.labels) && Array.isArray(content.data))) {
        throw new Error('Labels and data must be arrays.');
      } else if (content.labels.length !== content.data.length) {
        throw new Error('Labels and data length must match.');
      }
      content._data_standard_deviation = [];
      content._data_standard_error = [];
      for (var i = 0; i < content.data.length; ++i) {
        var isArr = Array.isArray(content.data[i]);
        if (isArr) {
          var mean = Helpers.avg(content.data[i]);
          var acc = 0;
          for (var i2 = 0; i2 < content.data[i].length; ++i2) acc += Math.pow(mean - content.data[i][i2], 2);
          acc = Math.sqrt(acc / (content.data[i].length - 1));
          content._data_standard_deviation.push(acc);
          content._data_standard_error.push(acc / Math.sqrt(content.data[i].length));
        } else {
          content._data_standard_deviation.push(0);
          content._data_standard_error.push(0);
        }
        if (this.options.scaleStyle === 'log2') {
          if (isArr) {
            for (var i3 = 0; i3 < content.data[i].length; ++i3) content.data[i][i3] = Math.log2(content.data[i][i3]);
          } else content.data[i] = Math.log2(content.data[i]);
        }
      }
      this.content = content;
      this.redraw();
    };

    BarChart.prototype.redraw = function() {
      setTimeout(function() {
        this._draw();
      }.bind(this), 0);
    };

    BarChart.prototype._draw = function() {
      var options = this.options;
      var ctx = this.ctx, content = this.content;
      ctx.translate(-0.5, -0.5);
      var width = ctx.canvas.width, height = ctx.canvas.height;
      var remainingWidth = width, remainingHeight = height;
      var index;

      if (options.fillColor_background != null) {
        ctx.save();
        ctx.fillStyle = options.fillColor_background;
        ctx.fillRect(0, 0, width, height);
        ctx.restore();
      }

      var topYPadding = options.padding_horizontal;
      remainingHeight -= options.padding_horizontal;
      ctx.fillStyle = 'rgb(0, 0, 0)';
      /* Draw title of bar chart */
      if (content.title != null) {
        ctx.save();
        ctx.font = options.fontSize_title + 'px ' + options.font;
        ctx.textAlign = 'center';
        ctx.fillText(content.title, width / 2, topYPadding + options.fontSize_title);
        ctx.restore();
        remainingHeight -= options.fontSize_title * 1.25;
        topYPadding += options.fontSize_title * 1.25;
      }

      /* Compute required left padding */
      var leftXPadding = options.padding_vertical;
      remainingWidth  -= options.padding_vertical;

      var leftXDrawYLabel = null;
      if (content.yAxis != null) {
        leftXDrawYLabel = leftXPadding + options.fontSize_axis * 0.5;
        remainingWidth -= options.fontSize_axis * 1.25;
        leftXPadding += options.fontSize_axis * 1.25;
      }

      ctx.save();
      ctx.font = options.fontSize_ticks + 'px ' + options.font;
      var maxChartValue = Helpers.upperMax(content.data);
      var maxYAxisTickWidth = options.scaleStyle == 'log2' ? Math.ceil(Math.pow(2, maxChartValue)) : maxChartValue;
      maxYAxisTickWidth = ctx.measureText(Math.max(maxYAxisTickWidth, 100)).width;
      maxYAxisTickWidth = Math.ceil(maxYAxisTickWidth) + options.padding_ticks;
      remainingWidth -= maxYAxisTickWidth;
      leftXPadding += maxYAxisTickWidth;
      ctx.restore();

      var rightXPadding = options.padding_vertical;
      remainingWidth -= options.padding_vertical;

      /* Draw x-axis label of bar chart */
      var bottomYPadding = options.padding_horizontal;
      remainingHeight -= options.padding_horizontal;
      if (content.xAxis != null) {
        ctx.save();
        ctx.font = options.fontSize_axis + 'px ' + options.font;
        ctx.fillStyle = 'rgb(0, 0, 0)';
        ctx.textAlign = 'center';
        ctx.fillText(content.xAxis, (width - remainingWidth) + remainingWidth / 2, height - bottomYPadding);
        remainingHeight -= options.fontSize_axis;
        bottomYPadding += options.fontSize_axis;
        ctx.restore();
      }

      /* Draw x-axis labels */
      ctx.save();
      ctx.textAlign = 'center';
      ctx.font = options.fontSize_labels + 'px ' + options.font;
      var widthPerBar = remainingWidth / content.data.length;
      var computedBarPadding = Math.floor((widthPerBar * options.barPaddingPercent) / 2);
      for (index = 0; index < content.labels.length; ++index) {
        ctx.fillText(
          content.labels[index],
          leftXPadding + index * widthPerBar + widthPerBar / 2,
          height - options.fontSize_labels / 2 - bottomYPadding
        );
      }
      remainingHeight -= options.fontSize_labels * 1.5;
      ctx.restore();

      /* Draw boundaries */
      ctx.save();
      ctx.strokeStyle = 'rgb(0, 0, 0)';
      ctx.beginPath();
      ctx.moveTo(leftXPadding, topYPadding);
      ctx.lineTo(leftXPadding, topYPadding + remainingHeight);
      ctx.lineTo(leftXPadding + remainingWidth, topYPadding + remainingHeight);
      ctx.stroke();
      ctx.restore();

      /* Draw y-axis label of bar chart */
      if (content.yAxis != null) {
        ctx.save();
        ctx.translate(0, 0);
        ctx.rotate(Math.PI * 1.5);
        ctx.font = options.fontSize_axis + 'px ' + options.font;
        ctx.fillStyle = 'rgb(0, 0, 0)';
        ctx.textAlign = 'center';
        ctx.fillText(content.yAxis, -(topYPadding + remainingHeight / 2), leftXDrawYLabel);
        ctx.restore();
      }

      /* Draw y-axis labels */
      ctx.save();
      ctx.fillStyle = 'rgb(0, 0, 0)';
      ctx.strokeStyle = 'rgba(0, 0, 0, 0.20)';
      ctx.font = options.fontSize_ticks + 'px ' + options.font;
      ctx.textAlign = 'right';
      var tickMeta = Helpers.getLinearTicks(0, maxChartValue, Math.max(2, remainingHeight / (options.fontSize_ticks * (1 + options.tickPaddingPercent))));
      maxChartValue = tickMeta[1] + Math.ceil(maxChartValue / options.fontSize_ticks);
      var ticks = [];
      while (tickMeta[0] <= tickMeta[1]) {
        ticks.push(tickMeta[0]);
        tickMeta[0] += tickMeta[2];
      }
      for (index = 0; index < ticks.length; ++index) {
        var tickHeight = Math.round(remainingHeight * (ticks[index] / maxChartValue));
        if (options.scaleStyle == 'log2' && ticks[index] !== 0) ticks[index] = Math.round(Math.pow(2, ticks[index]));
        ctx.fillText(ticks[index] + '', leftXPadding - options.padding_ticks, topYPadding + remainingHeight - tickHeight);
        if (index == 0) continue;
        ctx.beginPath();
        ctx.moveTo(leftXPadding, topYPadding + remainingHeight - tickHeight);
        ctx.lineTo(leftXPadding + remainingWidth, topYPadding + remainingHeight - tickHeight);
        ctx.stroke();
      }
      ctx.restore();

      /* Draw bars */
      ctx.save();
      ctx.strokeStyle = options.strokeColor_bar;
      ctx.fillStyle = options.fillColor_bar;
      for (index = 0; index < content.data.length; ++index) {
        var v = content.data[index];
        if (Array.isArray(v)) v = Helpers.avg(v);
        var renderBarHeight = Math.round(remainingHeight * (v / maxChartValue));
        var renderStartX = leftXPadding + widthPerBar * index;
        var renderUpToY = topYPadding + remainingHeight - renderBarHeight;
        ctx.beginPath();
        ctx.moveTo(renderStartX + computedBarPadding, topYPadding + remainingHeight);
        ctx.lineTo(renderStartX + computedBarPadding, renderUpToY);
        ctx.lineTo(renderStartX + (widthPerBar - 1) - computedBarPadding, renderUpToY);
        ctx.lineTo(renderStartX + (widthPerBar - 1) - computedBarPadding, topYPadding + remainingHeight);
        ctx.stroke();
        ctx.fill();

        if (options.errorBars === 'error') {
          var val;
          if ((val = content._data_standard_error[index]) != 0) {
            var renderBarError = Math.round(remainingHeight * (val / maxChartValue));
            ctx.beginPath();
            var wiskerWidth = Math.round((widthPerBar - computedBarPadding * 2) / 8);
            var x = leftXPadding + widthPerBar * index + widthPerBar / 2;
            ctx.moveTo(x - wiskerWidth, renderUpToY + renderBarError);
            ctx.lineTo(x + wiskerWidth, renderUpToY + renderBarError);
            ctx.moveTo(x, renderUpToY + renderBarError);
            ctx.lineTo(x, renderUpToY - renderBarError);
            ctx.moveTo(x - wiskerWidth, renderUpToY - renderBarError);
            ctx.lineTo(x + wiskerWidth, renderUpToY - renderBarError);
            ctx.stroke();
          }
        }
      }
      ctx.restore();
    };

    return BarChart;
  })();

  if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
    module.exports = BarChart;
  } else {
    window.BarChart = BarChart;
  }
})();
<commit_msg>Mod data at correct time<commit_after>/*global module:true*/
'use strict';

Math.log2 = Math.log2 || function(x) {
  return Math.log(x) / Math.LN2;
};

Math.log10 = Math.log10 || function(x) {
  return Math.log(x) / Math.LN10;
};

(function() {
  var Helpers = {
    avg: function(arr) {
      var v = 0;
      for (var index = 0; index < arr.length; ++index) {
        v += arr[index];
      }
      return v / arr.length;
    },
    min: function(arr) {
      if (arr.length === 0) return 0;
      var v = arr[0];
      for (var index = 1; index < arr.length; ++index) {
        var v2 = arr[index];
        if (Array.isArray(v2)) v2 = Helpers.avg(v2);
        if (v2 < v) v = v2;
      }
      return Math.max(0, v);
    },
    max: function(arr) {
      var v = 0;
      for (var index = 0; index < arr.length; ++index) {
        var v2 = arr[index];
        if (Array.isArray(v2)) v2 = Helpers.avg(v2);
        if (v2 > v) v = v2;
      }
      return Math.max(0, v);
    },
    upperMax: function(arr) {
      var v = 0;
      for (var index = 0; index < arr.length; ++index) {
        var v2 = arr[index];
        if (Array.isArray(v2)) v2 = Helpers.max(v2);
        if (v2 > v) v = v2;
      }
      return Math.max(0, v);
    },
    niceNumbers: function(range, round) {
      var exponent = Math.floor(Math.log10(range));
      var fraction = range / Math.pow(10, exponent);
      var niceFraction;
      if (round) {
        if (fraction < 1.5) niceFraction = 1;
        else if (fraction < 3) niceFraction = 2;
        else if (fraction < 7) niceFraction = 5;
        else niceFraction = 10;
      } else {
        if (fraction <= 1.0) niceFraction = 1;
        else if (fraction <= 2) niceFraction = 2;
        else if (fraction <= 5) niceFraction = 5;
        else niceFraction = 10;
      }
      return niceFraction * Math.pow(10, exponent);
    },
    getLinearTicks: function(min, max, maxTicks) {
      var range = Helpers.niceNumbers(max - min, false);
      var tickSpacing = Helpers.niceNumbers(range / (maxTicks - 1), true);
      return [
        Math.floor(min / tickSpacing) * tickSpacing,
        Math.ceil(max / tickSpacing) * tickSpacing,
        tickSpacing
      ];
    }
  };

  var BarChart = (function() {
    function BarChart(ctx, options) {
      this.options = {
        font: 'Helvetica',
        fontSize_title: 24,
        fontSize_axis: 20,
        fontSize_ticks: 18,
        fontSize_labels: 18,
        barPaddingPercent: 0.10,
        tickPaddingPercent: 0.10,
        padding_vertical: 10,
        padding_horizontal: 10,
        padding_ticks: 10,
        fillColor_background: 'rgb(220, 220, 220)',
        strokeColor_bar: 'rgb(0, 0, 0)',
        fillColor_bar: 'rgb(180, 180, 180)',
        scaleStyle: 'linear',
        errorBars: 'none'
      };
      options = options || { };
      for (var key in this.options) {
        if (options.hasOwnProperty(key)) this.options[key] = options[key];
      }
      this.ctx = ctx;
      this.content = { };
    }

    BarChart.prototype.update = function(content) {
      if (typeof content !== 'object') {
        throw new Error('Collections must be objects.');
      } else if (!(content.hasOwnProperty('labels') && content.hasOwnProperty('data'))) {
        throw new Error('Collection must specify labels and data.');
      } else if (!(Array.isArray(content.labels) && Array.isArray(content.data))) {
        throw new Error('Labels and data must be arrays.');
      } else if (content.labels.length !== content.data.length) {
        throw new Error('Labels and data length must match.');
      }
      content._data_standard_deviation = [];
      content._data_standard_error = [];
      for (var i = 0; i < content.data.length; ++i) {
        var isArr = Array.isArray(content.data[i]);
        if (this.options.scaleStyle === 'log2') {
          if (isArr) {
            for (var i3 = 0; i3 < content.data[i].length; ++i3) content.data[i][i3] = Math.log2(content.data[i][i3]);
          } else content.data[i] = Math.log2(content.data[i]);
        }
        if (isArr) {
          var mean = Helpers.avg(content.data[i]);
          var acc = 0;
          for (var i2 = 0; i2 < content.data[i].length; ++i2) acc += Math.pow(mean - content.data[i][i2], 2);
          acc = Math.sqrt(acc / (content.data[i].length - 1));
          content._data_standard_deviation.push(acc);
          content._data_standard_error.push(acc / Math.sqrt(content.data[i].length));
        } else {
          content._data_standard_deviation.push(0);
          content._data_standard_error.push(0);
        }
      }
      this.content = content;
      this.redraw();
    };

    BarChart.prototype.redraw = function() {
      setTimeout(function() {
        this._draw();
      }.bind(this), 0);
    };

    BarChart.prototype._draw = function() {
      var options = this.options;
      var ctx = this.ctx, content = this.content;
      ctx.translate(-0.5, -0.5);
      var width = ctx.canvas.width, height = ctx.canvas.height;
      var remainingWidth = width, remainingHeight = height;
      var index;

      if (options.fillColor_background != null) {
        ctx.save();
        ctx.fillStyle = options.fillColor_background;
        ctx.fillRect(0, 0, width, height);
        ctx.restore();
      }

      var topYPadding = options.padding_horizontal;
      remainingHeight -= options.padding_horizontal;
      ctx.fillStyle = 'rgb(0, 0, 0)';
      /* Draw title of bar chart */
      if (content.title != null) {
        ctx.save();
        ctx.font = options.fontSize_title + 'px ' + options.font;
        ctx.textAlign = 'center';
        ctx.fillText(content.title, width / 2, topYPadding + options.fontSize_title);
        ctx.restore();
        remainingHeight -= options.fontSize_title * 1.25;
        topYPadding += options.fontSize_title * 1.25;
      }

      /* Compute required left padding */
      var leftXPadding = options.padding_vertical;
      remainingWidth  -= options.padding_vertical;

      var leftXDrawYLabel = null;
      if (content.yAxis != null) {
        leftXDrawYLabel = leftXPadding + options.fontSize_axis * 0.5;
        remainingWidth -= options.fontSize_axis * 1.25;
        leftXPadding += options.fontSize_axis * 1.25;
      }

      ctx.save();
      ctx.font = options.fontSize_ticks + 'px ' + options.font;
      var maxChartValue = Helpers.upperMax(content.data);
      var maxYAxisTickWidth = options.scaleStyle == 'log2' ? Math.ceil(Math.pow(2, maxChartValue)) : maxChartValue;
      maxYAxisTickWidth = ctx.measureText(Math.max(maxYAxisTickWidth, 100)).width;
      maxYAxisTickWidth = Math.ceil(maxYAxisTickWidth) + options.padding_ticks;
      remainingWidth -= maxYAxisTickWidth;
      leftXPadding += maxYAxisTickWidth;
      ctx.restore();

      var rightXPadding = options.padding_vertical;
      remainingWidth -= options.padding_vertical;

      /* Draw x-axis label of bar chart */
      var bottomYPadding = options.padding_horizontal;
      remainingHeight -= options.padding_horizontal;
      if (content.xAxis != null) {
        ctx.save();
        ctx.font = options.fontSize_axis + 'px ' + options.font;
        ctx.fillStyle = 'rgb(0, 0, 0)';
        ctx.textAlign = 'center';
        ctx.fillText(content.xAxis, (width - remainingWidth) + remainingWidth / 2, height - bottomYPadding);
        remainingHeight -= options.fontSize_axis;
        bottomYPadding += options.fontSize_axis;
        ctx.restore();
      }

      /* Draw x-axis labels */
      ctx.save();
      ctx.textAlign = 'center';
      ctx.font = options.fontSize_labels + 'px ' + options.font;
      var widthPerBar = remainingWidth / content.data.length;
      var computedBarPadding = Math.floor((widthPerBar * options.barPaddingPercent) / 2);
      for (index = 0; index < content.labels.length; ++index) {
        ctx.fillText(
          content.labels[index],
          leftXPadding + index * widthPerBar + widthPerBar / 2,
          height - options.fontSize_labels / 2 - bottomYPadding
        );
      }
      remainingHeight -= options.fontSize_labels * 1.5;
      ctx.restore();

      /* Draw boundaries */
      ctx.save();
      ctx.strokeStyle = 'rgb(0, 0, 0)';
      ctx.beginPath();
      ctx.moveTo(leftXPadding, topYPadding);
      ctx.lineTo(leftXPadding, topYPadding + remainingHeight);
      ctx.lineTo(leftXPadding + remainingWidth, topYPadding + remainingHeight);
      ctx.stroke();
      ctx.restore();

      /* Draw y-axis label of bar chart */
      if (content.yAxis != null) {
        ctx.save();
        ctx.translate(0, 0);
        ctx.rotate(Math.PI * 1.5);
        ctx.font = options.fontSize_axis + 'px ' + options.font;
        ctx.fillStyle = 'rgb(0, 0, 0)';
        ctx.textAlign = 'center';
        ctx.fillText(content.yAxis, -(topYPadding + remainingHeight / 2), leftXDrawYLabel);
        ctx.restore();
      }

      /* Draw y-axis labels */
      ctx.save();
      ctx.fillStyle = 'rgb(0, 0, 0)';
      ctx.strokeStyle = 'rgba(0, 0, 0, 0.20)';
      ctx.font = options.fontSize_ticks + 'px ' + options.font;
      ctx.textAlign = 'right';
      var tickMeta = Helpers.getLinearTicks(0, maxChartValue, Math.max(2, remainingHeight / (options.fontSize_ticks * (1 + options.tickPaddingPercent))));
      maxChartValue = tickMeta[1] + Math.ceil(maxChartValue / options.fontSize_ticks);
      var ticks = [];
      while (tickMeta[0] <= tickMeta[1]) {
        ticks.push(tickMeta[0]);
        tickMeta[0] += tickMeta[2];
      }
      for (index = 0; index < ticks.length; ++index) {
        var tickHeight = Math.round(remainingHeight * (ticks[index] / maxChartValue));
        if (options.scaleStyle == 'log2' && ticks[index] !== 0) ticks[index] = Math.round(Math.pow(2, ticks[index]));
        ctx.fillText(ticks[index] + '', leftXPadding - options.padding_ticks, topYPadding + remainingHeight - tickHeight);
        if (index == 0) continue;
        ctx.beginPath();
        ctx.moveTo(leftXPadding, topYPadding + remainingHeight - tickHeight);
        ctx.lineTo(leftXPadding + remainingWidth, topYPadding + remainingHeight - tickHeight);
        ctx.stroke();
      }
      ctx.restore();

      /* Draw bars */
      ctx.save();
      ctx.strokeStyle = options.strokeColor_bar;
      ctx.fillStyle = options.fillColor_bar;
      for (index = 0; index < content.data.length; ++index) {
        var v = content.data[index];
        if (Array.isArray(v)) v = Helpers.avg(v);
        var renderBarHeight = Math.round(remainingHeight * (v / maxChartValue));
        var renderStartX = leftXPadding + widthPerBar * index;
        var renderUpToY = topYPadding + remainingHeight - renderBarHeight;
        ctx.beginPath();
        ctx.moveTo(renderStartX + computedBarPadding, topYPadding + remainingHeight);
        ctx.lineTo(renderStartX + computedBarPadding, renderUpToY);
        ctx.lineTo(renderStartX + (widthPerBar - 1) - computedBarPadding, renderUpToY);
        ctx.lineTo(renderStartX + (widthPerBar - 1) - computedBarPadding, topYPadding + remainingHeight);
        ctx.stroke();
        ctx.fill();

        if (options.errorBars === 'error') {
          var val;
          if ((val = content._data_standard_error[index]) != 0) {
            var renderBarError = Math.round(remainingHeight * (val / maxChartValue));
            ctx.beginPath();
            var wiskerWidth = Math.round((widthPerBar - computedBarPadding * 2) / 8);
            var x = leftXPadding + widthPerBar * index + widthPerBar / 2;
            ctx.moveTo(x - wiskerWidth, renderUpToY + renderBarError);
            ctx.lineTo(x + wiskerWidth, renderUpToY + renderBarError);
            ctx.moveTo(x, renderUpToY + renderBarError);
            ctx.lineTo(x, renderUpToY - renderBarError);
            ctx.moveTo(x - wiskerWidth, renderUpToY - renderBarError);
            ctx.lineTo(x + wiskerWidth, renderUpToY - renderBarError);
            ctx.stroke();
          }
        }
      }
      ctx.restore();
    };

    return BarChart;
  })();

  if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
    module.exports = BarChart;
  } else {
    window.BarChart = BarChart;
  }
})();
<|endoftext|>


<commit_before>// Strider Simple Worker
// Niall O'Higgins 2012
//
// A simple, in-process worker implementation for Strider.
//

var exec = require('child_process').exec
var gitane = require('gitane')
var gumshoe = require('gumshoe')
var path = require('path')
var spawn = require('child_process').spawn
var Step = require('step')

// Work around npm not being installed on some systems - use own copy
// the test clause is for Heroku
var npmCmd = "$(test -x ~/bin/node && echo ~/bin/node || echo node) ../../node_modules/npm/bin/npm-cli.js"
var nodePrepare = npmCmd + " install"
var nodeTest = npmCmd + " test"
var nodeStart = npmCmd + " start"
var virtualenvPath = path.join(__dirname, "thirdparty", "virtualenv.py")
console.log(virtualenvPath)
var djangoPrepare = "python " + virtualenvPath + " env && env/bin/pip -r requirements.txt"
var djangoTest = "env/bin/python manage.py test"
var setupPyPrepare = "python " + virtualenvPath + " env && env/bin/python setup.py develop ; env/bin/pip -r requirements.txt"
var setupPyTest = "env/bin/python setup.py test"

// Built-in rules for project-type detection
var DEFAULT_PROJECT_TYPE_RULES = [

  // Node
  {filename:"package.json", grep:/express/i, language:"node.js", framework:"express", prepare:nodePrepare, test:nodeTest, start:nodeStart},
  {filename:"package.json", grep:/connect/i, language:"node.js", framework:"connect", prepare:nodePrepare, test:nodeTest, start:nodeStart},
  {filename:"package.json", exists:true, language:"node.js", framework:null, prepare:nodePrepare, test:nodeTest, start:nodeStart},
  // Python
  {filename:"setup.py", grep:/pyramid/i, language:"python", framework:"pyramid", prepare:setupPyPrepare, test:setupPyTest},
  {filename:"manage.py", grep:/django/i, language:"python", framework:"django", prepare:djangoPrepare, test:djangoTest},
  {filename:"setup.py", exists:true, language:"python", framework:null, prepare:setupPyPrepare, test:setupPyTest},

]

// detection rules which may be added by worker plugins
// rules must contain a property *test* which can either be a string or a function taking a callback.
// if a string, this is a shell command to be executed to run project tests (e.g. "npm install")
// if a function, this accepts a callback argument of signature function(err) which must be called.
//
// rules may contain a property *prepare* which can be a string or a function like *test*. this is for pre-test
// preparations (e.g. "npm install")
//
var detectionRules = []
// pre-start commands which may be added by worker plugins
// E.g. to start mongodb or postgresql
// pre-start commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "mongod")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var setupActions = []
// teardown commands which may be added by worker plugins
// E.g. to stop mongodb or postgresql
// teardown commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "kill $PID")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var teardownActions = []

// Wrap a shell command for execution by spawn()
function shellWrap(str) {
  return { cmd:"sh", args:["-c", str] }
}

function registerEvents(emitter) {

  // the queue.new_job event is primary way jobs are submitted
  //

  emitter.on('queue.new_job', function(data) {
    // cross-process (per-job) output buffers
    var stderrBuffer = ""
    var stdoutBuffer = ""
    var stdmergedBuffer = ""
    // Put stuff under `_work`
    var dir = path.join(__dirname, '_work')
    console.log('new job')
    // Start the clock
    var t1 = new Date()

    // Emit a status update event. This can result in data being sent to the
    // user's browser in realtime via socket.io.
    function updateStatus(evType, opts) {
      var t2 = new Date()
      var elapsed = (t2.getTime() - t1.getTime()) / 1000
      var msg = {
        userId:data.user_id,
        jobId:data.job_id,
        timeElapsed:elapsed,
        repoUrl:data.repo_config.url,
        stdout: opts.stdout || "",
        stderr: opts.stderr || "",
        stdmerged: opts.stdmerged || "",
        autodetectResult:opts.autodetectResult || null,
        testExitCode: null,
        deployExitCode: null,
      }
      if (opts.testExitCode !== undefined) {
        msg.testExitCode = opts.testExitCode
      }
      if (opts.deployExitCode !== undefined) {
        msg.deployExitCode = opts.deployExitCode
      }

      emitter.emit(evType, msg)
    }

    // Insert a synthetic (non job-generated) output message
    // This automatically prefixes with "[STRIDER]" to make the source
    // of the message clearer to the user.
    function striderMessage(message) {
      var msg = "[STRIDER] " + message + "\n"
      stdmergedBuffer += msg
      stdoutBuffer += msg
      updateStatus("queue.task_update", {stdout:msg, stdmerged:msg})
    }

    //
    // forkProc(cwd, shell, cb)
    // or
    // forkProc(cwd, cmd, args, cb)
    // or
    // forkProc({opts}, cb)
    //
    function forkProc(cwd, cmd, args, cb) {
      var env = process.env
      if (typeof(cwd) === 'object') {
        cb = cmd
        var cmd = cwd.cmd
        var args = cwd.args
        // Merge/override any variables
        for (var i=0; i < Object.keys(cwd.env).length; i++) {
          env[Object.keys(cwd.env)[i]] = cwd.env[Object.keys(cwd.env)[i]]
        }
        cwd = cwd.cwd
      }
      if (typeof(cmd) === 'string' && typeof(args) === 'function') {
        var split = shell.split(/\s+/)
        var cmd = split[0]
        var args = split.slice(1)
        cb = args
      }
      env.PAAS_NAME = 'strider'
      var proc = spawn(cmd, args, {cwd: cwd, env: env})

      // per-process output buffers
      proc.stderrBuffer = ""
      proc.stdoutBuffer = ""
      proc.stdmergedBuffer = ""

      proc.stdout.setEncoding('utf8')
      proc.stderr.setEncoding('utf8')

      proc.stdout.on('data', function(buf) {
        proc.stdoutBuffer += buf
        proc.stdmergedBuffer += buf
        stdoutBuffer += buf
        stdmergedBuffer += buf
        updateStatus("queue.task_update" , {stdout:buf})
      })

      proc.stderr.on('data', function(buf) {
        proc.stderrBuffer += buf
        proc.stdmergedBuffer += buf
        stderrBuffer += buf
        stdmergedBuffer += buf
        updateStatus("queue.task_update", {stderr:buf})
      })

      proc.on('exit', function(exitCode) {
        console.log("process exited with code: %d", exitCode)
        cb(exitCode)
      })

      return proc
    }

    Step(
      function() {
        // XXX: Support incremental builds at some point
        exec('rm -rf ' + dir + ' ; mkdir -p ' + dir, this)
      },
      function(err) {
        if (err) throw err
        console.log("cloning %s into %s", data.repo_ssh_url, dir)
        var msg = "Starting git clone of repo at " + data.repo_ssh_url
        striderMessage(msg)
        gitane.run(dir, data.repo_config.privkey, 'git clone ' + data.repo_ssh_url, this)
      },
      function(err, stderr, stdout) {
        if (err) throw err
        this.workingDir = path.join(dir, path.basename(data.repo_ssh_url.replace('.git', '')))
        updateStatus("queue.task_update", {stdout:stdout, stderr:stderr, stdmerged:stdout+stderr})
        var msg = "Git clone complete"
        console.log(msg)
        striderMessage(msg)
        gumshoe.run(this.workingDir, detectionRules, this)
      },
      function(err, result) {
        if (err) throw err
        // TODO: Setup phase (database bringup, etc)

        // Context object for action functions
        var context = {
          forkProc: forkProc,
          updateStatus: updateStatus,
          striderMessage: striderMessage,
          shellWrap:shellWrap,
          workingDir: this.workingDir,
        }

        // No-op defaults
        var prepare = test = deploy = function(context, cb) {
          cb(null)
        }

        function complete(testCode, deployCode, cb) {
          updateStatus("queue.task_complete", {
            stderr:stderrBuffer,
            stdout:stdoutBuffer,
            stdmerged:stdmergedBuffer,
            testExitCode:testCode,
            deployExitCode:deployCode
          })
          if (typeof(cb) === 'function') cb(null)
        }

        // If actions are strings, we assume they are shell commands and try to execute them
        // directly ourselves.
        var self = this
        if (typeof(result.prepare) === 'string') {
          var psh = shellWrap(result.prepare)
          prepare = function(context, cb) {
            forkProc(self.workingDir, psh.cmd, psh.args, cb)
          }
        }

        if (typeof(result.test) === 'string') {
          var tsh = shellWrap(result.test)
          test = function(context, cb) {
            forkProc(self.workingDir, tsh.cmd, tsh.args, cb)
          }
        }
        // Execution actions may be delegated to functions.
        // This is useful for example for multi-step things like in Python where a virtual env must be set up.
        // Functions are of signature function(context, cb)
        // We assume the function handles any necessary shell interaction and sending of update messages.
        if (typeof(result.prepare) === 'function') {
          prepare = result.prepare
        }
        if (typeof(result.test) === 'function') {
          test = result.test
        }

        prepare(context, function(prepareExitCode) {
          if (prepareExitCode !== 0) {
            // Prepare step failed
            var msg = "Prepare failed; exit code: " + prepareExitCode
            striderMessage(msg)
            console.log(msg)
            console.log("stdmergedBuffer: %s", stdmergedBuffer);
            return complete(prepareExitCode, null)
          }
          test(context, function(testExitCode) {
            var msg = "Test exit code: " + testExitCode
            console.log(msg)
            striderMessage(msg)
            if (testExitCode !== 0 || data.job_type !== "TEST_AND_DEPLOY") {
              // Test step failed or no deploy requested
              complete(testExitCode, null)
            } else {
              // Test step passed and deploy requested
              deploy(context, function(deployExitCode) {
                complete(testExitCode, deployExitCode)
              })
            }

          })
        })

        // TODO: Deploy (e.g. Heroku, dotCloud)

        // TODO: Teardown phase (database shutdown, etc)

      }

    )
  })
}

// Add an array of detection rules to head of list
function addDetectionRules(r) {
  detectionRules = r.concat(detectionRules)
}

// Add a single detection rule to head of list
function addDetectionRule(r) {
  detectionRules = [r].concat(detectionRules)
}

module.exports = function(context, cb) {
  // XXX test purposes
  detectionRules = DEFAULT_PROJECT_TYPE_RULES
  // Build a worker context, which is a stripped-down version of the webapp context
  var workerContext = {
    addDetectionRule:addDetectionRule,
    addDetectionRules:addDetectionRules,
    config: context.config,
    emitter: context.emitter,
    extdir: context.extdir,
  }

  Step(
    function() {
      context.loader.initExtensions(context.extdir, "worker", workerContext, null, this)
    },
    function(err, initialized) {
      registerEvents(context.emitter)
      console.log("Strider Simple Worker ready")
      cb(null, null)
    }
  )

}
<commit_msg>simplify<commit_after>// Strider Simple Worker
// Niall O'Higgins 2012
//
// A simple, in-process worker implementation for Strider.
//

var exec = require('child_process').exec
var gitane = require('gitane')
var gumshoe = require('gumshoe')
var path = require('path')
var spawn = require('child_process').spawn
var Step = require('step')

// Work around npm not being installed on some systems - use own copy
// the test clause is for Heroku
var npmCmd = "$(test -x ~/bin/node && echo ~/bin/node || echo node) ../../node_modules/npm/bin/npm-cli.js"
var nodePrepare = npmCmd + " install"
var nodeTest = npmCmd + " test"
var nodeStart = npmCmd + " start"
var virtualenvPath = path.join(__dirname, "thirdparty", "virtualenv.py")
console.log(virtualenvPath)
var djangoPrepare = "python " + virtualenvPath + " env && env/bin/pip -r requirements.txt"
var djangoTest = "env/bin/python manage.py test"
var setupPyPrepare = "python " + virtualenvPath + " env && env/bin/python setup.py develop ; env/bin/pip -r requirements.txt"
var setupPyTest = "env/bin/python setup.py test"

// Built-in rules for project-type detection
var DEFAULT_PROJECT_TYPE_RULES = [

  // Node
  {filename:"package.json", grep:/express/i, language:"node.js", framework:"express", prepare:nodePrepare, test:nodeTest, start:nodeStart},
  {filename:"package.json", grep:/connect/i, language:"node.js", framework:"connect", prepare:nodePrepare, test:nodeTest, start:nodeStart},
  {filename:"package.json", exists:true, language:"node.js", framework:null, prepare:nodePrepare, test:nodeTest, start:nodeStart},
  // Python
  {filename:"setup.py", grep:/pyramid/i, language:"python", framework:"pyramid", prepare:setupPyPrepare, test:setupPyTest},
  {filename:"manage.py", grep:/django/i, language:"python", framework:"django", prepare:djangoPrepare, test:djangoTest},
  {filename:"setup.py", exists:true, language:"python", framework:null, prepare:setupPyPrepare, test:setupPyTest},

]

// detection rules which may be added by worker plugins
// rules must contain a property *test* which can either be a string or a function taking a callback.
// if a string, this is a shell command to be executed to run project tests (e.g. "npm install")
// if a function, this accepts a callback argument of signature function(err) which must be called.
//
// rules may contain a property *prepare* which can be a string or a function like *test*. this is for pre-test
// preparations (e.g. "npm install")
//
var detectionRules = []
// pre-start commands which may be added by worker plugins
// E.g. to start mongodb or postgresql
// pre-start commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "mongod")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var setupActions = []
// teardown commands which may be added by worker plugins
// E.g. to stop mongodb or postgresql
// teardown commands may be either a string or a function
// XXX: foreground vs daemonprocesses
// if a string, this is a shell command to be executed to run project tests (e.g. "kill $PID")
// if a function, this accepts a callback argument of signature function(err) which must be called.
var teardownActions = []

// Wrap a shell command for execution by spawn()
function shellWrap(str) {
  return { cmd:"sh", args:["-c", str] }
}

function registerEvents(emitter) {

  // the queue.new_job event is primary way jobs are submitted
  //

  emitter.on('queue.new_job', function(data) {
    // cross-process (per-job) output buffers
    var stderrBuffer = ""
    var stdoutBuffer = ""
    var stdmergedBuffer = ""
    // Put stuff under `_work`
    var dir = path.join(__dirname, '_work')
    console.log('new job')
    // Start the clock
    var t1 = new Date()

    // Emit a status update event. This can result in data being sent to the
    // user's browser in realtime via socket.io.
    function updateStatus(evType, opts) {
      var t2 = new Date()
      var elapsed = (t2.getTime() - t1.getTime()) / 1000
      var msg = {
        userId:data.user_id,
        jobId:data.job_id,
        timeElapsed:elapsed,
        repoUrl:data.repo_config.url,
        stdout: opts.stdout || "",
        stderr: opts.stderr || "",
        stdmerged: opts.stdmerged || "",
        autodetectResult:opts.autodetectResult || null,
        testExitCode: null,
        deployExitCode: null,
      }
      if (opts.testExitCode !== undefined) {
        msg.testExitCode = opts.testExitCode
      }
      if (opts.deployExitCode !== undefined) {
        msg.deployExitCode = opts.deployExitCode
      }

      emitter.emit(evType, msg)
    }

    // Insert a synthetic (non job-generated) output message
    // This automatically prefixes with "[STRIDER]" to make the source
    // of the message clearer to the user.
    function striderMessage(message) {
      var msg = "[STRIDER] " + message + "\n"
      stdmergedBuffer += msg
      stdoutBuffer += msg
      updateStatus("queue.task_update", {stdout:msg, stdmerged:msg})
    }

    //
    // forkProc(cwd, shell, cb)
    // or
    // forkProc(cwd, cmd, args, cb)
    // or
    // forkProc({opts}, cb)
    //
    function forkProc(cwd, cmd, args, cb) {
      var env = process.env
      if (typeof(cwd) === 'object') {
        cb = cmd
        var cmd = cwd.cmd
        var args = cwd.args
        // Merge/override any variables
        var keys = Object.keys(cwd.env)
        for (var i=0; i < keys.length; i++) {
          env[keys[i]] = cwd.env[keys[i]]
        }
        cwd = cwd.cwd
      }
      if (typeof(cmd) === 'string' && typeof(args) === 'function') {
        var split = shell.split(/\s+/)
        var cmd = split[0]
        var args = split.slice(1)
        cb = args
      }
      env.PAAS_NAME = 'strider'
      var proc = spawn(cmd, args, {cwd: cwd, env: env})

      // per-process output buffers
      proc.stderrBuffer = ""
      proc.stdoutBuffer = ""
      proc.stdmergedBuffer = ""

      proc.stdout.setEncoding('utf8')
      proc.stderr.setEncoding('utf8')

      proc.stdout.on('data', function(buf) {
        proc.stdoutBuffer += buf
        proc.stdmergedBuffer += buf
        stdoutBuffer += buf
        stdmergedBuffer += buf
        updateStatus("queue.task_update" , {stdout:buf})
      })

      proc.stderr.on('data', function(buf) {
        proc.stderrBuffer += buf
        proc.stdmergedBuffer += buf
        stderrBuffer += buf
        stdmergedBuffer += buf
        updateStatus("queue.task_update", {stderr:buf})
      })

      proc.on('exit', function(exitCode) {
        console.log("process exited with code: %d", exitCode)
        cb(exitCode)
      })

      return proc
    }

    Step(
      function() {
        // XXX: Support incremental builds at some point
        exec('rm -rf ' + dir + ' ; mkdir -p ' + dir, this)
      },
      function(err) {
        if (err) throw err
        console.log("cloning %s into %s", data.repo_ssh_url, dir)
        var msg = "Starting git clone of repo at " + data.repo_ssh_url
        striderMessage(msg)
        gitane.run(dir, data.repo_config.privkey, 'git clone ' + data.repo_ssh_url, this)
      },
      function(err, stderr, stdout) {
        if (err) throw err
        this.workingDir = path.join(dir, path.basename(data.repo_ssh_url.replace('.git', '')))
        updateStatus("queue.task_update", {stdout:stdout, stderr:stderr, stdmerged:stdout+stderr})
        var msg = "Git clone complete"
        console.log(msg)
        striderMessage(msg)
        gumshoe.run(this.workingDir, detectionRules, this)
      },
      function(err, result) {
        if (err) throw err
        // TODO: Setup phase (database bringup, etc)

        // Context object for action functions
        var context = {
          forkProc: forkProc,
          updateStatus: updateStatus,
          striderMessage: striderMessage,
          shellWrap:shellWrap,
          workingDir: this.workingDir,
        }

        // No-op defaults
        var prepare = test = deploy = function(context, cb) {
          cb(null)
        }

        function complete(testCode, deployCode, cb) {
          updateStatus("queue.task_complete", {
            stderr:stderrBuffer,
            stdout:stdoutBuffer,
            stdmerged:stdmergedBuffer,
            testExitCode:testCode,
            deployExitCode:deployCode
          })
          if (typeof(cb) === 'function') cb(null)
        }

        // If actions are strings, we assume they are shell commands and try to execute them
        // directly ourselves.
        var self = this
        if (typeof(result.prepare) === 'string') {
          var psh = shellWrap(result.prepare)
          prepare = function(context, cb) {
            forkProc(self.workingDir, psh.cmd, psh.args, cb)
          }
        }

        if (typeof(result.test) === 'string') {
          var tsh = shellWrap(result.test)
          test = function(context, cb) {
            forkProc(self.workingDir, tsh.cmd, tsh.args, cb)
          }
        }
        // Execution actions may be delegated to functions.
        // This is useful for example for multi-step things like in Python where a virtual env must be set up.
        // Functions are of signature function(context, cb)
        // We assume the function handles any necessary shell interaction and sending of update messages.
        if (typeof(result.prepare) === 'function') {
          prepare = result.prepare
        }
        if (typeof(result.test) === 'function') {
          test = result.test
        }

        prepare(context, function(prepareExitCode) {
          if (prepareExitCode !== 0) {
            // Prepare step failed
            var msg = "Prepare failed; exit code: " + prepareExitCode
            striderMessage(msg)
            console.log(msg)
            console.log("stdmergedBuffer: %s", stdmergedBuffer);
            return complete(prepareExitCode, null)
          }
          test(context, function(testExitCode) {
            var msg = "Test exit code: " + testExitCode
            console.log(msg)
            striderMessage(msg)
            if (testExitCode !== 0 || data.job_type !== "TEST_AND_DEPLOY") {
              // Test step failed or no deploy requested
              complete(testExitCode, null)
            } else {
              // Test step passed and deploy requested
              deploy(context, function(deployExitCode) {
                complete(testExitCode, deployExitCode)
              })
            }

          })
        })

        // TODO: Deploy (e.g. Heroku, dotCloud)

        // TODO: Teardown phase (database shutdown, etc)

      }

    )
  })
}

// Add an array of detection rules to head of list
function addDetectionRules(r) {
  detectionRules = r.concat(detectionRules)
}

// Add a single detection rule to head of list
function addDetectionRule(r) {
  detectionRules = [r].concat(detectionRules)
}

module.exports = function(context, cb) {
  // XXX test purposes
  detectionRules = DEFAULT_PROJECT_TYPE_RULES
  // Build a worker context, which is a stripped-down version of the webapp context
  var workerContext = {
    addDetectionRule:addDetectionRule,
    addDetectionRules:addDetectionRules,
    config: context.config,
    emitter: context.emitter,
    extdir: context.extdir,
  }

  Step(
    function() {
      context.loader.initExtensions(context.extdir, "worker", workerContext, null, this)
    },
    function(err, initialized) {
      registerEvents(context.emitter)
      console.log("Strider Simple Worker ready")
      cb(null, null)
    }
  )

}
<|endoftext|>


<commit_before>
/* Config
--------------------------------------------------------------------------- */

var casper_options = {
    clientScripts: ['./bower_components/jquery/dist/jquery.min.js']
  // , verbose: true
  // , logLevel: 'debug'
};

var casper  = require('casper').create(casper_options),
    utils   = require('utils');

/* Helper functions
--------------------------------------------------------------------------- */

function clearScreen() {
    for (var i = 10 - 1; i >= 0; i--) {
        console.log('\n');
    }
}

function drawLine() {
    console.log('-------------------------------------------------------');
}

function title(text) {
    return casper.echo('# ' + text, 'PARAMETER');
}

function info(text) {
    return casper.echo(text, 'INFO');
}

function warn(text) {
    return casper.echo(text, 'WARNING');
}

/* Testing start
--------------------------------------------------------------------------- */

casper.start(casper.cli.get('url'));

casper.then( function () {
    clearScreen();
    drawLine();
    this.echo('Suitmedia web validation');

    if ( !casper.cli.get('url') ) {
        warn('No URL specified.');
        info('eg: casperjs check.js --url=http://suitmedia.com');
        drawLine();
        casper.exit();
    }

    this.echo('link: ' + this.getCurrentUrl());
    drawLine();
});

/* Viewport declaration
--------------------------------------------------------------------------- */
casper.then( function () {
    var meta_vp = this.evaluate( function () {
        var vp = $('meta[name="viewport"]');

        if ( vp.length ) {
            return {
                'status': true,
                'content': vp.attr('content')
            };
        } else {
            return {
                'status': false
            };
        }

    });

    title('Meta viewport');

    if ( meta_vp.status ) {
        info('  - is specified: ' + meta_vp.content);
    } else if ( !meta_vp.status ) {
        warn('  - is not specified');
    }
});

/* Favicon
--------------------------------------------------------------------------- */

casper.then( function () {
    var favicon = this.evaluate( function () {
        var _favicon = $('link[rel*="icon"]');

        if ( _favicon.length ) {
            return {
                'status': true,
                'content': _favicon.attr('href')
            };
        }

        return false;
    });

    title('Favicon');

    if ( favicon.status ) {
        info('  - Favicon is specified: ' + favicon.content);
    } else {
        warn('  - No favicon specified');
    }
});

/* ARIA Landmark
--------------------------------------------------------------------------- */

casper.then( function () {
    var Landmark = this.evaluate( function () {

        var role = {

            banner: function () {
                var _banner = $('[role="banner"]');

                if ( _banner.length ) return true;
                return false;
            },

            main: function () {
                var _main = $('[role="main"]');

                if ( _main.length ) return true;
                return false;
            },

            contentinfo: function () {
                var _contentinfo = $('[role="contentinfo"]');

                if ( _contentinfo.length ) return true;
                return false;
            },

            navigation: function () {
                var _navigation = $('[role="navigation"]');

                if ( _navigation.length ) return true;
                return false;
            },

            search: function () {
                var _search = $('[role="search"]');

                if ( _search.length ) return true;
                return false;
            }

        };

        return {
            'banner': role.banner(),
            'main': role.main(),
            'contentinfo': role.contentinfo(),
            'navigation': role.navigation(),
            'search': role.search()
        };

    });

    title('ARIA Landmark');

    if ( Landmark.banner ) {
        info('  - Banner');
    } else {
        warn('  - Banner');
    }

    if ( Landmark.main ) {
        info('  - Main');
    } else {
        warn('  - Main');
    }

    if ( Landmark.contentinfo ) {
        info('  - Contentinfo');
    } else {
        warn('  - Contentinfo');
    }

    if ( Landmark.navigation ) {
        info('  - Navigation');
    } else {
        warn('  - Navigation');
    }

    if ( Landmark.search ) {
        info('  - Search');
    } else {
        warn('  - Search');
    }
});

/* i18n
--------------------------------------------------------------------------- */

casper.then( function () {
    var i18n = this.evaluate( function () {

        var check = {

            encode: function () {
                var meta1 = $('meta[charset]');
                var meta2 = $('meta[content*="charset"]');

                if ( meta1.length ) {
                    return {
                        'status': true,
                        'content': meta1.attr('charset')
                    };
                } else if ( meta2.length ) {
                    return {
                        'status': true,
                        'content': meta2.attr('content')
                    };
                } else {
                    return {
                        'status': false
                    };
                }

            },

            language: function () {
                var lang = $('html[lang]');

                if ( lang.length ) {
                    return {
                        'status': true,
                        'content': lang.attr('lang')
                    };
                }

                return false;
            }

        };

        return {
            'encode': check.encode(),
            'language': check.language()
        };
    });

    title('Internationalization');

    if ( i18n.encode.status ) {
        info('  - Character encoding is specified: ' + i18n.encode.content);
    } else {
        warn('  - Character encoding is not specified');
    }

    if ( i18n.language.status ) {
        info('  - Language is specified: ' + i18n.language.content);
    } else {
        warn('  - Language is not specified');
    }
});

/* Testing end
--------------------------------------------------------------------------- */

casper.then( function () {
    drawLine();
});

/* Run test
--------------------------------------------------------------------------- */

casper.run();<commit_msg>add missing casper.open<commit_after>
/* Config
--------------------------------------------------------------------------- */

var casper_options = {
    clientScripts: ['./bower_components/jquery/dist/jquery.min.js']
  // , verbose: true
  // , logLevel: 'debug'
};

var casper  = require('casper').create(casper_options),
    utils   = require('utils');

/* Helper functions
--------------------------------------------------------------------------- */

function clearScreen() {
    for (var i = 10 - 1; i >= 0; i--) {
        console.log('\n');
    }
}

function drawLine() {
    console.log('-------------------------------------------------------');
}

function title(text) {
    return casper.echo('# ' + text, 'PARAMETER');
}

function info(text) {
    return casper.echo(text, 'INFO');
}

function warn(text) {
    return casper.echo(text, 'WARNING');
}

/* Testing start
--------------------------------------------------------------------------- */

casper.start(casper.cli.get('url'));

casper.then( function () {
    clearScreen();
    drawLine();
    this.echo('Suitmedia web validation');

    if ( !casper.cli.get('url') ) {
        warn('No URL specified.');
        info('eg: casperjs check.js --url=http://suitmedia.com');
        drawLine();
        casper.exit();
    }

    this.echo('link: ' + this.getCurrentUrl());
    drawLine();
});

casper.open(casper.cli.get('url'));

/* Viewport declaration
--------------------------------------------------------------------------- */
casper.then( function () {
    var meta_vp = this.evaluate( function () {
        var vp = $('meta[name="viewport"]');

        if ( vp.length ) {
            return {
                'status': true,
                'content': vp.attr('content')
            };
        } else {
            return {
                'status': false
            };
        }

    });

    title('Meta viewport');

    if ( meta_vp.status ) {
        info('  - is specified: ' + meta_vp.content);
    } else if ( !meta_vp.status ) {
        warn('  - is not specified');
    }
});

/* Favicon
--------------------------------------------------------------------------- */

casper.then( function () {
    var favicon = this.evaluate( function () {
        var _favicon = $('link[rel*="icon"]');

        if ( _favicon.length ) {
            return {
                'status': true,
                'content': _favicon.attr('href')
            };
        }

        return false;
    });

    title('Favicon');

    if ( favicon.status ) {
        info('  - Favicon is specified: ' + favicon.content);
    } else {
        warn('  - No favicon specified');
    }
});

/* ARIA Landmark
--------------------------------------------------------------------------- */

casper.then( function () {
    var Landmark = this.evaluate( function () {

        var role = {

            banner: function () {
                var _banner = $('[role="banner"]');

                if ( _banner.length ) return true;
                return false;
            },

            main: function () {
                var _main = $('[role="main"]');

                if ( _main.length ) return true;
                return false;
            },

            contentinfo: function () {
                var _contentinfo = $('[role="contentinfo"]');

                if ( _contentinfo.length ) return true;
                return false;
            },

            navigation: function () {
                var _navigation = $('[role="navigation"]');

                if ( _navigation.length ) return true;
                return false;
            },

            search: function () {
                var _search = $('[role="search"]');

                if ( _search.length ) return true;
                return false;
            }

        };

        return {
            'banner': role.banner(),
            'main': role.main(),
            'contentinfo': role.contentinfo(),
            'navigation': role.navigation(),
            'search': role.search()
        };

    });

    title('ARIA Landmark');

    if ( Landmark.banner ) {
        info('  - Banner');
    } else {
        warn('  - Banner');
    }

    if ( Landmark.main ) {
        info('  - Main');
    } else {
        warn('  - Main');
    }

    if ( Landmark.contentinfo ) {
        info('  - Contentinfo');
    } else {
        warn('  - Contentinfo');
    }

    if ( Landmark.navigation ) {
        info('  - Navigation');
    } else {
        warn('  - Navigation');
    }

    if ( Landmark.search ) {
        info('  - Search');
    } else {
        warn('  - Search');
    }
});

/* i18n
--------------------------------------------------------------------------- */

casper.then( function () {
    var i18n = this.evaluate( function () {

        var check = {

            encode: function () {
                var meta1 = $('meta[charset]');
                var meta2 = $('meta[content*="charset"]');

                if ( meta1.length ) {
                    return {
                        'status': true,
                        'content': meta1.attr('charset')
                    };
                } else if ( meta2.length ) {
                    return {
                        'status': true,
                        'content': meta2.attr('content')
                    };
                } else {
                    return {
                        'status': false
                    };
                }

            },

            language: function () {
                var lang = $('html[lang]');

                if ( lang.length ) {
                    return {
                        'status': true,
                        'content': lang.attr('lang')
                    };
                }

                return false;
            }

        };

        return {
            'encode': check.encode(),
            'language': check.language()
        };
    });

    title('Internationalization');

    if ( i18n.encode.status ) {
        info('  - Character encoding is specified: ' + i18n.encode.content);
    } else {
        warn('  - Character encoding is not specified');
    }

    if ( i18n.language.status ) {
        info('  - Language is specified: ' + i18n.language.content);
    } else {
        warn('  - Language is not specified');
    }
});

/* Testing end
--------------------------------------------------------------------------- */

casper.then( function () {
    drawLine();
});

/* Run test
--------------------------------------------------------------------------- */

casper.run();<|endoftext|>


<commit_before>'use strict';

/* Controllers */

var myappControllers = angular.module('myappControllers', []);

myappControllers.controller('LoginCtrl', ['$scope', '$routeParams', '$location', '$http', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $http, $timeout, $filter, Partage, Utils) {

    Partage.hideStuff = false;

    $scope.partage = Partage; // Share data between controllers
    $scope.message = "";
    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    // INIT
    $scope.init = function(){
        // CLEAN SESSION
    	$http.get(serverHost + '/logout', {
         withCredentials: true
         }).success(function(data){
            // do something ?
         });
    }

    $scope.init();

}]);

myappControllers.controller('MenuCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket) {

    $scope.partage = Partage; // share data between controllers
    $scope.room = "";

    // INIT
    $scope.init = function(){

    }

    $scope.logout = function () {
        Partage.room = "";
        mySocket.disconnect();
        $location.path('/login');
    }

    $scope.change = function (){
        $location.path('/lobby');
        $('#lobbyInput').focus();
    }

    $scope.init();

}]);

myappControllers.controller('LobbyCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils) {

    Partage.hideStuff = true;

    $scope.partage = Partage; // share data between controllers
    $scope.room = "";
    $scope.hideStuff = Partage.hideStuff;

    // INIT
    $scope.init = function(){
        $('#lobbyInput').focus();
    }

    $scope.join = function (){
        if($.trim($scope.room) != ""){
            Partage.room = $scope.room;
            $location.path('/chat/'+Partage.room);
        }
        else{
            $scope.room = "";
        }
    }

    $scope.init();

}]);

myappControllers.controller('ChatCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket', 'MessageStorage',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket, MessageStorage) {

    Partage.hideStuff = false;

    $scope.partage = Partage; // share data between controllers

    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    $scope.room = "";
    $scope.hideStuff = Partage.hideStuff;

	$scope.message = ""; // chat message
	$scope.messages = []; // chat messages
    $scope.usersList = [];
    $scope.numUsers = 0;
    $scope.numUsersLabel = "";
    $scope.fullUrl = "";

    // INIT
    $scope.init = function(){
        // we need the room name in the route
        if($.trim($routeParams.room) == ""){
            $location.path('/lobby');
        }

        // init room from the route
        Partage.room = $routeParams.room;
        $scope.room = $routeParams.room;

        $scope.fullUrl = $location.absUrl();
        $scope.displayUsersNumber();
        $('#chatInput').focus();

        // NEW USER ENTER
        MessageStorage.setId(Partage.room); // set storage id
        $scope.messages = MessageStorage.get(); // chat messages from localStorage
        mySocket.emit('NEW_USER', {id: $scope.partage.id, provider: $scope.partage.provider, username: $scope.partage.username , room: $scope.partage.room, avatar: $scope.partage.avatar});

		// destroy socket when leaving the chat
		$scope.$on('$destroy', function () {
              Partage.room = "";
			  mySocket.disconnect();
              mySocket.connect();
		});

        mySocket.on('LOGIN', function(data){
            $('#chatInput').focus();
            $scope.numUsers = data.numUsers;
            $scope.displayUsersNumber();
            // scroll bottom if necessary
                if(!Partage.isScrolling){
                    Partage.isScrolling = true;
                    $("html, body").animate(
                    { scrollTop: $(document).height() },
                    1000,
                        function(){
                            Partage.isScrolling = false;
                        }
                    );
                }
            // console.log('LOGIN ' + data.numUsers);
        });

        mySocket.on('NEW_USER', function(data){
            $scope.numUsers = data.numUsers;
            $scope.usersList.push({username: data.username, room: data.room, avatar: data.avatar});
            $scope.displayUsersNumber();
            var mome = new Date().getTime();
            $scope.messages.push({username: 'System', avatar: 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', message: '### ' + data.username + ' just joined the room', moment: mome});
			// console.log('NEW USER ' + data.username + ' ' + data.room + ' ' + data.avatar);
        });

        mySocket.on('USER_LEFT', function(data){
            $scope.numUsers = data.numUsers;
            $scope.displayUsersNumber();
            var mome = new Date().getTime();
            $scope.messages.push({username: 'System', avatar: 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', message: '### ' + data.username + ' just left the room', moment: mome});
            // console.log('USER LEFT ' + data.username + ' ' + data.room);
        });

		mySocket.on('NEW_MESSAGE', function(data){
            // limit to 100 messages (by room) in the localStorage
            if($scope.messages.length == 100){
                 $scope.messages.shift();
            }
            // console.log ('get a new message');
            $scope.messages.push(data);
            // store the messages list in the localStorage (max = 100)
            MessageStorage.put($scope.messages);

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}

        });

    }

    $scope.displayUsersNumber = function(){
        if($scope.numUsers == 1){
            $scope.numUsersLabel = "lonely user";
        }
        else{
            $scope.numUsersLabel = $scope.numUsers + " users";
        }
    }

    $scope.init();

}]);

myappControllers.controller('MessageCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket) {

    $scope.partage = Partage; // Share data between controllers
	$scope.message = ""; // chat message
    $scope.uploadme = "";

    // INIT
    $scope.init = function(){
		$('#chatInput').focus();
    }

	$scope.sendMessage = function (){
		if($.trim($scope.message)!= ""){
			// console.log('send message')
            var mome = new Date().getTime();
			mySocket.emit('NEW_MESSAGE', {message : $scope.message, moment: mome});

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}
		}
		$scope.message = "";
		$('#chatInput').focus();
	}

    $scope.$watch('uploadme', function(oldValue, newValue){
        if(newValue != ""){
            var mome = new Date().getTime();
            mySocket.emit('NEW_MESSAGE', {message : '', moment: mome});
        }
    });

    $scope.init();

}]);
<commit_msg>send image<commit_after>'use strict';

/* Controllers */

var myappControllers = angular.module('myappControllers', []);

myappControllers.controller('LoginCtrl', ['$scope', '$routeParams', '$location', '$http', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $http, $timeout, $filter, Partage, Utils) {

    Partage.hideStuff = false;

    $scope.partage = Partage; // Share data between controllers
    $scope.message = "";
    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    // INIT
    $scope.init = function(){
        // CLEAN SESSION
    	$http.get(serverHost + '/logout', {
         withCredentials: true
         }).success(function(data){
            // do something ?
         });
    }

    $scope.init();

}]);

myappControllers.controller('MenuCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket) {

    $scope.partage = Partage; // share data between controllers
    $scope.room = "";

    // INIT
    $scope.init = function(){

    }

    $scope.logout = function () {
        Partage.room = "";
        mySocket.disconnect();
        $location.path('/login');
    }

    $scope.change = function (){
        $location.path('/lobby');
        $('#lobbyInput').focus();
    }

    $scope.init();

}]);

myappControllers.controller('LobbyCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils) {

    Partage.hideStuff = true;

    $scope.partage = Partage; // share data between controllers
    $scope.room = "";
    $scope.hideStuff = Partage.hideStuff;

    // INIT
    $scope.init = function(){
        $('#lobbyInput').focus();
    }

    $scope.join = function (){
        if($.trim($scope.room) != ""){
            Partage.room = $scope.room;
            $location.path('/chat/'+Partage.room);
        }
        else{
            $scope.room = "";
        }
    }

    $scope.init();

}]);

myappControllers.controller('ChatCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket', 'MessageStorage',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket, MessageStorage) {

    Partage.hideStuff = false;

    $scope.partage = Partage; // share data between controllers

    $scope.errors = []; // errors
    $scope.alert = {text:'', type:''}; // alert

    $scope.room = "";
    $scope.hideStuff = Partage.hideStuff;

	$scope.message = ""; // chat message
	$scope.messages = []; // chat messages
    $scope.usersList = [];
    $scope.numUsers = 0;
    $scope.numUsersLabel = "";
    $scope.fullUrl = "";

    // INIT
    $scope.init = function(){
        // we need the room name in the route
        if($.trim($routeParams.room) == ""){
            $location.path('/lobby');
        }

        // init room from the route
        Partage.room = $routeParams.room;
        $scope.room = $routeParams.room;

        $scope.fullUrl = $location.absUrl();
        $scope.displayUsersNumber();
        $('#chatInput').focus();

        // NEW USER ENTER
        MessageStorage.setId(Partage.room); // set storage id
        $scope.messages = MessageStorage.get(); // chat messages from localStorage
        mySocket.emit('NEW_USER', {id: $scope.partage.id, provider: $scope.partage.provider, username: $scope.partage.username , room: $scope.partage.room, avatar: $scope.partage.avatar});

		// destroy socket when leaving the chat
		$scope.$on('$destroy', function () {
              Partage.room = "";
			  mySocket.disconnect();
              mySocket.connect();
		});

        mySocket.on('LOGIN', function(data){
            $('#chatInput').focus();
            $scope.numUsers = data.numUsers;
            $scope.displayUsersNumber();
            // scroll bottom if necessary
                if(!Partage.isScrolling){
                    Partage.isScrolling = true;
                    $("html, body").animate(
                    { scrollTop: $(document).height() },
                    1000,
                        function(){
                            Partage.isScrolling = false;
                        }
                    );
                }
            // console.log('LOGIN ' + data.numUsers);
        });

        mySocket.on('NEW_USER', function(data){
            $scope.numUsers = data.numUsers;
            $scope.usersList.push({username: data.username, room: data.room, avatar: data.avatar});
            $scope.displayUsersNumber();
            var mome = new Date().getTime();
            $scope.messages.push({username: 'System', avatar: 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', message: '### ' + data.username + ' just joined the room', moment: mome});
			// console.log('NEW USER ' + data.username + ' ' + data.room + ' ' + data.avatar);
        });

        mySocket.on('USER_LEFT', function(data){
            $scope.numUsers = data.numUsers;
            $scope.displayUsersNumber();
            var mome = new Date().getTime();
            $scope.messages.push({username: 'System', avatar: 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', message: '### ' + data.username + ' just left the room', moment: mome});
            // console.log('USER LEFT ' + data.username + ' ' + data.room);
        });

		mySocket.on('NEW_MESSAGE', function(data){
            // limit to 100 messages (by room) in the localStorage
            if($scope.messages.length == 100){
                 $scope.messages.shift();
            }
            // console.log ('get a new message');
            $scope.messages.push(data);
            // store the messages list in the localStorage (max = 100)
            MessageStorage.put($scope.messages);

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}

        });

    }

    $scope.displayUsersNumber = function(){
        if($scope.numUsers == 1){
            $scope.numUsersLabel = "lonely user";
        }
        else{
            $scope.numUsersLabel = $scope.numUsers + " users";
        }
    }

    $scope.init();

}]);

myappControllers.controller('MessageCtrl', ['$scope', '$routeParams', '$location', '$timeout', '$filter', 'Partage', 'Utils', 'mySocket',
 function($scope, $routeParams, $location, $timeout, $filter, Partage, Utils, mySocket) {

    $scope.partage = Partage; // Share data between controllers
	$scope.message = ""; // chat message
    $scope.uploadme = "";

    // INIT
    $scope.init = function(){
		$('#chatInput').focus();
    }

	$scope.sendMessage = function (){
		if($.trim($scope.message)!= ""){
			// console.log('send message')
            var mome = new Date().getTime();
			mySocket.emit('NEW_MESSAGE', {message : $scope.message, moment: mome});

			// scroll bottom if necessary
			if(!Partage.isScrolling){
				Partage.isScrolling = true;
				$("html, body").animate(
				{ scrollTop: $(document).height() },
				1000,
					function(){
						Partage.isScrolling = false;
					}
				);
			}
		}
		$scope.message = "";
		$('#chatInput').focus();
	}

    $scope.$watch('uploadme', function(oldValue, newValue){
        if(newValue != ""){
            var mome = new Date().getTime();
            mySocket.emit('NEW_MESSAGE', {message : '', moment: mome});
        }
    });

    $scope.init();

}]);
<|endoftext|>


<commit_before>import React, { PropTypes } from 'react'
import {
  matchContext as matchContextType
} from './PropTypes'

class MatchProvider extends React.Component {
  static propTypes = {
    match: PropTypes.any,
    children: PropTypes.node
  }

  static childContextTypes = {
    match: matchContextType.isRequired
  }

  constructor(props) {
    super(props)

    this.state = {
      parent: props.match,
      matches: []
    }
  }

  addMatch = match => {
    const { matches } = this.state

    this.setState({
      matches: matches.concat([match])
    })
  }

  removeMatch = match => {
    const { matches } = this.state

    this.setState({
      matches: matches.splice(matches.indexOf(match), 1)
    })
  }

  getChildContext() {
    return {
      match: {
        addMatch: this.addMatch,
        removeMatch: this.removeMatch,

        parent: this.state.parent,
        matches: this.state.matches,

        matchFound: () => this.state.matches.length > 0
      }
    }
  }

  render() {
    return this.props.children
  }
}

export default MatchProvider
<commit_msg>Use an instance variable for match tracking.<commit_after>import React, { PropTypes } from 'react'
import {
  matchContext as matchContextType
} from './PropTypes'

class MatchProvider extends React.Component {
  static propTypes = {
    match: PropTypes.any,
    children: PropTypes.node
  }

  static childContextTypes = {
    match: matchContextType.isRequired
  }

  constructor(props) {
    super(props)

    this.state = {
      parent: props.match
    }

    // When accessing state along this.context, it appears any enqueued setState
    // calls don't get dispatched on the server (renderToString) and something
    // like Miss will never work. This works around that by using a simple
    // instance variable. Sorry it's not idiomatic React!
    this.matches = []
  }

  addMatch = match => {
    this.matches = this.matches.concat([match])
  }

  removeMatch = match => {
    this.matches = this.matches.splice(this.matches.indexOf(match), 1)
  }

  getChildContext() {
    return {
      match: {
        addMatch: this.addMatch,
        removeMatch: this.removeMatch,

        parent: this.state.parent,
        matches: this.matches,

        matchFound: () => this.matches.length > 0
      }
    }
  }

  render() {
    return this.props.children
  }
}

export default MatchProvider
<|endoftext|>


<commit_before>define( function( require, exports, module ) {
	'use strict';

	// Get module dependencies.
	var CodeInspection = brackets.getModule( 'language/CodeInspection' ),
		DocumentManager = brackets.getModule( 'document/DocumentManager' ),
		EditorManager = brackets.getModule( 'editor/EditorManager' ),
		ExtensionUtils = brackets.getModule( 'utils/ExtensionUtils' ),
		PreferencesManager = brackets.getModule( 'preferences/PreferencesManager' ),

		// Extension Modules.
		CommandRunner = require( 'modules/CommandRunner' ),
		Events = require( 'modules/Events' ),
		Parsers = require( 'modules/Parsers' ),

		// Setup extension.
		preferences = PreferencesManager.getExtensionPrefs( 'mikaeljorhult.bracketsPHPLintTools' ),

		// Variables.
		basePath = ExtensionUtils.getModulePath( module, 'vendor/' ).replace( ' ', '\\ ' ),
		paths = {
			phpcpd: 'php ' + basePath + 'phpcpd/phpcpd.phar',
			phpcs: 'php ' + basePath + 'phpcs/phpcs.phar',
			phpl: 'php',
			phpmd: 'php ' + basePath + 'phpmd/phpmd.phar'
		};

	// Lint path and return found errors.
	function getErrors( fullPath ) {
		var filePath = normalizePath( fullPath ),
			phpcsStandards = concatenateArray( preferences.get( 'phpcs-standards' ), ' --standard=' ),
			phpmdRulesets = concatenateArray( preferences.get( 'phpmd-rulesets' ) ),

			// Commands.
			phpcpdCommand = paths.phpcpd + ' ' + filePath,
			phpcsCommand = paths.phpcs + phpcsStandards + ' ' + filePath,
			phplCommand = paths.phpl + ' -d display_errors=1 -d error_reporting=-1 -l ' + filePath,
			phpmdCommand = paths.phpmd + ' ' + filePath + ' text ' + phpmdRulesets;

		// Pass command to parser.
		Parsers.run( {
			name: 'phpcs',
			command: phpcsCommand
		} );

		Parsers.run( {
			name: 'phpcpd',
			command: phpcpdCommand
		} );

		Parsers.run( {
			name: 'phpl',
			command: phplCommand
		} );

		Parsers.run( {
			name: 'phpmd',
			command: phpmdCommand
		} );
	}

	// Concatenate a array of values to a comma separated string.
	function concatenateArray( valueArray, prefix ) {
		var returnValue = false;

		if ( valueArray.length > 0 ) {
			returnValue = ( prefix !== undefined ? prefix : '' ) + valueArray.join( ',' );
		}

		return returnValue;
	}

	// Escape paths on different systems.
	function normalizePath( fullPath ) {
		if ( brackets.platform === 'win' ) {
			fullPath = '"' + fullPath + '"';
		} else {
			fullPath = fullPath.replace( new RegExp( ' ', 'g' ), '\\ ' );
		}

		return fullPath;
	}

	// Register event listeners.
	function registerEvents() {
		// Test for PHP.
		CommandRunner.run( 'php -v', function( data ) {
			var phpAvailable = data.indexOf( 'PHP' ) > -1;

			// Save PHP state
			preferences.set( 'php-available', phpAvailable );
			preferences.save();

			// Only register linters and listeners if PHP is available on machine.
			if ( phpAvailable ) {
				// Register linting service.
				CodeInspection.register( 'php', {
					name: 'PHP Copy/Paste Detector',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpcpd
						};
					}
				} );

				CodeInspection.register( 'php', {
					name: 'PHP CodeSniffer',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpcs
						};
					}
				} );

				CodeInspection.register( 'php', {
					name: 'PHP Lint',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpl
						};
					}
				} );

				CodeInspection.register( 'php', {
					name: 'PHP Mess Detector',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpmd
						};
					}
				} );

				// Run CodeInspection when a file is saved or other file get focus.
				$( DocumentManager ).on( 'documentSaved.phpLintTools', function( event, fileEntry ) {
					getErrors( fileEntry.file.fullPath );
				} );

				$( EditorManager ).on( 'activeEditorChange', function( event, editor ) {
					getErrors( editor.document.file.fullPath );
				} );
			}
		} );
	}

	// Register event listeners.
	if ( CommandRunner.initialized() ) {
		registerEvents();
	} else {
		Events.subscribe( 'node:connected', registerEvents );
	}
} );<commit_msg>ParserManager should return paths for testing.<commit_after>define( function( require, exports, module ) {
	'use strict';

	// Get module dependencies.
	var CodeInspection = brackets.getModule( 'language/CodeInspection' ),
		DocumentManager = brackets.getModule( 'document/DocumentManager' ),
		EditorManager = brackets.getModule( 'editor/EditorManager' ),
		ExtensionUtils = brackets.getModule( 'utils/ExtensionUtils' ),
		PreferencesManager = brackets.getModule( 'preferences/PreferencesManager' ),

		// Extension Modules.
		CommandRunner = require( 'modules/CommandRunner' ),
		Events = require( 'modules/Events' ),
		Parsers = require( 'modules/Parsers' ),

		// Setup extension.
		preferences = PreferencesManager.getExtensionPrefs( 'mikaeljorhult.bracketsPHPLintTools' ),

		// Variables.
		basePath = ExtensionUtils.getModulePath( module, 'vendor/' ).replace( ' ', '\\ ' ),
		paths = {
			phpcpd: 'php ' + basePath + 'phpcpd/phpcpd.phar',
			phpcs: 'php ' + basePath + 'phpcs/phpcs.phar',
			phpl: 'php',
			phpmd: 'php ' + basePath + 'phpmd/phpmd.phar'
		};

	// Lint path and return found errors.
	function getErrors( fullPath ) {
		var filePath = normalizePath( fullPath ),
			phpcsStandards = concatenateArray( preferences.get( 'phpcs-standards' ), ' --standard=' ),
			phpmdRulesets = concatenateArray( preferences.get( 'phpmd-rulesets' ) ),

			// Commands.
			phpcpdCommand = paths.phpcpd + ' ' + filePath,
			phpcsCommand = paths.phpcs + phpcsStandards + ' ' + filePath,
			phplCommand = paths.phpl + ' -d display_errors=1 -d error_reporting=-1 -l ' + filePath,
			phpmdCommand = paths.phpmd + ' ' + filePath + ' text ' + phpmdRulesets;

		// Pass command to parser.
		Parsers.run( {
			name: 'phpcs',
			command: phpcsCommand
		} );

		Parsers.run( {
			name: 'phpcpd',
			command: phpcpdCommand
		} );

		Parsers.run( {
			name: 'phpl',
			command: phplCommand
		} );

		Parsers.run( {
			name: 'phpmd',
			command: phpmdCommand
		} );
	}

	// Concatenate a array of values to a comma separated string.
	function concatenateArray( valueArray, prefix ) {
		var returnValue = false;

		if ( valueArray.length > 0 ) {
			returnValue = ( prefix !== undefined ? prefix : '' ) + valueArray.join( ',' );
		}

		return returnValue;
	}

	// Escape paths on different systems.
	function normalizePath( fullPath ) {
		if ( brackets.platform === 'win' ) {
			fullPath = '"' + fullPath + '"';
		} else {
			fullPath = fullPath.replace( new RegExp( ' ', 'g' ), '\\ ' );
		}

		return fullPath;
	}

	// Register event listeners.
	function registerEvents() {
		// Test for PHP.
		CommandRunner.run( 'php -v', function( data ) {
			var phpAvailable = data.indexOf( 'PHP' ) > -1;

			// Save PHP state
			preferences.set( 'php-available', phpAvailable );
			preferences.save();

			// Only register linters and listeners if PHP is available on machine.
			if ( phpAvailable ) {
				// Register linting service.
				CodeInspection.register( 'php', {
					name: 'PHP Copy/Paste Detector',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpcpd
						};
					}
				} );

				CodeInspection.register( 'php', {
					name: 'PHP CodeSniffer',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpcs
						};
					}
				} );

				CodeInspection.register( 'php', {
					name: 'PHP Lint',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpl
						};
					}
				} );

				CodeInspection.register( 'php', {
					name: 'PHP Mess Detector',
					scanFile: function() {
						return {
							errors: Parsers.errors().phpmd
						};
					}
				} );

				// Run CodeInspection when a file is saved or other file get focus.
				$( DocumentManager ).on( 'documentSaved.phpCodeQualityTools', function( event, fileEntry ) {
					getErrors( fileEntry.file.fullPath );
				} );

				$( EditorManager ).on( 'activeEditorChange.phpCodeQualityTools', function( event, editor ) {
					getErrors( editor.document.file.fullPath );
				} );
			}
		} );
	}

	// Register event listeners.
	if ( CommandRunner.initialized() ) {
		registerEvents();
	} else {
		Events.subscribe( 'node:connected', registerEvents );
	}

	return {
		_paths: paths
	}
} );<|endoftext|>