<commit_before>/*
 * Copyright (c) 2015 Steven Soloff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

var _ = require('underscore');
var evaluateController = require('../../controllers/evaluate-controller');
var fs = require('fs');
var ja = require('json-assert');
var path = require('path');

describe('evaluateController', function () {
    var req;
    var res;
    var request;
    var response;

    function isJsonEqual(actual, expected) {
        if ((_.has(actual, 'expression') || _.has(actual, 'error')) &&
                (_.has(expected, 'expression') || _.has(expected, 'error'))) {
            return ja.isEqual(expected, actual, true);
        }
    }

    beforeEach(function () {
        jasmine.addCustomEqualityTester(isJsonEqual);

        request = {
            expression: {
                text: '3d6+4'
            },
            randomNumberGenerator: {
                name: 'constantMax'
            }
        };
        req = {
            body: request
        };

        res = {
            json: function (json) {
                response = json;
                return this;
            },
            status: function () {
                return this;
            }
        };
        spyOn(res, 'json').and.callThrough();
        spyOn(res, 'status').and.callThrough();

        evaluateController.setKeys(
            fs.readFileSync(path.join(__dirname, '../../test/private-key.pem')),
            fs.readFileSync(path.join(__dirname, '../../test/public-key.pem'))
        );
    });

    describe('.evaluate', function () {
        describe('when expression is well-formed', function () {
            it('should respond with the expression result', function () {
                evaluateController.evaluate(req, res);

                expect(res.status).toHaveBeenCalledWith(200);
                expect(response.content).toEqual({
                    expression: {
                        canonicalText: 'sum(roll(3, d6)) + 4',
                        text: '3d6+4'
                    },
                    expressionResult: {
                        text: '[sum([roll(3, d6) -> [6, 6, 6]]) -> 18] + 4',
                        value: 22
                    },
                    randomNumberGenerator: {
                        name: 'constantMax'
                    }
                });
            });

            it('should respond with a signature', function () {
                expect(response).toBeSigned();
            });
        });

        describe('when expression is malformed', function () {
            it('should respond with an error', function () {
                request.expression.text = '<<INVALID>>';

                evaluateController.evaluate(req, res);

                expect(res.status).toHaveBeenCalledWith(200);
                expect(response.content).toEqual({
                    error: {
                        message: ja.matchType('string')
                    }
                });
            });

            it('should respond with a signature', function () {
                expect(response).toBeSigned();
            });
        });

        describe('specifying a random number generator', function () {
            describe('when the random number generator is not specified', function () {
                it('should use the default random number generator', function () {
                    delete request.randomNumberGenerator;

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.randomNumberGenerator.name).toBe('uniform');
                });
            });

            describe('when the uniform random number generator is specified', function () {
                it('should use the uniform random number generator', function () {
                    request.randomNumberGenerator.name = 'uniform';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.randomNumberGenerator.name).toBe('uniform');
                    expect(response.content.expressionResult.value).toBeGreaterThan(2 + 4);
                    expect(response.content.expressionResult.value).toBeLessThan(19 + 4);
                });
            });

            describe('when the constantMax random number generator is specified', function () {
                it('should use the constantMax random number generator', function () {
                    request.randomNumberGenerator.name = 'constantMax';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.randomNumberGenerator.name).toBe('constantMax');
                    expect(response.content.expressionResult.value).toBe(22);
                });
            });

            describe('when an unknown random number generator is specified', function () {
                it('should respond with an error', function () {
                    request.randomNumberGenerator.name = '<<UNKNOWN>>';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.error).toBeDefined();
                });
            });
        });

        describe('evaluating an expression whose result value is not a finite number', function () {
            describe('when result value is not a number', function () {
                it('should respond with an error', function () {
                    request.expression.text = 'd6';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.error).toBeDefined();
                });
            });

            describe('when result value is NaN', function () {
                it('should respond with an error', function () {
                    request.expression.text = 'round(d6)';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.error).toBeDefined();
                });
            });
        });
    });
});

<commit_msg>Fix interdependent unit tests.<commit_after>/*
 * Copyright (c) 2015 Steven Soloff
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

'use strict';

var _ = require('underscore');
var evaluateController = require('../../controllers/evaluate-controller');
var fs = require('fs');
var ja = require('json-assert');
var path = require('path');

describe('evaluateController', function () {
    var req;
    var res;
    var request;
    var response;

    function isJsonEqual(actual, expected) {
        if ((_.has(actual, 'expression') || _.has(actual, 'error')) &&
                (_.has(expected, 'expression') || _.has(expected, 'error'))) {
            return ja.isEqual(expected, actual, true);
        }
    }

    beforeEach(function () {
        jasmine.addCustomEqualityTester(isJsonEqual);

        request = {
            expression: {
                text: '3d6+4'
            },
            randomNumberGenerator: {
                name: 'constantMax'
            }
        };
        req = {
            body: request
        };

        response = null
        res = {
            json: function (json) {
                response = json;
                return this;
            },
            status: function () {
                return this;
            }
        };
        spyOn(res, 'json').and.callThrough();
        spyOn(res, 'status').and.callThrough();

        evaluateController.setKeys(
            fs.readFileSync(path.join(__dirname, '../../test/private-key.pem')),
            fs.readFileSync(path.join(__dirname, '../../test/public-key.pem'))
        );
    });

    describe('.evaluate', function () {
        describe('when expression is well-formed', function () {
            it('should respond with the expression result', function () {
                evaluateController.evaluate(req, res);

                expect(res.status).toHaveBeenCalledWith(200);
                expect(response.content).toEqual({
                    expression: {
                        canonicalText: 'sum(roll(3, d6)) + 4',
                        text: '3d6+4'
                    },
                    expressionResult: {
                        text: '[sum([roll(3, d6) -> [6, 6, 6]]) -> 18] + 4',
                        value: 22
                    },
                    randomNumberGenerator: {
                        name: 'constantMax'
                    }
                });
            });

            it('should respond with a signature', function () {
                evaluateController.evaluate(req, res);

                expect(response).toBeSigned();
            });
        });

        describe('when expression is malformed', function () {
            it('should respond with an error', function () {
                request.expression.text = '<<INVALID>>';

                evaluateController.evaluate(req, res);

                expect(res.status).toHaveBeenCalledWith(200);
                expect(response.content).toEqual({
                    error: {
                        message: ja.matchType('string')
                    }
                });
            });

            it('should respond with a signature', function () {
                evaluateController.evaluate(req, res);

                expect(response).toBeSigned();
            });
        });

        describe('specifying a random number generator', function () {
            describe('when the random number generator is not specified', function () {
                it('should use the default random number generator', function () {
                    delete request.randomNumberGenerator;

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.randomNumberGenerator.name).toBe('uniform');
                });
            });

            describe('when the uniform random number generator is specified', function () {
                it('should use the uniform random number generator', function () {
                    request.randomNumberGenerator.name = 'uniform';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.randomNumberGenerator.name).toBe('uniform');
                    expect(response.content.expressionResult.value).toBeGreaterThan(2 + 4);
                    expect(response.content.expressionResult.value).toBeLessThan(19 + 4);
                });
            });

            describe('when the constantMax random number generator is specified', function () {
                it('should use the constantMax random number generator', function () {
                    request.randomNumberGenerator.name = 'constantMax';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.randomNumberGenerator.name).toBe('constantMax');
                    expect(response.content.expressionResult.value).toBe(22);
                });
            });

            describe('when an unknown random number generator is specified', function () {
                it('should respond with an error', function () {
                    request.randomNumberGenerator.name = '<<UNKNOWN>>';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.error).toBeDefined();
                });
            });
        });

        describe('evaluating an expression whose result value is not a finite number', function () {
            describe('when result value is not a number', function () {
                it('should respond with an error', function () {
                    request.expression.text = 'd6';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.error).toBeDefined();
                });
            });

            describe('when result value is NaN', function () {
                it('should respond with an error', function () {
                    request.expression.text = 'round(d6)';

                    evaluateController.evaluate(req, res);

                    expect(res.status).toHaveBeenCalledWith(200);
                    expect(response.content.error).toBeDefined();
                });
            });
        });
    });
});

<|endoftext|>


<commit_before>/**
 * @param model{morpheus.SelectionModel}
 */
morpheus.ScentedSearch = function (model, positions, isVertical, scrollbar,
                                   heatMap) {
  morpheus.AbstractCanvas.call(this, false);
  this.model = model;
  this.positions = positions;
  this.isVertical = isVertical;
  this.scrollbar = scrollbar;
  this.heatMap = heatMap;
  this.searchIndices = [];
  scrollbar.decorator = this;
  var _this = this;
  var mouseMove = function (e) {
    var indices = _this.getSearchIndices(e);

    document.body.style.cursor = indices.length === 0 ? 'default' : 'pointer';
    scrollbar.canvas.style.cursor = indices.length === 0 ? 'default' : 'pointer';
    var tipOptions = {
      event: e,
      heatMapLens: indices.length >= 0
    };
    if (isVertical) {
      heatMap.setToolTip(indices.length >= 0 ? indices : null,
        -1, tipOptions);
    } else {
      heatMap.setToolTip(-1, indices.length >= 0 ? indices
        : null, tipOptions);
    }

  };
  var mouseExit = function (e) {
    // need to set body cursor b/c mouse can be partially on the scroll bar,
    // but the canvas cursor has no effect
    document.body.style.cursor = 'default';
    scrollbar.canvas.style.cursor = 'default';
    heatMap.setToolTip(-1, -1, {event: e});
  };
  var showPopup = function (e) {
    e.preventDefault();
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    if (e.stopImmediatePropagation) {
      e.stopImmediatePropagation();
    }
    morpheus.Popup
    .showPopup(
      [

        {
          name: 'Selection To Top',
          checked: heatMap.getToolbar().isSelectionOnTop(!isVertical),
          disabled: isVertical ? heatMap.getProject().getRowSelectionModel()
            .count() === 0 : heatMap.getProject().getColumnSelectionModel()
            .count() === 0
        },
        {
          name: 'New Heat Map (' + morpheus.Util.COMMAND_KEY + 'X)'
        }],
      {
        x: e.pageX,
        y: e.pageY
      },
      e.target,
      function (event, item) {
        if (item === 'Selection To Top') {
          heatMap.getToolbar().setSelectionOnTop({
            isColumns: !isVertical,
            isOnTop: !heatMap.getToolbar().isSelectionOnTop(!isVertical),
            updateButtonStatus: true
          });
        } else {
          morpheus.HeatMap.showTool(new morpheus.NewHeatMapTool(),
            heatMap);
        }
      });
    return false;
  };
  $(scrollbar.canvas).on('mousemove.morpheus', mouseMove).on('mouseout.morpheus', mouseExit).on('contextmenu.morpheus', showPopup);

};

morpheus.ScentedSearch.LINE_HEIGHT = 3.5;
morpheus.ScentedSearch.prototype = {
  mouseMovedIndex: -1,
  getIndex: function (event) {
    var pix = morpheus.CanvasUtil.getMousePos(event.target, event);
    var val = pix[this.isVertical ? 'y' : 'x'];
    return this.getIndexForPix(val);
  },
  getSearchIndices: function (event) {
    var pix = morpheus.CanvasUtil.getMousePos(event.target, event);
    var val = pix[this.isVertical ? 'y' : 'x'];
    return this.getSearchIndicesForPix(val);
  },
  getSearchIndicesForPix: function (pix) {
    var indices = this.searchIndices;
    if (indices == null) {
      return [];
    }
    var scale = this.scale;
    var tolerance = morpheus.ScentedSearch.LINE_HEIGHT;
    var matches = [];
    for (var i = 0, length = indices.length; i < length; i++) {
      var midVal = this.positions.getPosition(indices[i]) * scale;
      if (Math.abs(midVal - pix) <= tolerance) {
        matches.push(indices[i]);
      }
    }
    return matches;
  },
  getIndexForPix: function (pix) {
    var indices = this.searchIndices;
    if (indices == null) {
      return -1;
    }
    var tolerance = morpheus.ScentedSearch.LINE_HEIGHT;
    if (this.mouseMovedIndex > 0) {
      var midVal = this.positions
        .getPosition(indices[this.mouseMovedIndex])
        * scale;
      if (Math.abs(midVal - pix) <= tolerance) {
        return this.mouseMovedIndex;
      }
    }
    var low = 0;
    var scale = this.scale;
    var high = indices.length - 1;

    while (low <= high) {
      var mid = (low + high) >> 1;
      var midVal = this.positions.getPosition(indices[mid]) * scale;
      var cmp = 0;
      if (Math.abs(midVal - pix) <= tolerance) {
        cmp = 0;
      } else if (midVal < pix) {
        cmp = -1; // Neither val is NaN, thisVal is smaller
      } else if (midVal > pix) {
        cmp = 1; // Neither val is NaN, thisVal is larger
      }
      if (cmp < 0)
        low = mid + 1;
      else if (cmp > 0)
        high = mid - 1;
      else
        return mid; // key found
    }
    return -1; // -(low + 1); // key not found.

  },
  tap: function (position) {
    var val = position[this.isVertical ? 'y' : 'x'];
    var index = this.getIndexForPix(val);
    this.scrollbar.canvas.style.cursor = index < 0 ? 'default' : 'pointer';
    if (index >= 0) {
      if (this.isVertical) {
        this.heatMap.scrollTop(this.positions
        .getPosition(this.searchIndices[index]));
      } else {
        this.heatMap.scrollLeft(this.positions
        .getPosition(this.searchIndices[index]));
      }
      return true;
    }
    return false;
  },
  update: function () {
    this.searchIndices = this.model.getViewIndices().values().sort(
      function (a, b) {
        return a < b ? -1 : 1;
      });
  },
  draw: function (clip, context) {
    var width = this.scrollbar.getUnscaledWidth();
    var height = this.scrollbar.getUnscaledHeight();
    var availableLength = ((this.isVertical ? height : width))
      - morpheus.ScentedSearch.LINE_HEIGHT;
    this.scale = availableLength
      / (this.positions.getPosition(this.positions.getLength() - 1) + this.positions
      .getItemSize(this.positions.getLength() - 1));
    context.strokeStyle = 'rgb(106,137,177)';
    context.fillStyle = 'rgb(182,213,253)';
    context.lineWidth = 1;
    this.drawIndices(context, this.searchIndices);
    this.drawHoverMatchingValues(context);
  },
  drawHoverMatchingValues: function (context) {
    var heatmap = this.heatMap;
    context.fillStyle = 'black';
    if (heatmap.mousePositionOptions
      && heatmap.mousePositionOptions.name != null) {
      var isColumns = !this.isVertical;
      var track = heatmap.getTrack(heatmap.mousePositionOptions.name,
        isColumns);
      if (track == null) {
        return;
      }
      if (track.settings.highlightMatchingValues) {
        var hoverIndex = isColumns ? heatmap.getProject()
          .getHoverColumnIndex() : heatmap.getProject()
          .getHoverRowIndex();
        if (hoverIndex === -1) {
          return;
        }
        var vector = track.getVector();
        var value = vector.getValue(hoverIndex);
        var valueToModelIndices = track.getFullVector().getProperties()
        .get(morpheus.VectorKeys.VALUE_TO_INDICES);
        if (!valueToModelIndices) {
          var fullVector = track.getFullVector();
          valueToModelIndices = morpheus.VectorUtil
          .createValueToIndicesMap(fullVector);
          fullVector.getProperties().set(
            morpheus.VectorKeys.VALUE_TO_INDICES,
            valueToModelIndices);

        }
        var modelIndices = valueToModelIndices.get(value);
        if (modelIndices == null) {
          console.log('valueToModelIndices error');
          return;
        }
        var scale = this.scale;
        var lineLength = !this.isVertical ? this.scrollbar
          .getUnscaledHeight() : this.scrollbar
          .getUnscaledWidth();
        var isVertical = this.isVertical;
        var positions = this.positions;
        var project = heatmap.getProject();
        for (var i = 0, length = modelIndices.length; i < length; i++) {
          var modelIndex = modelIndices[i];
          var index = isVertical ? project
            .convertModelRowIndexToView(modelIndex) : project
            .convertModelColumnIndexToView(modelIndex);
          if (index === -1) {
            continue;
          }
          var pix = positions.getPosition(index) * scale;
          if (isVertical) {
            context.fillRect(0, pix, lineLength,
              morpheus.ScentedSearch.LINE_HEIGHT);
          } else {
            context.fillRect(pix, 0,
              morpheus.ScentedSearch.LINE_HEIGHT, lineLength);

          }
        }
      }

    }
  },
  drawIndices: function (context, highlightedIndices) {
    var scale = this.scale;
    var lineLength = !this.isVertical ? this.scrollbar.getUnscaledHeight()
      : this.scrollbar.getUnscaledWidth();

    var isVertical = this.isVertical;
    var positions = this.positions;
    for (var i = 0, length = highlightedIndices.length; i < length; i++) {
      var index = highlightedIndices[i];
      var pix = positions.getPosition(index) * scale;
      if (isVertical) {
        context.beginPath();
        context.rect(0, pix, lineLength,
          morpheus.ScentedSearch.LINE_HEIGHT);
        context.fill();
        context.stroke();

      } else {
        context.beginPath();
        context.rect(pix, 0, morpheus.ScentedSearch.LINE_HEIGHT,
          lineLength);
        context.fill();
        context.stroke();
      }
    }

  }
};
morpheus.Util.extend(morpheus.ScentedSearch, morpheus.AbstractCanvas);
<commit_msg>tick colors<commit_after>/**
 * @param model{morpheus.SelectionModel}
 */
morpheus.ScentedSearch = function (model, positions, isVertical, scrollbar,
                                   heatMap) {
  morpheus.AbstractCanvas.call(this, false);
  this.model = model;
  this.positions = positions;
  this.isVertical = isVertical;
  this.scrollbar = scrollbar;
  this.heatMap = heatMap;
  this.searchIndices = [];
  scrollbar.decorator = this;
  var _this = this;
  var mouseMove = function (e) {
    var indices = _this.getSearchIndices(e);

    document.body.style.cursor = indices.length === 0 ? 'default' : 'pointer';
    scrollbar.canvas.style.cursor = indices.length === 0 ? 'default' : 'pointer';
    var tipOptions = {
      event: e,
      heatMapLens: indices.length >= 0
    };
    if (isVertical) {
      heatMap.setToolTip(indices.length >= 0 ? indices : null,
        -1, tipOptions);
    } else {
      heatMap.setToolTip(-1, indices.length >= 0 ? indices
        : null, tipOptions);
    }

  };
  var mouseExit = function (e) {
    // need to set body cursor b/c mouse can be partially on the scroll bar,
    // but the canvas cursor has no effect
    document.body.style.cursor = 'default';
    scrollbar.canvas.style.cursor = 'default';
    heatMap.setToolTip(-1, -1, {event: e});
  };
  var showPopup = function (e) {
    e.preventDefault();
    if (e.stopPropagation) {
      e.stopPropagation();
    }
    if (e.stopImmediatePropagation) {
      e.stopImmediatePropagation();
    }
    morpheus.Popup
    .showPopup(
      [

        {
          name: 'Selection To Top',
          checked: heatMap.getToolbar().isSelectionOnTop(!isVertical),
          disabled: isVertical ? heatMap.getProject().getRowSelectionModel()
            .count() === 0 : heatMap.getProject().getColumnSelectionModel()
            .count() === 0
        },
        {
          name: 'New Heat Map (' + morpheus.Util.COMMAND_KEY + 'X)'
        }],
      {
        x: e.pageX,
        y: e.pageY
      },
      e.target,
      function (event, item) {
        if (item === 'Selection To Top') {
          heatMap.getToolbar().setSelectionOnTop({
            isColumns: !isVertical,
            isOnTop: !heatMap.getToolbar().isSelectionOnTop(!isVertical),
            updateButtonStatus: true
          });
        } else {
          morpheus.HeatMap.showTool(new morpheus.NewHeatMapTool(),
            heatMap);
        }
      });
    return false;
  };
  $(scrollbar.canvas).on('mousemove.morpheus', mouseMove).on('mouseout.morpheus', mouseExit).on('contextmenu.morpheus', showPopup);

};

morpheus.ScentedSearch.LINE_HEIGHT = 3.5;
morpheus.ScentedSearch.prototype = {
  mouseMovedIndex: -1,
  getIndex: function (event) {
    var pix = morpheus.CanvasUtil.getMousePos(event.target, event);
    var val = pix[this.isVertical ? 'y' : 'x'];
    return this.getIndexForPix(val);
  },
  getSearchIndices: function (event) {
    var pix = morpheus.CanvasUtil.getMousePos(event.target, event);
    var val = pix[this.isVertical ? 'y' : 'x'];
    return this.getSearchIndicesForPix(val);
  },
  getSearchIndicesForPix: function (pix) {
    var indices = this.searchIndices;
    if (indices == null) {
      return [];
    }
    var scale = this.scale;
    var tolerance = morpheus.ScentedSearch.LINE_HEIGHT;
    var matches = [];
    for (var i = 0, length = indices.length; i < length; i++) {
      var midVal = this.positions.getPosition(indices[i]) * scale;
      if (Math.abs(midVal - pix) <= tolerance) {
        matches.push(indices[i]);
      }
    }
    return matches;
  },
  getIndexForPix: function (pix) {
    var indices = this.searchIndices;
    if (indices == null) {
      return -1;
    }
    var tolerance = morpheus.ScentedSearch.LINE_HEIGHT;
    if (this.mouseMovedIndex > 0) {
      var midVal = this.positions
        .getPosition(indices[this.mouseMovedIndex])
        * scale;
      if (Math.abs(midVal - pix) <= tolerance) {
        return this.mouseMovedIndex;
      }
    }
    var low = 0;
    var scale = this.scale;
    var high = indices.length - 1;

    while (low <= high) {
      var mid = (low + high) >> 1;
      var midVal = this.positions.getPosition(indices[mid]) * scale;
      var cmp = 0;
      if (Math.abs(midVal - pix) <= tolerance) {
        cmp = 0;
      } else if (midVal < pix) {
        cmp = -1; // Neither val is NaN, thisVal is smaller
      } else if (midVal > pix) {
        cmp = 1; // Neither val is NaN, thisVal is larger
      }
      if (cmp < 0)
        low = mid + 1;
      else if (cmp > 0)
        high = mid - 1;
      else
        return mid; // key found
    }
    return -1; // -(low + 1); // key not found.

  },
  tap: function (position) {
    var val = position[this.isVertical ? 'y' : 'x'];
    var index = this.getIndexForPix(val);
    this.scrollbar.canvas.style.cursor = index < 0 ? 'default' : 'pointer';
    if (index >= 0) {
      if (this.isVertical) {
        this.heatMap.scrollTop(this.positions
        .getPosition(this.searchIndices[index]));
      } else {
        this.heatMap.scrollLeft(this.positions
        .getPosition(this.searchIndices[index]));
      }
      return true;
    }
    return false;
  },
  update: function () {
    this.searchIndices = this.model.getViewIndices().values().sort(
      function (a, b) {
        return a < b ? -1 : 1;
      });
  },
  draw: function (clip, context) {
    var width = this.scrollbar.getUnscaledWidth();
    var height = this.scrollbar.getUnscaledHeight();
    var availableLength = ((this.isVertical ? height : width))
      - morpheus.ScentedSearch.LINE_HEIGHT;
    this.scale = availableLength
      / (this.positions.getPosition(this.positions.getLength() - 1) + this.positions
      .getItemSize(this.positions.getLength() - 1));
    context.fillStyle = morpheus.ScentedSearch.TICK_COLOR;
    context.lineWidth = 1;
    this.drawIndices(context, this.searchIndices);
    this.drawHoverMatchingValues(context);
  },
  drawHoverMatchingValues: function (context) {
    var heatmap = this.heatMap;
    context.fillStyle = morpheus.ScentedSearch.MATCHING_VALUES_TICK_COLOR;
    if (heatmap.mousePositionOptions
      && heatmap.mousePositionOptions.name != null) {
      var isColumns = !this.isVertical;
      var track = heatmap.getTrack(heatmap.mousePositionOptions.name,
        isColumns);
      if (track == null) {
        return;
      }
      if (track.settings.highlightMatchingValues) {
        var hoverIndex = isColumns ? heatmap.getProject()
        .getHoverColumnIndex() : heatmap.getProject()
        .getHoverRowIndex();
        if (hoverIndex === -1) {
          return;
        }
        var vector = track.getVector();
        var value = vector.getValue(hoverIndex);
        var valueToModelIndices = track.getFullVector().getProperties()
        .get(morpheus.VectorKeys.VALUE_TO_INDICES);
        if (!valueToModelIndices) {
          var fullVector = track.getFullVector();
          valueToModelIndices = morpheus.VectorUtil
          .createValueToIndicesMap(fullVector);
          fullVector.getProperties().set(
            morpheus.VectorKeys.VALUE_TO_INDICES,
            valueToModelIndices);

        }
        var modelIndices = valueToModelIndices.get(value);
        if (modelIndices == null) {
          console.log('valueToModelIndices error');
          return;
        }
        var scale = this.scale;
        var lineLength = !this.isVertical ? this.scrollbar
        .getUnscaledHeight() : this.scrollbar
        .getUnscaledWidth();
        var isVertical = this.isVertical;
        var positions = this.positions;
        var project = heatmap.getProject();
        for (var i = 0, length = modelIndices.length; i < length; i++) {
          var modelIndex = modelIndices[i];
          var index = isVertical ? project
          .convertModelRowIndexToView(modelIndex) : project
          .convertModelColumnIndexToView(modelIndex);
          if (index === -1) {
            continue;
          }
          var pix = positions.getPosition(index) * scale;
          if (isVertical) {
            context.fillRect(0, pix, lineLength,
              morpheus.ScentedSearch.LINE_HEIGHT);
          } else {
            context.fillRect(pix, 0,
              morpheus.ScentedSearch.LINE_HEIGHT, lineLength);

          }
        }
      }

    }
  },
  drawIndices: function (context, highlightedIndices) {
    var scale = this.scale;
    var lineLength = !this.isVertical ? this.scrollbar.getUnscaledHeight()
      : this.scrollbar.getUnscaledWidth();

    var isVertical = this.isVertical;
    var positions = this.positions;
    for (var i = 0, length = highlightedIndices.length; i < length; i++) {
      var index = highlightedIndices[i];
      var pix = positions.getPosition(index) * scale;
      if (isVertical) {
        context.beginPath();
        context.rect(0, pix, lineLength,
          morpheus.ScentedSearch.LINE_HEIGHT);
        context.fill();
        //  context.stroke();

      } else {
        context.beginPath();
        context.rect(pix, 0, morpheus.ScentedSearch.LINE_HEIGHT,
          lineLength);
        context.fill();
        //context.stroke();
      }
    }

  }
};
morpheus.Util.extend(morpheus.ScentedSearch, morpheus.AbstractCanvas);
morpheus.ScentedSearch.MATCHING_VALUES_TICK_COLOR = 'black';
morpheus.ScentedSearch.TICK_COLOR = '#3182bd';
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const stream = require('stream');
const fetch = require('node-fetch');
const { Error: DiscordError, TypeError } = require('../errors');
const { browser } = require('../util/Constants');
const Util = require('../util/Util');

/**
 * The DataResolver identifies different objects and tries to resolve a specific piece of information from them.
 * @private
 */
class DataResolver {
  constructor() {
    throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
  }

  /**
   * Data that can be resolved to give an invite code. This can be:
   * * An invite code
   * * An invite URL
   * @typedef {string} InviteResolvable
   */

  /**
   * Resolves InviteResolvable to an invite code.
   * @param {InviteResolvable} data The invite resolvable to resolve
   * @returns {string}
   */
  static resolveInviteCode(data) {
    const inviteRegex = /discord(?:(?:app)?\.com\/invite|\.gg(?:\/invite)?)\/([\w-]{2,255})/i;
    const match = inviteRegex.exec(data);
    if (match && match[1]) return match[1];
    return data;
  }

  /**
   * Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
   * @param {BufferResolvable|Base64Resolvable} image The image to be resolved
   * @returns {Promise<?string>}
   */
  static async resolveImage(image) {
    if (!image) return null;
    if (typeof image === 'string' && image.startsWith('data:')) {
      return image;
    }
    const file = await this.resolveFileAsBuffer(image);
    return DataResolver.resolveBase64(file);
  }

  /**
   * Data that resolves to give a Base64 string, typically for image uploading. This can be:
   * * A Buffer
   * * A base64 string
   * @typedef {Buffer|string} Base64Resolvable
   */

  /**
   * Resolves a Base64Resolvable to a Base 64 image.
   * @param {Base64Resolvable} data The base 64 resolvable you want to resolve
   * @returns {?string}
   */
  static resolveBase64(data) {
    if (Buffer.isBuffer(data)) return `data:image/jpg;base64,${data.toString('base64')}`;
    return data;
  }

  /**
   * Data that can be resolved to give a Buffer. This can be:
   * * A Buffer
   * * The path to a local file
   * * A URL
   * @typedef {string|Buffer} BufferResolvable
   */

  /**
   * @external Stream
   * @see {@link https://nodejs.org/api/stream.html}
   */

  /**
   * Resolves a BufferResolvable to a Buffer or a Stream.
   * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
   * @returns {Promise<Buffer|Stream>}
   */
  static async resolveFile(resource) {
    if (!browser && Buffer.isBuffer(resource)) return resource;
    if (browser && resource instanceof ArrayBuffer) return Util.convertToBuffer(resource);
    if (resource instanceof stream.Readable) return resource;

    if (typeof resource === 'string') {
      if (/^https?:\/\//.test(resource)) {
        const res = await fetch(resource);
        return browser ? res.blob() : res.body;
      } else if (!browser) {
        return new Promise((resolve, reject) => {
          const file = path.resolve(resource);
          fs.stat(file, (err, stats) => {
            if (err) return reject(err);
            if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file));
            return resolve(fs.createReadStream(file));
          });
        });
      }
    }

    throw new TypeError('REQ_RESOURCE_TYPE');
  }

  /**
   * Resolves a BufferResolvable to a Buffer.
   * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
   * @returns {Promise<Buffer>}
   */
  static async resolveFileAsBuffer(resource) {
    const file = await this.resolveFile(resource);
    if (Buffer.isBuffer(file)) return file;

    const buffers = [];
    for await (const data of file) buffers.push(data);
    return Buffer.concat(buffers);
  }
}

module.exports = DataResolver;
<commit_msg>feat: add Blob support for browser (#4338)<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const stream = require('stream');
const fetch = require('node-fetch');
const { Error: DiscordError, TypeError } = require('../errors');
const { browser } = require('../util/Constants');
const Util = require('../util/Util');

/**
 * The DataResolver identifies different objects and tries to resolve a specific piece of information from them.
 * @private
 */
class DataResolver {
  constructor() {
    throw new Error(`The ${this.constructor.name} class may not be instantiated.`);
  }

  /**
   * Data that can be resolved to give an invite code. This can be:
   * * An invite code
   * * An invite URL
   * @typedef {string} InviteResolvable
   */

  /**
   * Resolves InviteResolvable to an invite code.
   * @param {InviteResolvable} data The invite resolvable to resolve
   * @returns {string}
   */
  static resolveInviteCode(data) {
    const inviteRegex = /discord(?:(?:app)?\.com\/invite|\.gg(?:\/invite)?)\/([\w-]{2,255})/i;
    const match = inviteRegex.exec(data);
    if (match && match[1]) return match[1];
    return data;
  }

  /**
   * Resolves a Base64Resolvable, a string, or a BufferResolvable to a Base 64 image.
   * @param {BufferResolvable|Base64Resolvable} image The image to be resolved
   * @returns {Promise<?string>}
   */
  static async resolveImage(image) {
    if (!image) return null;
    if (typeof image === 'string' && image.startsWith('data:')) {
      return image;
    }
    const file = await this.resolveFileAsBuffer(image);
    return DataResolver.resolveBase64(file);
  }

  /**
   * Data that resolves to give a Base64 string, typically for image uploading. This can be:
   * * A Buffer
   * * A base64 string
   * @typedef {Buffer|string} Base64Resolvable
   */

  /**
   * Resolves a Base64Resolvable to a Base 64 image.
   * @param {Base64Resolvable} data The base 64 resolvable you want to resolve
   * @returns {?string}
   */
  static resolveBase64(data) {
    if (Buffer.isBuffer(data)) return `data:image/jpg;base64,${data.toString('base64')}`;
    return data;
  }

  /**
   * Data that can be resolved to give a Buffer. This can be:
   * * A Buffer
   * * The path to a local file
   * * A URL
   * @typedef {string|Buffer} BufferResolvable
   */

  /**
   * @external Stream
   * @see {@link https://nodejs.org/api/stream.html}
   */

  /**
   * Resolves a BufferResolvable to a Buffer or a Stream.
   * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
   * @returns {Promise<Buffer|Stream>}
   */
  static async resolveFile(resource) {
    if (!browser && Buffer.isBuffer(resource)) return resource;
    if (browser && resource instanceof ArrayBuffer) return Util.convertToBuffer(resource);
    // eslint-disable-next-line no-undef
    if (browser && resource instanceof Blob) return resource;
    if (resource instanceof stream.Readable) return resource;

    if (typeof resource === 'string') {
      if (/^https?:\/\//.test(resource)) {
        const res = await fetch(resource);
        return browser ? res.blob() : res.body;
      } else if (!browser) {
        return new Promise((resolve, reject) => {
          const file = path.resolve(resource);
          fs.stat(file, (err, stats) => {
            if (err) return reject(err);
            if (!stats.isFile()) return reject(new DiscordError('FILE_NOT_FOUND', file));
            return resolve(fs.createReadStream(file));
          });
        });
      }
    }

    throw new TypeError('REQ_RESOURCE_TYPE');
  }

  /**
   * Resolves a BufferResolvable to a Buffer.
   * @param {BufferResolvable|Stream} resource The buffer or stream resolvable to resolve
   * @returns {Promise<Buffer>}
   */
  static async resolveFileAsBuffer(resource) {
    const file = await this.resolveFile(resource);
    if (Buffer.isBuffer(file)) return file;

    const buffers = [];
    for await (const data of file) buffers.push(data);
    return Buffer.concat(buffers);
  }
}

module.exports = DataResolver;
<|endoftext|>


<commit_before>describe('simulatorController', function() {
  beforeEach(angular.mock.module('app'));

  var $controller;
  var that = this;
  beforeEach(function() {
    that.video = document.createElement('video');
    that.videoRight = document.createElement('video');
    spyOn(ons, 'isWebView').and.returnValue(true);
    inject(function(_$controller_){
      // The injector unwraps the underscores (_) from around the parameter names when matching
      $controller = _$controller_;
    })
  });

  it('should exist', function() {
    expect($controller).toBeDefined();
  });

  describe('resizeVideo', function() {
    it('should resize videos to window size', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });

      controller.resizeVideo();
      expect($scope.video.height).toEqual(window.innerHeight - 44);
      expect($scope.videoRight.height).toEqual(window.innerHeight - 44);
      expect($scope.video.width).toEqual(window.innerWidth);
      expect($scope.videoRight.width).toEqual(window.innerWidth);

    });
  });

  describe('switchOnAR', function() {
    it('should copy video attributes and play video', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });

      $scope.video.src = 'http://clips.vorwaerts-gmbh.de/VfE_html5.mp4';
      $scope.video.play();
      $scope.video.className = 'stereo';
      expect($scope.video.paused).toEqual(false);

      controller.switchOnAR();

      expect($scope.videoRight.paused).toEqual(false);
      expect($scope.videoRight.src).toEqual('http://clips.vorwaerts-gmbh.de/VfE_html5.mp4');
      expect($scope.videoRight.className).toEqual('stereo');
    });
  });

  describe('switchOffAR', function() {
    it('should pause videoRight and remove classname', function() {
      var $scope = {
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });
      $scope.videoRight.play();
      $scope.videoRight.className = 'stereo';
      expect($scope.videoRight.paused).toEqual(false);

      controller.switchOffAR();
      expect($scope.videoRight.paused).toEqual(true);
      expect($scope.videoRight.className).toEqual('');
    });
  });

  describe('stopVideo', function() {
    it('should pause videos and remove sources', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });
      $scope.videoRight.src = 'http://clips.vorwaerts-gmbh.de/VfE_html5.mp4';
      $scope.video.src = 'http://clips.vorwaerts-gmbh.de/VfE_html5.mp4';
      $scope.video.play();
      $scope.videoRight.play();

      expect($scope.video.paused).toEqual(false);
      expect($scope.videoRight.paused).toEqual(false);

      controller.stopVideo();

      expect($scope.video.paused).toEqual(true);
      expect($scope.videoRight.paused).toEqual(true);

      // empty src return test page (verified with video tag log)
      expect($scope.video.src).toEqual('http://localhost:9876/context.html');
      expect($scope.videoRight.src).toEqual('http://localhost:9876/context.html');
    });
  });

});
<commit_msg>add SimulatorController tests<commit_after>describe('simulatorController', function() {
  beforeEach(angular.mock.module('app'));

  var $controller;
  var that = this;
  beforeEach(function() {
    that.video = document.createElement('video');
    that.videoRight = document.createElement('video');
    spyOn(ons, 'isWebView').and.returnValue(true);
    inject(function(_$controller_){
      // The injector unwraps the underscores (_) from around the parameter names when matching
      $controller = _$controller_;
    })
  });

  it('should exist', function() {
    expect($controller).toBeDefined();
  });

  describe('resizeVideo', function() {
    it('should resize videos to window size', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });

      controller.resizeVideo();
      expect($scope.video.height).toEqual(window.innerHeight - 44);
      expect($scope.videoRight.height).toEqual(window.innerHeight - 44);
      expect($scope.video.width).toEqual(window.innerWidth);
      expect($scope.videoRight.width).toEqual(window.innerWidth);

    });
  });

  describe('switchOnAR', function() {
    it('should copy video attributes and play video', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });

      $scope.video.src = 'http://clips.vorwaerts-gmbh.de/VfE_html5.mp4';
      $scope.video.play();
      $scope.video.className = 'stereo';
      expect($scope.video.paused).toEqual(false);

      controller.switchOnAR();

      expect($scope.videoRight.paused).toEqual(false);
      expect($scope.videoRight.src).toEqual('http://clips.vorwaerts-gmbh.de/VfE_html5.mp4');
      expect($scope.videoRight.className).toEqual('stereo');
    });
  });

  describe('switchOffAR', function() {
    it('should pause videoRight and remove classname', function() {
      var $scope = {
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });
      $scope.videoRight.play();
      $scope.videoRight.className = 'stereo';
      expect($scope.videoRight.paused).toEqual(false);

      controller.switchOffAR();
      expect($scope.videoRight.paused).toEqual(true);
      expect($scope.videoRight.className).toEqual('');
    });
  });

  describe('stopVideo', function() {
    it('should pause videos and remove sources', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight
      };
      var controller = $controller('simulatorController', { $scope: $scope });
      $scope.videoRight.src = 'http://clips.vorwaerts-gmbh.de/VfE_html5.mp4';
      $scope.video.src = 'http://clips.vorwaerts-gmbh.de/VfE_html5.mp4';
      $scope.video.play();
      $scope.videoRight.play();

      expect($scope.video.paused).toEqual(false);
      expect($scope.videoRight.paused).toEqual(false);

      controller.stopVideo();

      expect($scope.video.paused).toEqual(true);
      expect($scope.videoRight.paused).toEqual(true);

      // empty src return test page (verified with video tag log)
      expect($scope.video.src).toEqual('http://localhost:9876/context.html');
      expect($scope.videoRight.src).toEqual('http://localhost:9876/context.html');
    });
  });

  describe('$scope.updateFilter', function() {
    it('should update active filter on video in stereo mode', function() {
      var $scope = {
        video: that.video,
        videoRight: that.videoRight,
        selectedFilter: 'test',
        armode: true
      };
      var controller = $controller('simulatorController', { $scope: $scope });

      $scope.updateFilter();

      expect($scope.video.className).toEqual('stereo testEffect');
      expect($scope.videoRight.className).toEqual('stereo testEffect');
    });

    it('should update active filter on video', function() {
      var $scope = {
        video: that.video,
        selectedFilter: 'test',
        armode: false
      };
      var controller = $controller('simulatorController', { $scope: $scope });

      $scope.updateFilter();

      expect($scope.video.className).toEqual('testEffect');
    });
  });

});
<|endoftext|>


<commit_before>import { warningNotification } from "../actions/notifications";
import { charonAPIAddress } from "./globals";
import { NEW_NARRATIVE } from "../actions/types";

// eslint-disable-next-line
export const getNarrative = (dispatch, datasetPathName) => {
  const errorHandler = (e) => {
    this.props.dispatch(warningNotification({message: "Failed to get narrative from server"}));
    console.error(e);
  };
  const xmlHttp = new XMLHttpRequest();
  xmlHttp.onload = () => {
    if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
      dispatch({
        type: NEW_NARRATIVE,
        blocks: JSON.parse(xmlHttp.responseText)
      });
    } else {
      errorHandler(xmlHttp);
    }
  };
  xmlHttp.onerror = errorHandler;
  const name = datasetPathName.replace(/^\//, '').replace(/\//, '_');
  xmlHttp.open("get", `${charonAPIAddress}request=narrative&name=${name}`, true);
  xmlHttp.send(null);
};
<commit_msg>fix bug in getNarrative 404 handling<commit_after>import { warningNotification } from "../actions/notifications";
import { charonAPIAddress } from "./globals";
import { NEW_NARRATIVE } from "../actions/types";

// eslint-disable-next-line
export const getNarrative = (dispatch, datasetPathName) => {
  const errorHandler = (e) => {
    // dispatch(warningNotification({message: "Failed to get narrative from server"}));
    console.error("Failed to get narrative from server");
    console.error(e);
  };
  const xmlHttp = new XMLHttpRequest();
  xmlHttp.onload = () => {
    if (xmlHttp.readyState === 4 && xmlHttp.status === 200) {
      dispatch({
        type: NEW_NARRATIVE,
        blocks: JSON.parse(xmlHttp.responseText)
      });
    } else {
      errorHandler(xmlHttp);
    }
  };
  xmlHttp.onerror = errorHandler;
  const name = datasetPathName.replace(/^\//, '').replace(/\//, '_');
  xmlHttp.open("get", `${charonAPIAddress}request=narrative&name=${name}`, true);
  xmlHttp.send(null);
};
<|endoftext|>


<commit_before>import Client from './client';
import getScope from './get-scope.ts';
import getArgs from './get-args';

export default async (sentry, error, apiUrl, configFiles) => {
  // Do not report errors in development
  if (!process.pkg) {
    return;
  }

  let user = null;
  let team = null;

  try {
    const { token } = configFiles.readAuthConfigFile();
    const { currentTeam } = configFiles.readConfigFile();
    const client = new Client({ apiUrl, token, currentTeam, debug: false });
    ({ user, team } = await getScope(client));
  } catch (err) {
    // We can safely ignore this, as the error
    // reporting works even without this metadata attached.
  }

  if (user || team) {
    sentry.withScope(scope => {
      if (user) {
        const spec = {
          email: user.email,
          id: user.uid
        };

        if (user.username) {
          spec.username = user.username;
        }

        if (user.name) {
          spec.name = user.name;
        }

        scope.setUser(spec);
      }

      if (team) {
        scope.setTag('currentTeam', team.id);
      }

      // Report process.argv without sensitive data
      let args
      try {
        args = getArgs(process.argv.slice(2), {});
      } catch (_) {}

      if (args) {
        const flags = ['--env', '--build-env', '--token']
        for (const flag of flags) {
          if (args[flag]) args[flag] = 'REDACTED';
        }
        if (args._[0].startsWith('secret') && args._[1] === 'add' && args._[3]) {
          args._[3] = 'REDACTED';
        }
        scope.setExtra('args', args);
      }

      sentry.captureException(error);
    });
  } else {
    sentry.captureException(error);
  }

  const client = sentry.getCurrentHub().getClient();

  if (client) {
    await client.close();
  }
};
<commit_msg>Fix null pointer access in error reporting (#1983)<commit_after>import Client from './client';
import getScope from './get-scope.ts';
import getArgs from './get-args';

export default async (sentry, error, apiUrl, configFiles) => {
  // Do not report errors in development
  if (!process.pkg) {
    return;
  }

  let user = null;
  let team = null;

  try {
    const { token } = configFiles.readAuthConfigFile();
    const { currentTeam } = configFiles.readConfigFile();
    const client = new Client({ apiUrl, token, currentTeam, debug: false });
    ({ user, team } = await getScope(client));
  } catch (err) {
    // We can safely ignore this, as the error
    // reporting works even without this metadata attached.
  }

  if (user || team) {
    sentry.withScope(scope => {
      if (user) {
        const spec = {
          email: user.email,
          id: user.uid
        };

        if (user.username) {
          spec.username = user.username;
        }

        if (user.name) {
          spec.name = user.name;
        }

        scope.setUser(spec);
      }

      if (team) {
        scope.setTag('currentTeam', team.id);
      }

      // Report process.argv without sensitive data
      let args
      try {
        args = getArgs(process.argv.slice(2), {});
      } catch (_) {}

      if (args) {
        const flags = ['--env', '--build-env', '--token']
        for (const flag of flags) {
          if (args[flag]) args[flag] = 'REDACTED';
        }
        if (args._.length >= 4 && args._[0].startsWith('secret') && args._[1] === 'add') {
          args._[3] = 'REDACTED';
        }
        scope.setExtra('args', args);
      }

      sentry.captureException(error);
    });
  } else {
    sentry.captureException(error);
  }

  const client = sentry.getCurrentHub().getClient();

  if (client) {
    await client.close();
  }
};
<|endoftext|>


<commit_before>const LINE = {};
const BLOCK = {};

export default class CodeBuilder {
	constructor () {
		this.result = '';

		this.first = null;
		this.last = null;

		this.lastCondition = null;
	}

	addConditionalLine ( condition, line ) {
		if ( condition === this.lastCondition ) {
			this.result += `\n\t${line}`;
		} else {
			if ( this.lastCondition ) {
				this.result += `\n}`;
			}

			this.result += `if ( ${condition} ) {\n\t${line}`;
			this.lastCondition = condition;
		}

		this.last = BLOCK;
	}

	addLine ( line ) {
		if ( this.lastCondition ) {
			this.result += `\n}`;
			this.lastCondition = null;
		}

		if ( this.last === BLOCK ) {
			this.result += `\n\n${line}`;
		} else if ( this.last === LINE ) {
			this.result += `\n${line}`;
		} else {
			this.result += line;
		}

		this.last = LINE;
		if ( !this.first ) this.first = LINE;
	}

	addLineAtStart ( line ) {
		if ( this.first === BLOCK ) {
			this.result = `${line}\n\n${this.result}`;
		} else if ( this.first === LINE ) {
			this.result = `${line}\n${this.result}`;
		} else {
			this.result += line;
		}

		this.first = LINE;
		if ( !this.last ) this.last = LINE;
	}

	addBlock ( block ) {
		if ( this.lastCondition ) {
			this.result += `\n}`;
			this.lastCondition = null;
		}

		if ( this.result ) {
			this.result += `\n\n${block}`;
		} else {
			this.result += block;
		}

		this.last = BLOCK;
		if ( !this.first ) this.first = BLOCK;
	}

	addBlockAtStart ( block ) {
		if ( this.result ) {
			this.result = `${block}\n\n${this.result}`;
		} else {
			this.result += block;
		}

		this.first = BLOCK;
		if ( !this.last ) this.last = BLOCK;
	}

	isEmpty () {
		return this.result === '';
	}

	toString () {
		return this.result.trim() + ( this.lastCondition ? `\n}` : `` );
	}
}
<commit_msg>separate if statements in codebuilder<commit_after>const LINE = {};
const BLOCK = {};

export default class CodeBuilder {
	constructor () {
		this.result = '';

		this.first = null;
		this.last = null;

		this.lastCondition = null;
	}

	addConditionalLine ( condition, line ) {
		if ( condition === this.lastCondition ) {
			this.result += `\n\t${line}`;
		} else {
			if ( this.lastCondition ) {
				this.result += `\n}\n\n`;
			}

			this.result += `if ( ${condition} ) {\n\t${line}`;
			this.lastCondition = condition;
		}

		this.last = BLOCK;
	}

	addLine ( line ) {
		if ( this.lastCondition ) {
			this.result += `\n}`;
			this.lastCondition = null;
		}

		if ( this.last === BLOCK ) {
			this.result += `\n\n${line}`;
		} else if ( this.last === LINE ) {
			this.result += `\n${line}`;
		} else {
			this.result += line;
		}

		this.last = LINE;
		if ( !this.first ) this.first = LINE;
	}

	addLineAtStart ( line ) {
		if ( this.first === BLOCK ) {
			this.result = `${line}\n\n${this.result}`;
		} else if ( this.first === LINE ) {
			this.result = `${line}\n${this.result}`;
		} else {
			this.result += line;
		}

		this.first = LINE;
		if ( !this.last ) this.last = LINE;
	}

	addBlock ( block ) {
		if ( this.lastCondition ) {
			this.result += `\n}`;
			this.lastCondition = null;
		}

		if ( this.result ) {
			this.result += `\n\n${block}`;
		} else {
			this.result += block;
		}

		this.last = BLOCK;
		if ( !this.first ) this.first = BLOCK;
	}

	addBlockAtStart ( block ) {
		if ( this.result ) {
			this.result = `${block}\n\n${this.result}`;
		} else {
			this.result += block;
		}

		this.first = BLOCK;
		if ( !this.last ) this.last = BLOCK;
	}

	isEmpty () {
		return this.result === '';
	}

	toString () {
		return this.result.trim() + ( this.lastCondition ? `\n}` : `` );
	}
}
<|endoftext|>


<commit_before>import { findIndex } from './array.js';

const handlers = {
	Identifier: destructureIdentifier,
	AssignmentPattern: destructureAssignmentPattern,
	ArrayPattern: destructureArrayPattern,
	ObjectPattern: destructureObjectPattern
};

export default function destructure ( code, scope, node, ref, inline, statementGenerators ) {
	handlers[ node.type ]( code, scope, node, ref, inline, statementGenerators );
}

function destructureIdentifier ( code, scope, node, ref, inline, statementGenerators ) {
	statementGenerators.push( ( start, prefix, suffix ) => {
		code.prependRight( node.start, inline ? prefix : `${prefix}var ` );
		code.appendLeft( node.end, ` = ${ref}${suffix}` );
		code.move( node.start, node.end, start );
	});
}

function destructureAssignmentPattern ( code, scope, node, ref, inline, statementGenerators ) {
	const isIdentifier = node.left.type === 'Identifier';
	const name = isIdentifier ? node.left.name : ref;

	if ( !inline ) {
		statementGenerators.push( ( start, prefix, suffix ) => {
			code.prependRight( node.left.end, `${prefix}if ( ${name} === void 0 ) ${name}` );
			code.move( node.left.end, node.right.end, start );
			code.appendLeft( node.right.end, suffix );
		});
	}

	if ( !isIdentifier ) {
		destructure( code, scope, node.left, ref, inline, statementGenerators );
	}
}

function destructureArrayPattern ( code, scope, node, ref, inline, statementGenerators ) {
	let c = node.start;

	node.elements.forEach( ( element, i ) => {
		if ( !element ) return;

		if ( element.type === 'RestElement' ) {
			handleProperty( code, scope, c, element.argument, `${ref}.slice(${i})`, inline, statementGenerators );
		} else {
			handleProperty( code, scope, c, element, `${ref}[${i}]`, inline, statementGenerators );
		}
		c = element.end;
	});

	code.remove( c, node.end );
}

function destructureObjectPattern ( code, scope, node, ref, inline, statementGenerators ) {
	let c = node.start;

	const nonRestKeys = [];
	node.properties.forEach( prop => {
		let value;
		let content
		if (prop.type === "Property") {
			const isComputedKey = prop.computed || prop.key.type !== 'Identifier'
			const key = isComputedKey ? code.slice(prop.key.start, prop.key.end) : prop.key.name
			value = isComputedKey ? `${ref}[${key}]` : `${ref}.${key}`;
			content = prop.value;
			nonRestKeys.push(isComputedKey ? key : '"' + key + '"')
		} else if (prop.type === "RestElement") {
			content = prop.argument
			value = scope.createIdentifier( 'rest' );
			const n = scope.createIdentifier( 'n' );
			statementGenerators.push( ( start, prefix, suffix ) => {
				code.overwrite(prop.start, c = prop.argument.start, `${prefix}var ${value} = {}; for (var ${n} in ${ref}) if([${nonRestKeys.join(", ")}].indexOf(${n}) === -1) ${value}[${n}] = ${ref}[${n}]${suffix}`)
				code.move(prop.start, c, start)
			} );
		} else {
			throw new CompileError( this, `Unexpected node of type ${prop.type} in object pattern`)
		}
		handleProperty( code, scope, c, content, value, inline, statementGenerators );
		c = prop.end;
	});

	code.remove( c, node.end );
}

function handleProperty ( code, scope, c, node, value, inline, statementGenerators ) {
	switch ( node.type ) {
		case 'Identifier': {
			code.remove( c, node.start );
			destructureIdentifier( code, scope, node, value, inline, statementGenerators );
			break;
		}

		case 'AssignmentPattern': {
			let name;

			const isIdentifier = node.left.type === 'Identifier';

			if ( isIdentifier ) {
				name = node.left.name;
				const declaration = scope.findDeclaration( name );
				if ( declaration ) name = declaration.name;
			} else {
				name = scope.createIdentifier( value );
			}

			statementGenerators.push( ( start, prefix, suffix ) => {
				if ( inline ) {
					code.prependRight( node.right.start, `${name} = ${value} === undefined ? ` );
					code.appendLeft( node.right.end, ` : ${value}` );
				} else {
					code.prependRight( node.right.start, `${prefix}var ${name} = ${value}; if ( ${name} === void 0 ) ${name} = ` );
					code.appendLeft( node.right.end, suffix );
				}

				code.move( node.right.start, node.right.end, start );
			});

			if ( isIdentifier ) {
				code.remove( c, node.right.start );
			} else {
				code.remove( c, node.left.start );
				code.remove( node.left.end, node.right.start );
				handleProperty( code, scope, c, node.left, name, inline, statementGenerators );
			}

			break;
		}

		case 'ObjectPattern': {
			code.remove( c, c = node.start );

			let ref = value;
			if ( node.properties.length > 1 ) {
				ref = scope.createIdentifier( value );

				statementGenerators.push( ( start, prefix, suffix ) => {
					// this feels a tiny bit hacky, but we can't do a
					// straightforward appendLeft and keep correct order...
					code.prependRight( node.start, `${prefix}var ${ref} = ` );
					code.overwrite( node.start, c = node.start + 1, value );
					code.appendLeft( c, suffix );

					code.overwrite( node.start, c = node.start + 1, `${prefix}var ${ref} = ${value}${suffix}` );
					code.move( node.start, c, start );
				});
			}

			destructureObjectPattern( code, scope, node, ref, inline, statementGenerators )

			break;
		}

		case 'ArrayPattern': {
			code.remove( c, c = node.start );

			if ( node.elements.filter( Boolean ).length > 1 ) {
				const ref = scope.createIdentifier( value );

				statementGenerators.push( ( start, prefix, suffix ) => {
					code.prependRight( node.start, `${prefix}var ${ref} = ` );
					code.overwrite( node.start, c = node.start + 1, value );
					code.appendLeft( c, suffix );

					code.move( node.start, c, start );
				});

				node.elements.forEach( ( element, i ) => {
					if ( !element ) return;

					if ( element.type === 'RestElement' ) {
						handleProperty( code, scope, c, element.argument, `${ref}.slice(${i})`, inline, statementGenerators );
					} else {
						handleProperty( code, scope, c, element, `${ref}[${i}]`, inline, statementGenerators );
					}
					c = element.end;
				});
			} else {
				const index = findIndex( node.elements, Boolean );
				const element = node.elements[ index ];
				if ( element.type === 'RestElement' ) {
					handleProperty( code, scope, c, element.argument, `${value}.slice(${index})`, inline, statementGenerators );
				} else {
					handleProperty( code, scope, c, element, `${value}[${index}]`, inline, statementGenerators );
				}
				c = element.end;
			}

			code.remove( c, node.end );
			break;
		}

		default: {
			throw new Error( `Unexpected node type in destructuring (${node.type})` );
		}
	}
}
<commit_msg>contentOnly<commit_after>import { findIndex } from './array.js';

const handlers = {
	Identifier: destructureIdentifier,
	AssignmentPattern: destructureAssignmentPattern,
	ArrayPattern: destructureArrayPattern,
	ObjectPattern: destructureObjectPattern
};

export default function destructure ( code, scope, node, ref, inline, statementGenerators ) {
	handlers[ node.type ]( code, scope, node, ref, inline, statementGenerators );
}

function destructureIdentifier ( code, scope, node, ref, inline, statementGenerators ) {
	statementGenerators.push( ( start, prefix, suffix ) => {
		code.prependRight( node.start, inline ? prefix : `${prefix}var ` );
		code.appendLeft( node.end, ` = ${ref}${suffix}` );
		code.move( node.start, node.end, start );
	});
}

function destructureAssignmentPattern ( code, scope, node, ref, inline, statementGenerators ) {
	const isIdentifier = node.left.type === 'Identifier';
	const name = isIdentifier ? node.left.name : ref;

	if ( !inline ) {
		statementGenerators.push( ( start, prefix, suffix ) => {
			code.prependRight( node.left.end, `${prefix}if ( ${name} === void 0 ) ${name}` );
			code.move( node.left.end, node.right.end, start );
			code.appendLeft( node.right.end, suffix );
		});
	}

	if ( !isIdentifier ) {
		destructure( code, scope, node.left, ref, inline, statementGenerators );
	}
}

function destructureArrayPattern ( code, scope, node, ref, inline, statementGenerators ) {
	let c = node.start;

	node.elements.forEach( ( element, i ) => {
		if ( !element ) return;

		if ( element.type === 'RestElement' ) {
			handleProperty( code, scope, c, element.argument, `${ref}.slice(${i})`, inline, statementGenerators );
		} else {
			handleProperty( code, scope, c, element, `${ref}[${i}]`, inline, statementGenerators );
		}
		c = element.end;
	});

	code.remove( c, node.end );
}

function destructureObjectPattern ( code, scope, node, ref, inline, statementGenerators ) {
	let c = node.start;

	const nonRestKeys = [];
	node.properties.forEach( prop => {
		let value;
		let content
		if (prop.type === "Property") {
			const isComputedKey = prop.computed || prop.key.type !== 'Identifier'
			const key = isComputedKey ? code.slice(prop.key.start, prop.key.end) : prop.key.name
			value = isComputedKey ? `${ref}[${key}]` : `${ref}.${key}`;
			content = prop.value;
			nonRestKeys.push(isComputedKey ? key : '"' + key + '"')
		} else if (prop.type === "RestElement") {
			content = prop.argument
			value = scope.createIdentifier( 'rest' );
			const n = scope.createIdentifier( 'n' );
			statementGenerators.push( ( start, prefix, suffix ) => {
				code.overwrite(prop.start, c = prop.argument.start, `${prefix}var ${value} = {}; for (var ${n} in ${ref}) if([${nonRestKeys.join(", ")}].indexOf(${n}) === -1) ${value}[${n}] = ${ref}[${n}]${suffix}`)
				code.move(prop.start, c, start)
			} );
		} else {
			throw new CompileError( this, `Unexpected node of type ${prop.type} in object pattern`)
		}
		handleProperty( code, scope, c, content, value, inline, statementGenerators );
		c = prop.end;
	});

	code.remove( c, node.end );
}

function handleProperty ( code, scope, c, node, value, inline, statementGenerators ) {
	switch ( node.type ) {
		case 'Identifier': {
			code.remove( c, node.start );
			destructureIdentifier( code, scope, node, value, inline, statementGenerators );
			break;
		}

		case 'AssignmentPattern': {
			let name;

			const isIdentifier = node.left.type === 'Identifier';

			if ( isIdentifier ) {
				name = node.left.name;
				const declaration = scope.findDeclaration( name );
				if ( declaration ) name = declaration.name;
			} else {
				name = scope.createIdentifier( value );
			}

			statementGenerators.push( ( start, prefix, suffix ) => {
				if ( inline ) {
					code.prependRight( node.right.start, `${name} = ${value} === undefined ? ` );
					code.appendLeft( node.right.end, ` : ${value}` );
				} else {
					code.prependRight( node.right.start, `${prefix}var ${name} = ${value}; if ( ${name} === void 0 ) ${name} = ` );
					code.appendLeft( node.right.end, suffix );
				}

				code.move( node.right.start, node.right.end, start );
			});

			if ( isIdentifier ) {
				code.remove( c, node.right.start );
			} else {
				code.remove( c, node.left.start );
				code.remove( node.left.end, node.right.start );
				handleProperty( code, scope, c, node.left, name, inline, statementGenerators );
			}

			break;
		}

		case 'ObjectPattern': {
			code.remove( c, c = node.start );

			let ref = value;
			if ( node.properties.length > 1 ) {
				ref = scope.createIdentifier( value );

				statementGenerators.push( ( start, prefix, suffix ) => {
					// this feels a tiny bit hacky, but we can't do a
					// straightforward appendLeft and keep correct order...
					code.prependRight( node.start, `${prefix}var ${ref} = ` );
					code.overwrite( node.start, c = node.start + 1, value );
					code.appendLeft( c, suffix );

					code.overwrite( node.start, c = node.start + 1, `${prefix}var ${ref} = ${value}${suffix}` );
					code.move( node.start, c, start );
				});
			}

			destructureObjectPattern( code, scope, node, ref, inline, statementGenerators );

			break;
		}

		case 'ArrayPattern': {
			code.remove( c, c = node.start );

			if ( node.elements.filter( Boolean ).length > 1 ) {
				const ref = scope.createIdentifier( value );

				statementGenerators.push( ( start, prefix, suffix ) => {
					code.prependRight( node.start, `${prefix}var ${ref} = ` );
					code.overwrite( node.start, c = node.start + 1, value, { contentOnly: true });
					code.appendLeft( c, suffix );

					code.move( node.start, c, start );
				});

				node.elements.forEach( ( element, i ) => {
					if ( !element ) return;

					if ( element.type === 'RestElement' ) {
						handleProperty( code, scope, c, element.argument, `${ref}.slice(${i})`, inline, statementGenerators );
					} else {
						handleProperty( code, scope, c, element, `${ref}[${i}]`, inline, statementGenerators );
					}
					c = element.end;
				});
			} else {
				const index = findIndex( node.elements, Boolean );
				const element = node.elements[ index ];
				if ( element.type === 'RestElement' ) {
					handleProperty( code, scope, c, element.argument, `${value}.slice(${index})`, inline, statementGenerators );
				} else {
					handleProperty( code, scope, c, element, `${value}[${index}]`, inline, statementGenerators );
				}
				c = element.end;
			}

			code.remove( c, node.end );
			break;
		}

		default: {
			throw new Error( `Unexpected node type in destructuring (${node.type})` );
		}
	}
}
<|endoftext|>


<commit_before>const TrackedData = require('./tracked-data');

const DEFAULT_INTENSITY = 0;

const COLOR_DEFAULT = "default";

export default class LightArray extends TrackedData {
  constructor(stripLengths, defaultIntensity=DEFAULT_INTENSITY, defaultColor=COLOR_DEFAULT) {
    const properties = {};
    for (let stripId of Object.keys(stripLengths)) {
      const strip = {};
      for (let panelId = 0; panelId < stripLengths[stripId]; panelId++) {
        strip[panelId] = new TrackedData({
          intensity: defaultIntensity,
          color: defaultColor,
          active: false
        });
      }
      properties[stripId] = new TrackedData({
        maxIntensity: 100,
        panels: new TrackedData(strip)
      });
    }
    super(properties);

    this.stripIds = Object.keys(stripLengths);
  }

  setMaxIntensity(intensity, stripId=null) {
    const stripsToModify = stripId === null ? this.stripIds : [stripId];

    for (let targetStripId of stripsToModify) {
      const strip = this.get(targetStripId);
      strip.set("maxIntensity", intensity);
    }
  }

  getMaxIntensity(stripId) {
    return this.get(stripId).get("maxIntensity");
  }

  getPanel(stripId, panelId) {
    return this.get(stripId).get("panels").get(panelId);
  }

  setColor(stripId, panelId, color) {
    this._applyToOnePanelOrAll((panel) => panel.set("color", color), stripId, panelId);
  }

  getColor(stripId, panelId) {
    const panel = this.getPanel(stripId, panelId);

    return panel.get("color");
  }

  getIntensity(stripId, panelId) {
    const panel = this.getPanel(stripId, panelId);

    return panel.get("intensity");
  }

  setIntensity(stripId, panelId, intensity) {
    this._applyToOnePanelOrAll((panel) => panel.set("intensity", intensity), stripId, panelId);
  }

  isActive(stripId, panelId) {
    const panel = this.getPanel(stripId, panelId);

    return panel.get("active");
  }

  activate(stripId, panelId, active=true) {
    const panel = this.getPanel(stripId, panelId);

    panel.set("active", active);
  }

  deactivate(stripId, panelId) {
    this.activate(stripId, panelId, false);
  }

  _applyToOnePanelOrAll(panelFunc, stripId, panelId=null) {
    const panels = this._getOnePanelOrAll(stripId, panelId);

    for (let panel of panels) {
      panelFunc(panel);
    }
  }

  _getOnePanelOrAll(stripId, panelId) {
    if (panelId === null) {
      // this code is necessary because there is no Object.values() function
      const stripPanels = this.get(stripId).get("panels");
      return [for (stripPanelId of Object.keys(stripPanels)) stripPanels[stripPanelId]];
    }
    else {
      return [this.getPanel(stripId, panelId)];
    }
  }
}
<commit_msg>Added a panelIds property to each strip to keep track of all the IDs added to that strip<commit_after>const TrackedData = require('./tracked-data');

const DEFAULT_INTENSITY = 0;

const COLOR_DEFAULT = "default";

export default class LightArray extends TrackedData {
  constructor(stripLengths, defaultIntensity=DEFAULT_INTENSITY, defaultColor=COLOR_DEFAULT) {
    const properties = {};
    for (let stripId of Object.keys(stripLengths)) {
      const strip = {};
      const panelIds = [];
      for (let panelId = 0; panelId < stripLengths[stripId]; panelId++) {
        strip[panelId] = new TrackedData({
          intensity: defaultIntensity,
          color: defaultColor,
          active: false
        });

        panelIds.push(panelId);
      }
      properties[stripId] = new TrackedData({
        maxIntensity: 100,
        panels: new TrackedData(strip)
      });
      properties[stripId].panelIds = panelIds;
    }
    super(properties);

    this.stripIds = Object.keys(stripLengths);
  }

  setMaxIntensity(intensity, stripId=null) {
    const stripsToModify = stripId === null ? this.stripIds : [stripId];

    for (let targetStripId of stripsToModify) {
      const strip = this.get(targetStripId);
      strip.set("maxIntensity", intensity);
    }
  }

  getMaxIntensity(stripId) {
    return this.get(stripId).get("maxIntensity");
  }

  getPanel(stripId, panelId) {
    return this.get(stripId).get("panels").get(panelId);
  }

  setColor(stripId, panelId, color) {
    this._applyToOnePanelOrAll((panel) => panel.set("color", color), stripId, panelId);
  }

  getColor(stripId, panelId) {
    const panel = this.getPanel(stripId, panelId);

    return panel.get("color");
  }

  getIntensity(stripId, panelId) {
    const panel = this.getPanel(stripId, panelId);

    return panel.get("intensity");
  }

  setIntensity(stripId, panelId, intensity) {
    this._applyToOnePanelOrAll((panel) => panel.set("intensity", intensity), stripId, panelId);
  }

  isActive(stripId, panelId) {
    const panel = this.getPanel(stripId, panelId);

    return panel.get("active");
  }

  activate(stripId, panelId, active=true) {
    const panel = this.getPanel(stripId, panelId);

    panel.set("active", active);
  }

  deactivate(stripId, panelId) {
    this.activate(stripId, panelId, false);
  }

  _applyToOnePanelOrAll(panelFunc, stripId, panelId=null) {
    const panels = this._getOnePanelOrAll(stripId, panelId);

    for (let panel of panels) {
      panelFunc(panel);
    }
  }

  _getOnePanelOrAll(stripId, panelId) {
    if (panelId === null) {
      // this code is necessary because there is no Object.values() function
      const stripPanels = this.get(stripId).get("panels");
      return [for (stripPanelId of Object.keys(stripPanels)) stripPanels[stripPanelId]];
    }
    else {
      return [this.getPanel(stripId, panelId)];
    }
  }
}
<|endoftext|>


<commit_before>'use strict'

var gulp = require('gulp')
var rimraf = require('gulp-rimraf')
var tslint = require('gulp-tslint')
var ts = require('gulp-typescript')
var sourcemaps = require('gulp-sourcemaps')
var bowerfiles = require('main-bower-files')
var concat = require('gulp-concat')
var stylelint = require('gulp-stylelint')
var cssmin = require('gulp-cssmin')
var uglify = require('gulp-uglify')
var flatten = require('gulp-flatten')
var htmlhint = require('gulp-htmlhint')
var util = require('gulp-util')
var watch = require('gulp-watch')
var batch = require('gulp-batch')
var browserSync = require('browser-sync').create()
var proxy = require('proxy-middleware')
var url = require('url')

var paths = {
  wwwroot: './wwwroot/',
  src: './src/',
  test: './test/'
}

paths.srcTs = paths.src + '**/*.ts'
paths.testTs = paths.test + '**/*.ts'
paths.srcCss = paths.src + '**/*.css'
paths.srcJs = paths.src + '**/*.js'
paths.srcHtml = paths.src + '**/*.html'
paths.testJs = paths.test + '**/*.js'
paths.srcJsMap = paths.src + '**/*.js.map'
paths.testJsMap = paths.test + '**/*.js.map'
paths.assets = paths.wwwroot + '**/*.{html,ico}' // Far from perfect :(

paths.concatJsDest = paths.wwwroot + 'js/site.min.js'
paths.concatVendorJsDest = paths.wwwroot + 'js/vendor.min.js'
paths.concatCssDest = paths.wwwroot + 'css/site.min.css'
paths.concatVendorCssDest = paths.wwwroot + 'css/vendor.min.css'
var vendorExtensions = '**/*.{eot,svg,ttf,woff,woff2}'
paths.vendorAssets = paths.wwwroot + vendorExtensions // Far from perfect :(

var tsProject = ts.createProject('tsconfig.json')
var bootstrapFiles = ['./dist/css/bootstrap.css', './dist/css/bootstrap-theme.css', './dist/fonts/*.*']
var fontawesomeFiles = ['./css/font-awesome.css', './fonts/*.*']

var proxyOptions = url.parse('http://localhost:5000/')
proxyOptions.route = '/api'

gulp.task('clean:js', function () {
  return gulp.src([
    paths.srcJs,
    paths.srcJsMap,
    paths.testJs,
    paths.testJsMap,
    paths.concatJsDest + '*'], { read: false })
    .pipe(rimraf())
})

gulp.task('clean:css', function () {
  return gulp.src(paths.concatCssDest + '*', { read: false })
    .pipe(rimraf())
})

gulp.task('clean:assets', function () {
  return gulp.src(paths.assets, { read: false })
    .pipe(rimraf())
})

gulp.task('clean:vendor', function () {
  return gulp.src([
    paths.concatVendorJsDest + '*',
    paths.concatVendorCssDest + '*',
    paths.vendorAssets], { read: false })
    .pipe(rimraf())
})

gulp.task('clean:all', function () {
  return gulp.src(paths.wwwroot, { read: false })
    .pipe(rimraf())
})

gulp.task('lint:ts', function () {
  gulp.src([paths.srcTs, paths.testTs])
    .pipe(tslint())
    .pipe(tslint.report('verbose', {emitError: false}))
})

gulp.task('tscompile', ['lint:ts', 'clean:js'], function () {
  var tsResult = tsProject.src()
    .pipe(sourcemaps.init())
    .pipe(ts(tsProject))

  return tsResult.js
    .pipe(sourcemaps.write('.', {
      mapSources: function (sourcePath) {
        var truncatedSourcePath = sourcePath.substr(sourcePath.lastIndexOf('/') + 1)
        util.log('SourcePath within source map truncated to:', util.colors.cyan(truncatedSourcePath))
        return truncatedSourcePath
      }
    }))
    .pipe(gulp.dest('.'))
})

gulp.task('min:js', ['tscompile'], function () {
  gulp.src([paths.srcJs], { base: '.' })
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(concat(paths.concatJsDest))
    .pipe(sourcemaps.write())
    .pipe(sourcemaps.init())
    // Note that compress: true and mangle: true gives you uglier code,
    // but makes debugging in the browser debugger more difficult.
    .pipe(uglify({compress: false, mangle: false}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('.'))
})

gulp.task('lint:css', function () {
  gulp.src(paths.srcCss)
    .pipe(stylelint({
      failAfterError: false,
      reporters: [
        {formatter: function (results) {
          results.forEach(function (element) {
            var file = element.source.substr(element.source.lastIndexOf('/') + 1)
            element.warnings.forEach(function (warning) {
              var message = '[' + util.colors.cyan('gulp-stylelint') + '] ' +
                util.colors.red(warning.severity) + ' ' + file +
                '[' + warning.line + ', ' + warning.column + ']: ' + warning.text
              util.log(message)
            })
          })
        }}
      ]
    }))
})

gulp.task('min:css', ['lint:css', 'clean:css'], function () {
  gulp.src(paths.srcCss)
    .pipe(sourcemaps.init())
    .pipe(concat(paths.concatCssDest))
    .pipe(cssmin())
    .pipe(sourcemaps.write('.', {
      mapSources: function (sourcePath) {
        var extendedSourcePath = paths.src.substr(2) + sourcePath
        util.log('SourcePath within source map extended to:', util.colors.cyan(extendedSourcePath))
        return extendedSourcePath
      }
    }))
    .pipe(gulp.dest('.'))
})

gulp.task('lint:html', function () {
  gulp.src(paths.srcHtml)
    .pipe(htmlhint())
    .pipe(htmlhint.reporter())
})

gulp.task('assets', ['lint:html', 'clean:assets'], function () {
  gulp.src([paths.src + '**/*', '!' + paths.srcTs, '!' + paths.srcJs, '!' + paths.srcJsMap, '!' + paths.srcCss])
    .pipe(gulp.dest(paths.wwwroot))
})

gulp.task('min:vendorjs', ['clean:vendor'], function () {
  gulp.src(bowerfiles('**/*.js'), { base: '.' })
    .pipe(sourcemaps.init())
    .pipe(concat(paths.concatVendorJsDest))
    .pipe(sourcemaps.write())
    .pipe(sourcemaps.init())
    // Note that compress: true and mangle: true gives you uglier code,
    // but makes debugging in the browser debugger more difficult.
    .pipe(uglify({compress: false, mangle: false}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('.'))
})

gulp.task('min:vendorcss', ['clean:vendor'], function () {
  gulp.src(bowerfiles('**/*.css',
    {overrides: {
      'bootstrap': {main: bootstrapFiles},
      'font-awesome': {main: fontawesomeFiles}
    }}), { base: '.' })
    .pipe(sourcemaps.init())
    .pipe(concat(paths.concatVendorCssDest))
    .pipe(cssmin())
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('.'))
})

gulp.task('vendorassets', ['clean:vendor'], function () {
  gulp.src(bowerfiles(vendorExtensions,
    {overrides: {
      'bootstrap': {main: bootstrapFiles},
      'font-awesome': {main: fontawesomeFiles}
    }}), { base: '.' })
    .pipe(flatten({includeParents: -1}))
    .pipe(gulp.dest(paths.wwwroot))
})

gulp.task('build:app', ['min:js', 'min:css', 'assets'])

gulp.task('build:vendor', ['min:vendorjs', 'min:vendorcss', 'vendorassets'])

gulp.task('build', ['clean:all'], function (cb) {
  gulp.start('build:app', cb)
  gulp.start('build:vendor', cb)
})

gulp.task('default', ['build'])

// Watch tasks

gulp.task('watch:js', function () {
  watch(paths.srcTs, batch(function (events, done) {
    gulp.start('min:js', done)
  }))
})

gulp.task('watch:css', function () {
  watch(paths.srcCss, batch(function (events, done) {
    gulp.start('min:css', done)
  }))
})

gulp.task('watch:assets', function () {
  watch([paths.src + '**/*', '!' + paths.srcTs, '!' + paths.srcJs, '!' + paths.srcJsMap, '!' + paths.srcCss], batch(function (events, done) {
    gulp.start('assets', done)
  }))
})

gulp.task('watch', ['watch:js', 'watch:css', 'watch:assets'])

// Browsersync tasks

gulp.task('watch:browsersync', function () {
  util.log(util.colors.cyan(' -- Only changes on application files will be synced using browser-sync --'))

  browserSync.init({
    server: {
      baseDir: paths.wwwroot,
      middleware: [proxy(proxyOptions)]
    }
  })

  var sync = function (vinyl) {
    if (vinyl.event === 'add' || vinyl.event === 'change') {
      return gulp.src(vinyl.path)
        .pipe(browserSync.stream())
    }
  }

  watch(paths.concatJsDest.substr(2) + '*', sync)
  watch(paths.concatCssDest.substr(2) + '*', sync)
  watch(paths.assets.substr(2), batch(function (events, done) {
    browserSync.reload()
    done()
  }))

  gulp.start('watch')
})
<commit_msg>Fix proxy route.<commit_after>'use strict'

var gulp = require('gulp')
var rimraf = require('gulp-rimraf')
var tslint = require('gulp-tslint')
var ts = require('gulp-typescript')
var sourcemaps = require('gulp-sourcemaps')
var bowerfiles = require('main-bower-files')
var concat = require('gulp-concat')
var stylelint = require('gulp-stylelint')
var cssmin = require('gulp-cssmin')
var uglify = require('gulp-uglify')
var flatten = require('gulp-flatten')
var htmlhint = require('gulp-htmlhint')
var util = require('gulp-util')
var watch = require('gulp-watch')
var batch = require('gulp-batch')
var browserSync = require('browser-sync').create()
var proxy = require('proxy-middleware')
var url = require('url')

var paths = {
  wwwroot: './wwwroot/',
  src: './src/',
  test: './test/'
}

paths.srcTs = paths.src + '**/*.ts'
paths.testTs = paths.test + '**/*.ts'
paths.srcCss = paths.src + '**/*.css'
paths.srcJs = paths.src + '**/*.js'
paths.srcHtml = paths.src + '**/*.html'
paths.testJs = paths.test + '**/*.js'
paths.srcJsMap = paths.src + '**/*.js.map'
paths.testJsMap = paths.test + '**/*.js.map'
paths.assets = paths.wwwroot + '**/*.{html,ico}' // Far from perfect :(

paths.concatJsDest = paths.wwwroot + 'js/site.min.js'
paths.concatVendorJsDest = paths.wwwroot + 'js/vendor.min.js'
paths.concatCssDest = paths.wwwroot + 'css/site.min.css'
paths.concatVendorCssDest = paths.wwwroot + 'css/vendor.min.css'
var vendorExtensions = '**/*.{eot,svg,ttf,woff,woff2}'
paths.vendorAssets = paths.wwwroot + vendorExtensions // Far from perfect :(

var tsProject = ts.createProject('tsconfig.json')
var bootstrapFiles = ['./dist/css/bootstrap.css', './dist/css/bootstrap-theme.css', './dist/fonts/*.*']
var fontawesomeFiles = ['./css/font-awesome.css', './fonts/*.*']

var proxyOptions = url.parse('http://localhost:5000/api')
proxyOptions.route = '/api'

gulp.task('clean:js', function () {
  return gulp.src([
    paths.srcJs,
    paths.srcJsMap,
    paths.testJs,
    paths.testJsMap,
    paths.concatJsDest + '*'], { read: false })
    .pipe(rimraf())
})

gulp.task('clean:css', function () {
  return gulp.src(paths.concatCssDest + '*', { read: false })
    .pipe(rimraf())
})

gulp.task('clean:assets', function () {
  return gulp.src(paths.assets, { read: false })
    .pipe(rimraf())
})

gulp.task('clean:vendor', function () {
  return gulp.src([
    paths.concatVendorJsDest + '*',
    paths.concatVendorCssDest + '*',
    paths.vendorAssets], { read: false })
    .pipe(rimraf())
})

gulp.task('clean:all', function () {
  return gulp.src(paths.wwwroot, { read: false })
    .pipe(rimraf())
})

gulp.task('lint:ts', function () {
  gulp.src([paths.srcTs, paths.testTs])
    .pipe(tslint())
    .pipe(tslint.report('verbose', {emitError: false}))
})

gulp.task('tscompile', ['lint:ts', 'clean:js'], function () {
  var tsResult = tsProject.src()
    .pipe(sourcemaps.init())
    .pipe(ts(tsProject))

  return tsResult.js
    .pipe(sourcemaps.write('.', {
      mapSources: function (sourcePath) {
        var truncatedSourcePath = sourcePath.substr(sourcePath.lastIndexOf('/') + 1)
        util.log('SourcePath within source map truncated to:', util.colors.cyan(truncatedSourcePath))
        return truncatedSourcePath
      }
    }))
    .pipe(gulp.dest('.'))
})

gulp.task('min:js', ['tscompile'], function () {
  gulp.src([paths.srcJs], { base: '.' })
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(concat(paths.concatJsDest))
    .pipe(sourcemaps.write())
    .pipe(sourcemaps.init())
    // Note that compress: true and mangle: true gives you uglier code,
    // but makes debugging in the browser debugger more difficult.
    .pipe(uglify({compress: false, mangle: false}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('.'))
})

gulp.task('lint:css', function () {
  gulp.src(paths.srcCss)
    .pipe(stylelint({
      failAfterError: false,
      reporters: [
        {formatter: function (results) {
          results.forEach(function (element) {
            var file = element.source.substr(element.source.lastIndexOf('/') + 1)
            element.warnings.forEach(function (warning) {
              var message = '[' + util.colors.cyan('gulp-stylelint') + '] ' +
                util.colors.red(warning.severity) + ' ' + file +
                '[' + warning.line + ', ' + warning.column + ']: ' + warning.text
              util.log(message)
            })
          })
        }}
      ]
    }))
})

gulp.task('min:css', ['lint:css', 'clean:css'], function () {
  gulp.src(paths.srcCss)
    .pipe(sourcemaps.init())
    .pipe(concat(paths.concatCssDest))
    .pipe(cssmin())
    .pipe(sourcemaps.write('.', {
      mapSources: function (sourcePath) {
        var extendedSourcePath = paths.src.substr(2) + sourcePath
        util.log('SourcePath within source map extended to:', util.colors.cyan(extendedSourcePath))
        return extendedSourcePath
      }
    }))
    .pipe(gulp.dest('.'))
})

gulp.task('lint:html', function () {
  gulp.src(paths.srcHtml)
    .pipe(htmlhint())
    .pipe(htmlhint.reporter())
})

gulp.task('assets', ['lint:html', 'clean:assets'], function () {
  gulp.src([paths.src + '**/*', '!' + paths.srcTs, '!' + paths.srcJs, '!' + paths.srcJsMap, '!' + paths.srcCss])
    .pipe(gulp.dest(paths.wwwroot))
})

gulp.task('min:vendorjs', ['clean:vendor'], function () {
  gulp.src(bowerfiles('**/*.js'), { base: '.' })
    .pipe(sourcemaps.init())
    .pipe(concat(paths.concatVendorJsDest))
    .pipe(sourcemaps.write())
    .pipe(sourcemaps.init())
    // Note that compress: true and mangle: true gives you uglier code,
    // but makes debugging in the browser debugger more difficult.
    .pipe(uglify({compress: false, mangle: false}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('.'))
})

gulp.task('min:vendorcss', ['clean:vendor'], function () {
  gulp.src(bowerfiles('**/*.css',
    {overrides: {
      'bootstrap': {main: bootstrapFiles},
      'font-awesome': {main: fontawesomeFiles}
    }}), { base: '.' })
    .pipe(sourcemaps.init())
    .pipe(concat(paths.concatVendorCssDest))
    .pipe(cssmin())
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('.'))
})

gulp.task('vendorassets', ['clean:vendor'], function () {
  gulp.src(bowerfiles(vendorExtensions,
    {overrides: {
      'bootstrap': {main: bootstrapFiles},
      'font-awesome': {main: fontawesomeFiles}
    }}), { base: '.' })
    .pipe(flatten({includeParents: -1}))
    .pipe(gulp.dest(paths.wwwroot))
})

gulp.task('build:app', ['min:js', 'min:css', 'assets'])

gulp.task('build:vendor', ['min:vendorjs', 'min:vendorcss', 'vendorassets'])

gulp.task('build', ['clean:all'], function (cb) {
  gulp.start('build:app', cb)
  gulp.start('build:vendor', cb)
})

gulp.task('default', ['build'])

// Watch tasks

gulp.task('watch:js', function () {
  watch(paths.srcTs, batch(function (events, done) {
    gulp.start('min:js', done)
  }))
})

gulp.task('watch:css', function () {
  watch(paths.srcCss, batch(function (events, done) {
    gulp.start('min:css', done)
  }))
})

gulp.task('watch:assets', function () {
  watch([paths.src + '**/*', '!' + paths.srcTs, '!' + paths.srcJs, '!' + paths.srcJsMap, '!' + paths.srcCss], batch(function (events, done) {
    gulp.start('assets', done)
  }))
})

gulp.task('watch', ['watch:js', 'watch:css', 'watch:assets'])

// Browsersync tasks

gulp.task('watch:browsersync', function () {
  util.log(util.colors.cyan(' -- Only changes on application files will be synced using browser-sync --'))

  browserSync.init({
    server: {
      baseDir: paths.wwwroot,
      middleware: [proxy(proxyOptions)]
    }
  })

  var sync = function (vinyl) {
    if (vinyl.event === 'add' || vinyl.event === 'change') {
      return gulp.src(vinyl.path)
        .pipe(browserSync.stream())
    }
  }

  watch(paths.concatJsDest.substr(2) + '*', sync)
  watch(paths.concatCssDest.substr(2) + '*', sync)
  watch(paths.assets.substr(2), batch(function (events, done) {
    browserSync.reload()
    done()
  }))

  gulp.start('watch')
})
<|endoftext|>


<commit_before>import Promise from 'pinkie';
import request from 'request-promise';
import delay from './delay';
import * as ERROR_MESSAGES from '../templates/error-messages';

const API_REQUEST_DELAY = 100;

let apiRequestPromise = Promise.resolve(null);

export default function (apiPath, params = {}) {
    if (!process.env['BROWSERSTACK_USERNAME'] || !process.env['BROWSERSTACK_ACCESS_KEY'])
        throw new Error(ERROR_MESSAGES.BROWSERSTACK_AUTHENTICATION_FAILED());

    var url = apiPath.url;

    var { body, executeImmediately, ...queryParams } = params;

    var opts = {
        auth: {
            user: process.env['BROWSERSTACK_USERNAME'],
            pass: process.env['BROWSERSTACK_ACCESS_KEY'],
        },

        qs: { ...queryParams },

        method: apiPath.method || 'GET',
        json:   apiPath.encoding === void 0
    };

    if (body)
        opts.body = body;

    if (apiPath.encoding !== void 0)
        opts.encoding = apiPath.encoding;

    const chainPromise = executeImmediately ? Promise.resolve(null) : apiRequestPromise;

    let currentRequestPromise = chainPromise
        .then(() => request(url, opts))
        .catch(error => {
            if (error.statusCode === 401)
                throw new Error(ERROR_MESSAGES.BROWSERSTACK_AUTHENTICATION_FAILED());

            throw error;
        });

    if (executeImmediately) {
        let result = null;

        currentRequestPromise = currentRequestPromise
            .then(promiseResult => {
                result = promiseResult;
            })
            .then(() => delay(API_REQUEST_DELAY))
            .then(() => result);
    }
    else
        apiRequestPromise = currentRequestPromise.then(() => delay(API_REQUEST_DELAY));

    return currentRequestPromise;
}
<commit_msg>Added support to use proxy when making API requests (#80)<commit_after>import Promise from 'pinkie';
import request from 'request-promise';
import delay from './delay';
import * as ERROR_MESSAGES from '../templates/error-messages';

const API_REQUEST_DELAY = 100;

let apiRequestPromise = Promise.resolve(null);

export default function (apiPath, params = {}) {
    if (!process.env['BROWSERSTACK_USERNAME'] || !process.env['BROWSERSTACK_ACCESS_KEY'])
        throw new Error(ERROR_MESSAGES.BROWSERSTACK_AUTHENTICATION_FAILED());

    var url = apiPath.url;

    var { body, executeImmediately, ...queryParams } = params;

    var opts = {
        auth: {
            user: process.env['BROWSERSTACK_USERNAME'],
            pass: process.env['BROWSERSTACK_ACCESS_KEY'],
        },

        qs: { ...queryParams },

        method: apiPath.method || 'GET',
        json:   apiPath.encoding === void 0
    };

    const proxy = process.env['BROWSERSTACK_PROXY'];

    if (proxy)
        opts.proxy = `http://${proxy}`;

    if (body)
        opts.body = body;

    if (apiPath.encoding !== void 0)
        opts.encoding = apiPath.encoding;

    const chainPromise = executeImmediately ? Promise.resolve(null) : apiRequestPromise;

    let currentRequestPromise = chainPromise
        .then(() => request(url, opts))
        .catch(error => {
            if (error.statusCode === 401)
                throw new Error(ERROR_MESSAGES.BROWSERSTACK_AUTHENTICATION_FAILED());

            throw error;
        });

    if (executeImmediately) {
        let result = null;

        currentRequestPromise = currentRequestPromise
            .then(promiseResult => {
                result = promiseResult;
            })
            .then(() => delay(API_REQUEST_DELAY))
            .then(() => result);
    }
    else
        apiRequestPromise = currentRequestPromise.then(() => delay(API_REQUEST_DELAY));

    return currentRequestPromise;
}
<|endoftext|>