<commit_before>var github = (function(){
  function render(target, repos){
    var i = 0, fragment = '', t = $(target)[0];

    for(i = 0; i < repos.length; i++) {
      fragment += '
'+repos[i].name+'
'+repos[i].description+'

';
    }
    t.innerHTML = fragment;
  }
  return {
    showRepos: function(options){
      $.ajax({
          url: "https://api.github.com/users/"+options.user+"/repos?callback=?"
        , type: 'jsonp'
        , error: function (err) { $(options.target + ' li.loading').addClass('error').text("Error loading feed"); }
        , success: function(data) {
          var repos = [];
          if (!data || !data.data) { return; }
          for (var i = 0; i < data.data.length; i++) {
            if (options.skip_forks && data.data[i].fork) { continue; }
            repos.push(data.data[i]);
          }
          repos.sort(function(a, b) {
            var aDate = new Date(a.pushed_at).valueOf(),
                bDate = new Date(b.pushed_at).valueOf();

            if (aDate === bDate) { return 0; }
            return aDate > bDate ? -1 : 1;
          });

          if (options.count) { repos.splice(options.count); }
          render(options.target, repos);
        }
      });
    }
  };
})();
<commit_msg>Updated Github API to V3.<commit_after>var github = (function(){
  function render(target, repos){
    var i = 0, fragment = '', t = $(target)[0];

    for(i = 0; i < repos.length; i++) {
      fragment += '
'+repos[i].name+'
'+(repos[i].description||'')+'

';
    }
    t.innerHTML = fragment;
  }
  return {
    showRepos: function(options){
      $.ajax({
          url: "https://api.github.com/users/"+options.user+"/repos?callback=?"
        , type: 'jsonp'
        , error: function (err) { $(options.target + ' li.loading').addClass('error').text("Error loading feed"); }
        , success: function(data) {
          var repos = [];
          if (!data || !data.data) { return; }
          for (var i = 0; i < data.data.length; i++) {
            if (options.skip_forks && data.data[i].fork) { continue; }
            repos.push(data.data[i]);
          }
          repos.sort(function(a, b) {
            var aDate = new Date(a.pushed_at).valueOf(),
                bDate = new Date(b.pushed_at).valueOf();

            if (aDate === bDate) { return 0; }
            return aDate > bDate ? -1 : 1;
          });

          if (options.count) { repos.splice(options.count); }
          render(options.target, repos);
        }
      });
    }
  };
})();
<|endoftext|>


<commit_before>function BowlingGame() {
  this.ball = 1;
  this.frameCounter = 1;
  this.frameHolder = [];
  this.rollTracker = [];
};

BowlingGame.prototype.roll = function(pins){
  this.rollTracker.push(pins);
  if ( this.ball === 1 ) {
        this.frameHolder[0] = pins;
        this.ball++;
        return this.frameValidate();
  } else if ( this.ball === 2 ) {
        this.frameHolder[1] = pins;
        return this.frameValidate();
        this.nextFrame();
  }
};

// BowlingGame.prototype.tenthFrame = function() {
//   if ( this.frameHolder[0] === 10 && this.frameCounter === 10) {
//       return 'Strike bonus roll';
//   } else {
//       return 'TESTING PLEASE WORK';
//   }
// };

BowlingGame.prototype.frameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
    if ( validate > 10 ) {
      this.ball = 2;
      this.rollTracker.pop();
      this.frameHolder.pop();
      throw new Error("Oops! You can't hit more than 10 pins per frame!");
  } else if ( this.frameHolder[0] === 10 && this.frameCounter != 10) {
      this.nextFrame();
      return 'X';
  } else if ( validate === 10 && this.ball === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
      return '/';
  } else if ( this.frameHolder.length === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
  } else if ( this.frameHolder[0] === 10 && this.frameCounter === 10 ) {
      return 'Strike bonus roll';
  } else if ( validate === 10 && this.frameHolder.length === 2 && this.frameCounter === 10 ) {
      return 'Spare bonus roll';
  }
};

BowlingGame.prototype.nextFrame = function(){
  this.ball = 1;
  this.frameCounter++;
  this.frameHolder = [];
  if ( this.frameCounter > 10 ) {
      throw new Error("You've played 10 frames, start a new game!")
  }
};

BowlingGame.prototype.autoRoll = function(){
  pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  if ( this.ball === 1 ) {
      roll_1 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_1);
      this.frameHolder[0] = roll_1;
      this.ball++;
      return roll_1;
  } else if ( this.ball === 2 ) {
      roll_2 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_2);
      this.frameHolder[1] = roll_2;
      this.autoFrameValidate();
      return roll_2;
  }
};

BowlingGame.prototype.autoFrameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
      if (validate > 10 ) {
        this.rollTracker.pop();
        this.frameHolder.pop();
        this.autoRoll().roll_2;
    } else {
      return this.frameValidate();
    }
  };

BowlingGame.prototype.score = function() {
  let calc = 0;
  let i = 0;
  for (var frame = 0; frame < 10; frame++) {
      if (this.scoreStrike(i)) {
          calc += 10 + this.rollTracker[i+1] + this.rollTracker[i+2];
          i++;
      } else if (this.scoreSpare(i)){
          calc += 10 + this.rollTracker[i+2];
          i+=2;
      } else {
          calc += this.rollTracker[i] + this.rollTracker[i+1];
          i+=2;
      }
  }
  return calc;
};

BowlingGame.prototype.gameOver = function() {
  return 'game over';
  if (this.score() < 150 ) {
        return "Ouch you need practice!";
        return 'your final score:' + this.score();
  } else if (this.score() > 250 ) {
        return "Woah! Are you a bowling pro?!";
        return 'your final score:' + this.score();
  } else {
        return "Not too bad, keep bowling!";
        return 'your final score:' + this.score();
  }
};

BowlingGame.prototype.frameLayout = function(){
  frames = this.rollTracker;
  frame = 1;
  for (i = 0; frames; i++) {
    // add logic for returning frames in pairs from index 0
  }
};

BowlingGame.prototype.scoreSpare = function(i) {
  return this.rollTracker[i] + this.rollTracker[i+1] === 10;
};

BowlingGame.prototype.scoreStrike = function(i) {
  return this.rollTracker[i] === 10;
};
<commit_msg>Defining end of game messages<commit_after>function BowlingGame() {
  this.ball = 1;
  this.frameCounter = 1;
  this.frameHolder = [];
  this.rollTracker = [];
};

BowlingGame.prototype.roll = function(pins){
  this.rollTracker.push(pins);
  if ( this.ball === 1 ) {
        this.frameHolder[0] = pins;
        this.ball++;
        return this.frameValidate();
  } else if ( this.ball === 2 ) {
        this.frameHolder[1] = pins;
        return this.frameValidate();
        this.nextFrame();
  }
};

// BowlingGame.prototype.tenthFrame = function() {
//   if ( this.frameHolder[0] === 10 && this.frameCounter === 10) {
//       return 'Strike bonus roll';
//   } else {
//       return 'TESTING PLEASE WORK';
//   }
// };

BowlingGame.prototype.frameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
    if ( validate > 10 ) {
      this.ball = 2;
      this.rollTracker.pop();
      this.frameHolder.pop();
      throw new Error("Oops! You can't hit more than 10 pins per frame!");
  } else if ( this.frameHolder[0] === 10 && this.frameCounter != 10) {
      this.nextFrame();
      return 'X';
  } else if ( validate === 10 && this.ball === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
      return '/';
  } else if ( this.frameHolder.length === 2 && this.frameCounter != 10 ) {
      this.nextFrame();
  } else if ( this.frameHolder[0] === 10 && this.frameCounter === 10 ) {
      return 'Strike bonus roll';
  } else if ( validate === 10 && this.frameHolder.length === 2 && this.frameCounter === 10 ) {
      return 'Spare bonus roll';
  } else if ( validate != 10 && this.frameHolder.length === 2 && this.frameCounter === 10 ) {
      return 'Game over';
  }
};

BowlingGame.prototype.nextFrame = function(){
  this.ball = 1;
  this.frameCounter++;
  this.frameHolder = [];
  if ( this.frameCounter > 10 ) {
      throw new Error("You've played 10 frames, start a new game!")
  }
};

BowlingGame.prototype.autoRoll = function(){
  pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
  if ( this.ball === 1 ) {
      roll_1 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_1);
      this.frameHolder[0] = roll_1;
      this.ball++;
      return roll_1;
  } else if ( this.ball === 2 ) {
      roll_2 = pins[Math.floor(Math.random() * pins.length)];
      this.rollTracker.push(roll_2);
      this.frameHolder[1] = roll_2;
      this.autoFrameValidate();
      return roll_2;
  }
};

BowlingGame.prototype.autoFrameValidate = function() {
  validate = this.frameHolder.reduce(function(a,b){
    return a+b;
  });
      if (validate > 10 ) {
        this.rollTracker.pop();
        this.frameHolder.pop();
        this.autoRoll().roll_2;
    } else {
      return this.frameValidate();
    }
  };

BowlingGame.prototype.score = function() {
  let calc = 0;
  let i = 0;
  for (var frame = 0; frame < 10; frame++) {
      if (this.scoreStrike(i)) {
          calc += 10 + this.rollTracker[i+1] + this.rollTracker[i+2];
          i++;
      } else if (this.scoreSpare(i)){
          calc += 10 + this.rollTracker[i+2];
          i+=2;
      } else {
          calc += this.rollTracker[i] + this.rollTracker[i+1];
          i+=2;
      }
  }
  return calc;
};

BowlingGame.prototype.gameOver = function() {
  return 'game over';
  if (this.score() < 150 ) {
        return "Ouch you need practice!";
        return 'your final score:' + this.score();
  } else if (this.score() > 250 ) {
        return "Woah! Are you a bowling pro?!";
        return 'your final score:' + this.score();
  } else {
        return "Not too bad, keep bowling!";
        return 'your final score:' + this.score();
  }
};

BowlingGame.prototype.frameLayout = function(){
  frames = this.rollTracker;
  frame = 1;
  for (i = 0; frames; i++) {
    // add logic for returning frames in pairs from index 0
  }
};

BowlingGame.prototype.scoreSpare = function(i) {
  return this.rollTracker[i] + this.rollTracker[i+1] === 10;
};

BowlingGame.prototype.scoreStrike = function(i) {
  return this.rollTracker[i] === 10;
};
<|endoftext|>


<commit_before>var CronJob = require('cron').CronJob;

module.exports = {
    type: 'ChuckNorrisJokes',
    image: 'http://cdn.marketplaceimages.windowsphone.com/v8/images/9de9475b-92c7-4617-b27b-6d1025c0ff90?imageType=ws_icon_large',
    description: 'Get a random daily Chuck Norris joke',
    schema: {
    },
    instance: function(user, emit) {
        var interval;
        return {
            start: function() {
              var dailyJob = new CronJob({
                //cronTime: '0 0 8 1/1 * ? *',
                cronTime: '0 0/5 * 1/1 * ? *',
                onTick: function() {
                  emit({
                      value: this.getChuckNorrisFact()
                  });
                },
                start: false,
                timeZone: "Europe/England"
              });
              dailyJob.start();
            },
            stop: function() {
              dailyJob.stop();
            }
        };
    },
    getChuckNorrisFact: function() {
      // $.get('http://api.icndb.com/jokes/random', function(response){
      //   return response.value.joke;
      // });
      return "BLARG";
    }
};
<commit_msg>chuck and broken gmail<commit_after>var CronJob = require('cron').CronJob;
var rest = require('superagent');

module.exports = {
    type: 'Chuck Norris Jokes',
    image: 'http://cdn.marketplaceimages.windowsphone.com/v8/images/9de9475b-92c7-4617-b27b-6d1025c0ff90?imageType=ws_icon_large',
    description: 'Get a random daily Chuck Norris joke',
    schema: {
    },
    getChuckNorrisFact: function() {

    },
    instance: function(user, emit) {
        var interval, dailyJob = new CronJob({
          cronTime: '00 00 08 * * 1-7',
          onTick: function() {
            rest.get('http://api.icndb.com/jokes/random', function(response){
              emit({
                  value: response.body.value.joke
              });
            });
          },
          start: false
        });

        return {
            start: function() {
              dailyJob.start();
            },
            stop: function() {
              dailyJob.stop();
            }
        };
    }
};
<|endoftext|>


<commit_before>import {
    AbstractChart,
    mergeBase,
    Globals,
    uuid
} from 'vizart-core';
import TooltipTpl from './tooltip-tpl';
import './tooltip.css';
import { select, mouse } from 'd3-selection';

class AbstractCartesianChart extends AbstractChart {
    constructor(canvasId, _userOptions) {
        super(canvasId, _userOptions);

        this._getMetric = ()=> this._options.data.y[0];
        this._getDimension = ()=> this._options.data.x;
        this._getDimensionVal = d=> d[this._getDimension().accessor];
        this._getMetricVal = d=> d[this._getMetric().accessor];
        this._x = d=> this._getDimension().scale(this._getDimensionVal(d));
        this._y = d=> this._getMetric().scale(this._getMetricVal(d));

        this._c = (d)=> {
            if (d.color) {
                return d.color;
            }

            switch (this._options.color.type){
                case Globals.ColorType.CATEGORICAL:
                    return this._color(this._getDimensionVal(d));
                case Globals.ColorType.GRADIENT:
                case Globals.ColorType.DISTINCT:
                    return this._color(this._getMetricVal(d));
                default:
                    return this._color(this._getMetricVal(d));
            }
        };
    }

    render(_data) {
        super.render(_data);

        this._tooltip = select(this._containerId)
            .append("div")
            .attr('id', 'tooltip-' + uuid())
            .attr('class', 'vizart-tooltip')
            .style("opacity", 0);
    }

    sort(_accessor, direction) {
        this._options.ordering = {
            accessor: _accessor,
            direction: direction
        };

        this.update();
    };

    _hasNegativeValue() {
        let hasNegative = false;

        dataLoop:
        for (let d of this.data()) {
            for (let _y of this._options.data.y) {
                if (d[_y.accessor] < 0) {
                    hasNegative = true;
                    break dataLoop;
                }
            }
        }

        return hasNegative;
    }
    _getTooltipHTML(d) {
        return TooltipTpl
            .replace("{{header}}", this._getDimensionVal(d))
            .replace("{{name}}", this._getMetric().name)
            .replace("{{value}}", this._getMetricVal(d))
            .replace("{{borderStroke}}", this._c(d));
    }

    //todo not a nice way to use 'this', need to improve
    _bindTooltip(_selector, polar = false) {
        let that = this;

        function _mouseMove(d) {
            that._tooltip
                .transition()
                .duration(that._options.tooltip.duration)
                .style("opacity", 1);

            let coordinates = mouse(this);
            let x = coordinates[0];
            let y = coordinates[1];

            if (polar === true) {
                that._tooltip.style("left", (x + (that._options.chart.width / 2)) + "px")
                    .style("top", (y + (that._options.chart.height / 2) +  90) + "px")
                    .html(that._getTooltipHTML(d));
            } else {
                that._tooltip.style("left", x < 40 ? x : (x - 22) + "px")
                    .style("top", y < 40 ? y + 34 : (y - 34) + "px")
                    .html( that._getTooltipHTML(d));
            }
        };

        function _mouseOut() {
            that._tooltip.transition()
                .duration(that._options.tooltip.duration)
                .style("opacity", 0)
        }

        _selector
            .on("mousemove", _mouseMove)
            .on("mouseout", _mouseOut);
    }

}

export default AbstractCartesianChart;<commit_msg>overlay front canvas and hidden canvas on svg<commit_after>import {
    AbstractChart,
    mergeBase,
    Globals,
    uuid
} from 'vizart-core';
import TooltipTpl from './tooltip-tpl';
import './tooltip.css';
import { select, mouse } from 'd3-selection';

class AbstractCartesianChart extends AbstractChart {
    constructor(canvasId, _userOptions) {
        super(canvasId, _userOptions);

        this._getMetric = ()=> this._options.data.y[0];
        this._getDimension = ()=> this._options.data.x;
        this._getDimensionVal = d=> d[this._getDimension().accessor];
        this._getMetricVal = d=> d[this._getMetric().accessor];
        this._x = d=> this._getDimension().scale(this._getDimensionVal(d));
        this._y = d=> this._getMetric().scale(this._getMetricVal(d));

        this._c = (d)=> {
            if (d.color) {
                return d.color;
            }

            switch (this._options.color.type){
                case Globals.ColorType.CATEGORICAL:
                    return this._color(this._getDimensionVal(d));
                case Globals.ColorType.GRADIENT:
                case Globals.ColorType.DISTINCT:
                    return this._color(this._getMetricVal(d));
                default:
                    return this._color(this._getMetricVal(d));
            }
        };

        this._fontCanvasId = 'front-canvas' + uuid();
        this._frontCanvasId = 'color-canvas-' + uuid();
        this._frontCanvas;
        this._hiddenCanvas;
        this._frontContext;
        this._hiddenContext;
    }

    render(_data) {
        super.render(_data);

        this._frontCanvas = select(this._containerId)
            .append("canvas")
            .attr("id", this._fontCanvasId)
            .style('display', 'block')
            .style("width", this._options.chart.innerWidth + "px")
            .style("height", this._options.chart.innerHeight + "px")
            .style('margin', this._options.chart.margin.top + 'px 0 0 ' + this._options.chart.margin.left + 'px ')
            .attr('width', this._options.chart.innerWidth * devicePixelRatio)
            .attr('height', this._options.chart.innerHeight * devicePixelRatio);

        this._hiddenCanvas = select(this._containerId)
            .append("canvas")
            .attr("id", this._frontCanvasId)
            .style('display', 'none')
            .style("width", this._options.chart.innerWidth + "px")
            .style("height", this._options.chart.innerHeight + "px")
            .style('margin', this._options.chart.margin.top + 'px 0 0 ' + this._options.chart.margin.left + 'px ')
            .attr('width', this._options.chart.innerWidth * devicePixelRatio)
            .attr('height', this._options.chart.innerHeight * devicePixelRatio);

        this._frontContext = this._frontCanvas.node().getContext('2d');
        this._frontContext.clearRect(0, 0, this._options.chart.innerWidth, this._options.chart.innerHeight);

        this._hiddenContext = this._hiddenCanvas.node().getContext("2d");
        this._hiddenContext.clearRect(0, 0, this._options.chart.innerWidth, this._options.chart.innerHeight);

        this._container
            .style('position', 'absolute')
            .style('top', 0)
            .style('left', 0);

        this._tooltip = select(this._containerId)
            .append("div")
            .attr('id', 'tooltip-' + uuid())
            .attr('class', 'vizart-tooltip')
            .style("opacity", 0);
    }

    sort(_accessor, direction) {
        this._options.ordering = {
            accessor: _accessor,
            direction: direction
        };

        this.update();
    };

    _hasNegativeValue() {
        let hasNegative = false;

        dataLoop:
        for (let d of this.data()) {
            for (let _y of this._options.data.y) {
                if (d[_y.accessor] < 0) {
                    hasNegative = true;
                    break dataLoop;
                }
            }
        }

        return hasNegative;
    }
    _getTooltipHTML(d) {
        return TooltipTpl
            .replace("{{header}}", this._getDimensionVal(d))
            .replace("{{name}}", this._getMetric().name)
            .replace("{{value}}", this._getMetricVal(d))
            .replace("{{borderStroke}}", this._c(d));
    }

    //todo not a nice way to use 'this', need to improve
    _bindTooltip(_selector, polar = false) {
        let that = this;

        function _mouseMove(d) {
            that._tooltip
                .transition()
                .duration(that._options.tooltip.duration)
                .style("opacity", 1);

            let coordinates = mouse(this);
            let x = coordinates[0];
            let y = coordinates[1];

            if (polar === true) {
                that._tooltip.style("left", (x + (that._options.chart.width / 2)) + "px")
                    .style("top", (y + (that._options.chart.height / 2) +  90) + "px")
                    .html(that._getTooltipHTML(d));
            } else {
                that._tooltip.style("left", x < 40 ? x : (x - 22) + "px")
                    .style("top", y < 40 ? y + 34 : (y - 34) + "px")
                    .html( that._getTooltipHTML(d));
            }
        };

        function _mouseOut() {
            that._tooltip.transition()
                .duration(that._options.tooltip.duration)
                .style("opacity", 0)
        }

        _selector
            .on("mousemove", _mouseMove)
            .on("mouseout", _mouseOut);
    }

}

export default AbstractCartesianChart;<|endoftext|>


<commit_before>'use strict';

const CachingWriter = require('broccoli-caching-writer');
const fs = require('fs');
const path = require('path');
const generateBrowserconfigFromConfiguration = require('../generate-browserconfig-from-configuration');
const getManifestConfiguration = require('../get-manifest-configuration');
const fuzzyPurgeRequireEntry = require('../fuzzy-purge-require-entry');

class GenerateBrowserconfig extends CachingWriter {
  constructor(inputNode, options) {
    super([inputNode], {
      annotation: 'generate browserconfig.xml'
    });

    this.options = options;
  }

  clearConfigGeneratorCache() {
    fuzzyPurgeRequireEntry(
      path.join(this.options.project.root, 'config', 'manifest.js')
    );
  }

  build() {
    this.clearConfigGeneratorCache();

    let outputFilePath = path.join(this.outputPath, this.options.browserconfigName);
    fs.writeFileSync(
      outputFilePath,
      this.generateBrowserconfig()
    );
  }

  generateBrowserconfig() {
    return generateBrowserconfigFromConfiguration(
      getManifestConfiguration(this.options.project, this.options.env),
      this.options.ui
    );
  }
}

module.exports = GenerateBrowserconfig;
<commit_msg>Only generate if manifest.ms is truthy<commit_after>'use strict';

const CachingWriter = require('broccoli-caching-writer');
const fs = require('fs');
const path = require('path');
const generateBrowserconfigFromConfiguration = require('../generate-browserconfig-from-configuration');
const getManifestConfiguration = require('../get-manifest-configuration');
const fuzzyPurgeRequireEntry = require('../fuzzy-purge-require-entry');

class GenerateBrowserconfig extends CachingWriter {
  constructor(inputNode, options) {
    super([inputNode], {
      annotation: 'generate browserconfig.xml'
    });

    this.options = options;
  }

  clearConfigGeneratorCache() {
    fuzzyPurgeRequireEntry(
      path.join(this.options.project.root, 'config', 'manifest.js')
    );
  }

  build() {
    this.clearConfigGeneratorCache();

    const options = this.options;

    const manifest = getManifestConfiguration(options.project, options.env);

    if (manifest.ms) {
      const outputFilePath = path.join(
        this.outputPath, options.browserconfigName
      );
      const browserconfig = generateBrowserconfigFromConfiguration(
        manifest, options.ui
      );

      fs.writeFileSync(outputFilePath, browserConfig);
    }
  }
}

module.exports = GenerateBrowserconfig;
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import {Disposable} from 'event-kit';

export default class Accumulator extends React.Component {
  static propTypes = {
    // Relay props
    relay: PropTypes.shape({
      hasMore: PropTypes.func.isRequired,
      loadMore: PropTypes.func.isRequired,
      isLoading: PropTypes.func.isRequired,
    }).isRequired,
    resultBatch: PropTypes.arrayOf(PropTypes.any).isRequired,

    // Control props
    pageSize: PropTypes.number.isRequired,
    waitTimeMs: PropTypes.number.isRequired,

    // Render prop. Called with (error, full result list, loading) each time more results arrive. Return value is
    // rendered as a child element.
    children: PropTypes.func,

    // Side-effect callback prop. Called with (error, full result list, loading) each time it's updated outside of the
    // render cycle. Use this callback when you need to trigger a side effect from the callback result (like a
    // setState() or forceUpdate() call).
    handleResults: PropTypes.func,

    // Called right after refetch happens
    onDidRefetch: PropTypes.func.isRequired,
  }

  static defaultProps = {
    children: () => null,
    handleResults: () => {},
  }

  constructor(props) {
    super(props);

    this.sub = new Disposable();
    this.nextUpdateID = null;
    this.state = {
      error: null,
      all: this.props.resultBatch,
    };
  }

  componentWillUnmount() {
    this.sub.dispose();
  }

  componentDidMount() {
    if (this.props.onDidRefetch) {
      this.sub = this.props.onDidRefetch(this.reset);
    }
    this.loadEverything();
  }

  loadEverything() {
    this.props.handleResults(this.state.error, this.props.resultBatch, this.props.relay.hasMore());
    this.attemptToLoadMore();
  }

  reset = () => {
    this.setState({
      error: null,
    }, this.loadEverything);
  }

  render() {
    return this.props.children(this.state.error, this.props.resultBatch, this.props.relay.hasMore());
  }

  attemptToLoadMore = () => {
    this.nextUpdateID = null;

    if (!this.props.relay.hasMore()) {
      return;
    }

    /* istanbul ignore if */
    if (this.props.relay.isLoading()) {
      return;
    }

    this.props.relay.loadMore(this.props.pageSize, this.accumulate);
  }

  accumulate = error => {
    if (error) {
      this.setState({error}, () => {
        this.props.handleResults(error, this.props.resultBatch, this.props.relay.hasMore());
      });
    } else {
      this.props.handleResults(this.state.error, this.props.resultBatch, this.props.relay.hasMore());
      /* istanbul ignore next */
      if (this.props.waitTimeMs > 0 && this.nextUpdateID === null) {
        this.nextUpdateID = setTimeout(this.attemptToLoadMore, this.props.waitTimeMs);
      } else {
        this.attemptToLoadMore();
      }
    }
  }
}
<commit_msg>stop calling handleResults from accumulator<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import {Disposable} from 'event-kit';

export default class Accumulator extends React.Component {
  static propTypes = {
    // Relay props
    relay: PropTypes.shape({
      hasMore: PropTypes.func.isRequired,
      loadMore: PropTypes.func.isRequired,
      isLoading: PropTypes.func.isRequired,
    }).isRequired,
    resultBatch: PropTypes.arrayOf(PropTypes.any).isRequired,

    // Control props
    pageSize: PropTypes.number.isRequired,
    waitTimeMs: PropTypes.number.isRequired,

    // Render prop. Called with (error, full result list, loading) each time more results arrive. Return value is
    // rendered as a child element.
    children: PropTypes.func,

    // Side-effect callback prop. Called with (error, full result list, loading) each time it's updated outside of the
    // render cycle. Use this callback when you need to trigger a side effect from the callback result (like a
    // setState() or forceUpdate() call).
    handleResults: PropTypes.func,

    // Called right after refetch happens
    onDidRefetch: PropTypes.func.isRequired,
  }

  static defaultProps = {
    children: () => null,
    handleResults: () => {},
  }

  constructor(props) {
    super(props);

    this.sub = new Disposable();
    this.nextUpdateID = null;
    this.state = {
      error: null,
      all: this.props.resultBatch,
    };
  }

  componentWillUnmount() {
    this.sub.dispose();
  }

  componentDidMount() {
    if (this.props.onDidRefetch) {
      this.sub = this.props.onDidRefetch(this.reset);
    }
    this.loadEverything();
  }

  loadEverything() {
    // this.props.handleResults(this.state.error, this.props.resultBatch, this.props.relay.hasMore());
    this.attemptToLoadMore();
  }

  reset = () => {
    this.setState({
      error: null,
    }, this.loadEverything);
  }

  render() {
    return this.props.children(this.state.error, this.props.resultBatch, this.props.relay.hasMore());
  }

  attemptToLoadMore = () => {
    this.nextUpdateID = null;

    if (!this.props.relay.hasMore()) {
      return;
    }

    /* istanbul ignore if */
    if (this.props.relay.isLoading()) {
      return;
    }

    this.props.relay.loadMore(this.props.pageSize, this.accumulate);
  }

  accumulate = error => {
    if (error) {
      this.setState({error});
    } else {
      // this.props.handleResults(this.state.error, this.props.resultBatch, this.props.relay.hasMore());
      /* istanbul ignore next */
      if (this.props.waitTimeMs > 0 && this.nextUpdateID === null) {
        this.nextUpdateID = setTimeout(this.attemptToLoadMore, this.props.waitTimeMs);
      } else {
        this.attemptToLoadMore();
      }
    }
  }
}
<|endoftext|>


<commit_before>'use strict';

let Promise = require('bluebird'),
  harBuilder = require('../../support/har_builder'),
  trafficShapeParser = require('../../support/traffic_shape_parser');

class FirefoxDelegate {
  constructor(options) {
    this.trafficShapeConfig = trafficShapeParser.parseTrafficShapeConfig(options);

    if (this.trafficShapeConfig || options.basicAuth || options.skipHar) {
      throw new Error('Firefox native har can\'t be combined with custom connection speeds, ' +
        'basic auth or skipping har generation.');
    }
  }

  onStart() {
    return Promise.resolve();
  }

  onStartRun() {
    this.hars = [];
    return Promise.resolve();
  }

  onStartIteration() {
    return Promise.resolve();
  }

  onStopIteration(runner) {
    const script = `
            var callback = arguments[arguments.length - 1];
            function triggerExport() {
              HAR.triggerExport({'token':'test', 'getData':true})
                .then((result) => {
                  // Fix timings via performance.timing, see https://github.com/firebug/har-export-trigger/issues/5
                  var har = JSON.parse(result.data);
                  var t = performance.timing;
                  var pageTimings = har.log.pages[0].pageTimings;
                  pageTimings.onContentLoad = t.domContentLoadedEventStart - t.navigationStart;
                  pageTimings.onLoad = t.loadEventStart - t.navigationStart;

                  return callback(JSON.stringify(har));
              })
              .catch((e) => callback(e));
            };
            if (typeof HAR === 'undefined') {
              addEventListener('har-api-ready', triggerExport, false);
            } else {
              triggerExport();
            }`;
    return runner.runAsyncScript(script).then(JSON.parse).then((har) => {
      // TODO potentially remove incomplete entries, such as with response.status=0 or time=null
      this.hars.push(har);
    });
  }

  onStopRun(result) {
    if (this.hars.length > 0) {
      result.har = harBuilder.mergeHars(this.hars);
    }

    return Promise.resolve();
  }

  onStop() {
    return Promise.resolve();
  }
}

module.exports = FirefoxDelegate;
<commit_msg>Handle errors from Firefox har export plugin.<commit_after>'use strict';

let Promise = require('bluebird'),
  log = require('intel'),
  harBuilder = require('../../support/har_builder'),
  trafficShapeParser = require('../../support/traffic_shape_parser');

class FirefoxDelegate {
  constructor(options) {
    this.trafficShapeConfig = trafficShapeParser.parseTrafficShapeConfig(options);

    if (this.trafficShapeConfig || options.basicAuth || options.skipHar) {
      throw new Error('Firefox native har can\'t be combined with custom connection speeds, ' +
        'basic auth or skipping har generation.');
    }
  }

  onStart() {
    return Promise.resolve();
  }

  onStartRun() {
    this.hars = [];
    return Promise.resolve();
  }

  onStartIteration() {
    return Promise.resolve();
  }

  onStopIteration(runner) {
    const script = `
            var callback = arguments[arguments.length - 1];
            function triggerExport() {
              HAR.triggerExport({'token':'test', 'getData':true})
                .then((result) => {
                  // Fix timings via performance.timing, see https://github.com/firebug/har-export-trigger/issues/5
                  var har = JSON.parse(result.data);
                  var t = performance.timing;
                  var pageTimings = har.log.pages[0].pageTimings;
                  pageTimings.onContentLoad = t.domContentLoadedEventStart - t.navigationStart;
                  pageTimings.onLoad = t.loadEventStart - t.navigationStart;

                  return callback({'har': JSON.stringify(har)});
              })
              .catch((e) => callback({'error': e}));
            };
            if (typeof HAR === 'undefined') {
              addEventListener('har-api-ready', triggerExport, false);
            } else {
              triggerExport();
            }`;
    return runner.runAsyncScript(script).then((reply) => {
      if (reply.error) {
        log.warn('Error from Firefox HAR generation: ' + JSON.stringify(reply.error));
        throw reply.error;
      } else {
        return reply.har;
      }
    }).then(JSON.parse).then((har) => {
      // TODO potentially remove incomplete entries, such as with response.status=0 or time=null
      this.hars.push(har);
    });
  }

  onStopRun(result) {
    if (this.hars.length > 0) {
      result.har = harBuilder.mergeHars(this.hars);
    }

    return Promise.resolve();
  }

  onStop() {
    return Promise.resolve();
  }
}

module.exports = FirefoxDelegate;
<|endoftext|>


<commit_before>const { describe, it } = global;
import { expect } from 'chai';
import ClientApi from '../client_api';
import sinon from 'sinon';

function getClientApi() {
  const syncedStore = { aa: 30 };
  const storyStore = { aa: 10 };
  const api = new ClientApi({ syncedStore, storyStore });

  return api;
}

describe('client.ClientApi', () => {
  describe('constructor', () => {
    it('should set _syncedStore & _storyStore properly', () => {
      const syncedStore = { aa: 30 };
      const storyStore = { aa: 10 };
      const api = new ClientApi({ syncedStore, storyStore });

      expect(api._syncedStore).to.be.equal(syncedStore);
      expect(api._storyStore).to.be.equal(storyStore);
    });
  });

  describe('storiesOf', () => {
    it('should return an api where we can add stories', () => {
      const api = getClientApi();
      const chainApi = api.storiesOf('kind');
      expect(typeof chainApi.add).to.be.equal('function');
    });

    it('should remove story of the given kind, when hot module is disposing', () => {
      let doDispose = null;
      const hotModule = {
        hot: {
          dispose: (fn) => { doDispose = fn; },
        },
      };

      const api = getClientApi();
      api._storyStore.removeStoryKind = sinon.stub();
      api.storiesOf('kind', hotModule);

      doDispose();

      expect(api._storyStore.removeStoryKind.args[0][0]).to.be.equal('kind');
    });
  });

  describe('storiesOf.add', () => {
    it('should add a given story', () => {
      const api = getClientApi();
      const handle = () => {};

      api._storyStore.addStory = sinon.stub();
      api.storiesOf('kind')
        .add('name', handle);

      const args = api._storyStore.addStory.args[0];
      expect(args[0]).to.be.equal('kind');
      expect(args[1]).to.be.equal('name');
      expect(args[2]).to.be.equal(handle);
    });

    it('should support method chaining', () => {
      const api = getClientApi();
      const handle = () => {};

      api._storyStore.addStory = sinon.stub();
      api.storiesOf('kind')
        .add('name', handle)
        .add('name2', handle);
    });
  });

  describe('storiesOf.addDecorator', () => {
    it('should wrap a given story', () => {
      const api = getClientApi();
      const handle = () => ['h'];
      const decorate1 = fn => fn().concat('d1');
      const decorate2 = fn => fn().concat('d2');

      api._storyStore.addStory = sinon.stub();
      api.storiesOf('kind')
        .addDecorator(decorate1)
        .addDecorator(decorate2)
        .add('name', handle);

      const args = api._storyStore.addStory.args[0];
      expect(args[0]).to.be.equal('kind');
      expect(args[1]).to.be.equal('name');
      expect(args[2]).to.be.a('function');

      const decoratedFn = args[2];
      expect(decoratedFn()).to.deep.equal(['h', 'd1', 'd2']);
    });
  });

  describe('action', () => {
    it('should send action info to the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({ actions: [] });
      api._syncedStore.setData = sinon.stub();

      const cb = api.action('hello');
      cb(10, 20);

      const args = api._syncedStore.setData.args[0];
      expect(args[0].actions).to.be.deep.equal([{
        data: {
          name: 'hello',
          args: [10, 20],
        },
        id: 1,
        count: 1,
      }]);
    });

    it('should only keep the latest 5 actions in the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        actions: [50, 40, 30, 20, 10],
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.action('hello');
      cb(10, 20);

      const args = api._syncedStore.setData.args[0];
      expect(args[0].actions.length).to.equal(5);
    });

    it('should replace any Synthetic Event with it\'s name', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({ actions: [] });
      api._syncedStore.setData = sinon.stub();

      const event = {
        preventDefault() {},
      };

      const cb = api.action('hello');
      cb(event);

      const args = api._syncedStore.setData.args[0];
      expect(args[0].actions).to.be.deep.equal([{
        data: {
          name: 'hello',
          args: ['[SyntheticEvent]'],
        },
        id: 1,
        count: 1,
      }]);
    });
  });

  describe('linkTo', () => {
    it('should send kind to the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        storyStore: [{ kind: 'Another Kind', stories: [] }],
        selectedKind: 'Some Kind',
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.linkTo('Another Kind');
      cb();

      const args = api._syncedStore.setData.args[0];
      expect(args[0].selectedKind).to.equal('Another Kind');
    });

    it('should send story to the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        storyStore: [{ kind: 'Another Kind', stories: [] }],
        selectedKind: 'Some Kind',
        selectedStory: 'Some Story',
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.linkTo('Another Kind', 'Another Story');
      cb();

      const args = api._syncedStore.setData.args[0];
      expect(args[0].selectedKind).to.equal('Another Kind');
      expect(args[0].selectedStory).to.equal('Another Story');
    });

    it('should allow functions for story and kind', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        storyStore: [{ kind: 'Another Kind', stories: [] }],
        selectedKind: 'Some Kind',
        selectedStory: 'Some Story',
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.linkTo(
        () => 'Another Kind',
        () => 'Another Story');
      cb();

      const args = api._syncedStore.setData.args[0];
      expect(args[0].selectedKind).to.equal('Another Kind');
      expect(args[0].selectedStory).to.equal('Another Story');
    });
  });
});
<commit_msg>Fix failing tests.<commit_after>const { describe, it } = global;
import { expect } from 'chai';
import ClientApi from '../client_api';
import sinon from 'sinon';

function getClientApi() {
  const syncedStore = { aa: 30 };
  const storyStore = { aa: 10 };
  const api = new ClientApi({ syncedStore, storyStore });

  return api;
}

describe('client.ClientApi', () => {
  describe('constructor', () => {
    it('should set _syncedStore & _storyStore properly', () => {
      const syncedStore = { aa: 30 };
      const storyStore = { aa: 10 };
      const api = new ClientApi({ syncedStore, storyStore });

      expect(api._syncedStore).to.be.equal(syncedStore);
      expect(api._storyStore).to.be.equal(storyStore);
    });
  });

  describe('storiesOf', () => {
    it('should return an api where we can add stories', () => {
      const api = getClientApi();
      const chainApi = api.storiesOf('kind');
      expect(typeof chainApi.add).to.be.equal('function');
    });

    it('should remove story of the given kind, when hot module is disposing', () => {
      let doDispose = null;
      const hotModule = {
        hot: {
          dispose: (fn) => { doDispose = fn; },
        },
      };

      const api = getClientApi();
      api._storyStore.removeStoryKind = sinon.stub();
      api.storiesOf('kind', hotModule);

      doDispose();

      expect(api._storyStore.removeStoryKind.args[0][0]).to.be.equal('kind');
    });
  });

  describe('storiesOf.add', () => {
    it('should add a given story', () => {
      const api = getClientApi();
      const handle = () => {};

      api._storyStore.addStory = sinon.stub();
      api.storiesOf('kind')
        .add('name', handle);

      const args = api._storyStore.addStory.args[0];
      expect(args[0]).to.be.equal('kind');
      expect(args[1]).to.be.equal('name');
      expect(args[2]).to.be.equal(handle);
    });

    it('should support method chaining', () => {
      const api = getClientApi();
      const handle = () => {};

      api._storyStore.addStory = sinon.stub();
      api.storiesOf('kind')
        .add('name', handle)
        .add('name2', handle);
    });
  });

  describe('storiesOf.addDecorator', () => {
    it('should wrap a given story', () => {
      const api = getClientApi();
      const handle = () => ['h'];
      const decorate1 = fn => fn().concat('d1');
      const decorate2 = fn => fn().concat('d2');

      api._storyStore.addStory = sinon.stub();
      api.storiesOf('kind')
        .addDecorator(decorate1)
        .addDecorator(decorate2)
        .add('name', handle);

      const args = api._storyStore.addStory.args[0];
      expect(args[0]).to.be.equal('kind');
      expect(args[1]).to.be.equal('name');
      expect(args[2]).to.be.a('function');

      const decoratedFn = args[2];
      expect(decoratedFn()).to.deep.equal(['h', 'd1', 'd2']);
    });
  });

  describe('action', () => {
    it('should send action info to the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({ actions: [] });
      api._syncedStore.setData = sinon.stub();

      const cb = api.action('hello');
      cb(10, 20);

      const args = api._syncedStore.setData.args[0];
      expect(args[0].actions).to.be.deep.equal([{
        data: {
          name: 'hello',
          args: [10, 20],
        },
        id: 1,
        count: 1,
      }]);
    });

    it('should only keep the latest 10 actions in the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        actions: [50, 40, 30, 20, 10, 50, 40, 30, 20, 10],
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.action('hello');
      cb(10, 20);

      const args = api._syncedStore.setData.args[0];
      expect(args[0].actions.length).to.equal(10);
    });

    it('should replace any Synthetic Event with it\'s name', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({ actions: [] });
      api._syncedStore.setData = sinon.stub();

      const event = {
        preventDefault() {},
      };

      const cb = api.action('hello');
      cb(event);

      const args = api._syncedStore.setData.args[0];
      expect(args[0].actions).to.be.deep.equal([{
        data: {
          name: 'hello',
          args: ['[SyntheticEvent]'],
        },
        id: 1,
        count: 1,
      }]);
    });
  });

  describe('linkTo', () => {
    it('should send kind to the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        storyStore: [{ kind: 'Another Kind', stories: [] }],
        selectedKind: 'Some Kind',
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.linkTo('Another Kind');
      cb();

      const args = api._syncedStore.setData.args[0];
      expect(args[0].selectedKind).to.equal('Another Kind');
    });

    it('should send story to the syncedStore', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        storyStore: [{ kind: 'Another Kind', stories: [] }],
        selectedKind: 'Some Kind',
        selectedStory: 'Some Story',
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.linkTo('Another Kind', 'Another Story');
      cb();

      const args = api._syncedStore.setData.args[0];
      expect(args[0].selectedKind).to.equal('Another Kind');
      expect(args[0].selectedStory).to.equal('Another Story');
    });

    it('should allow functions for story and kind', () => {
      const api = getClientApi();
      api._syncedStore.getData = () => ({
        storyStore: [{ kind: 'Another Kind', stories: [] }],
        selectedKind: 'Some Kind',
        selectedStory: 'Some Story',
      });
      api._syncedStore.setData = sinon.stub();

      const cb = api.linkTo(
        () => 'Another Kind',
        () => 'Another Story');
      cb();

      const args = api._syncedStore.setData.args[0];
      expect(args[0].selectedKind).to.equal('Another Kind');
      expect(args[0].selectedStory).to.equal('Another Story');
    });
  });
});
<|endoftext|>


<commit_before>const React = require('react');
const h = require('react-hyperscript');
const queryString = require('query-string');
const _ = require('lodash');
const Loader = require('react-loader');

const { BaseNetworkView } = require('../../common/components');
const { getLayoutConfig } = require('../../common/cy/layout');
const make_cytoscape = require('../../common/cy');

const { ServerAPI } = require('../../services');

const { ExpressionTable, applyExpressionData } = require('./expression-table');
const PaintMenu = require('./paint-menu');

const demoExpressions = require('./demo-expressions.json');
const demoPathways = require('./demo-pathway-results.json');

const paintMenuId = 'paintMenu';
const PaintViewConfig = {
  toolbarButtons: BaseNetworkView.config.toolbarButtons.concat({
    icon: 'format_paint',
    id: 'showPaintMenu',
    type: 'activateMenu',
    menuId: 'paintMenu',
    description: 'View expression data'
  }),
  menus: BaseNetworkView.config.menus.concat({
    id: paintMenuId,
    func: props => h(PaintMenu, props)
  }),
  useSearchBar: false
};

const getAugmentedSearchResults = (searchParam, expressions) => {
  const geneQueries = _.chunk(expressions.map(expression => expression.geneName), 15)
  .map(chunk => ServerAPI.querySearch({q: chunk.join(' ')}));

  const searchQuery = ServerAPI.querySearch({q: searchParam});

  return Promise.all([...geneQueries, searchQuery]).then(searchResults => {
    const uniqueResults = _.uniqBy(_.flatten(searchResults), result => result.uri);

    const pathwaysJSON = uniqueResults.map(result => ServerAPI.getGraphAndLayout(result.uri, 'latest'));

    return Promise.all(pathwaysJSON).then(pathways => {
      const processed = pathways.map(pathway => {
        const macromolecules = pathway.graph.nodes.filter(node => node.data.class === 'macromolecule');
        const genesInPathway = _.flattenDeep(_.uniq([...macromolecules.map(node => node.data.label), ...macromolecules.map(node => node.data.geneSynonyms)]));
        const genesInExpressionData = expressions.map(expression => expression.geneName);

        return {
          json: pathway,
          geneIntersection: _.intersection(genesInPathway, genesInExpressionData)
        };
      });

      return processed;
    });
  });
};

class Paint extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      cy: {},
      componentConfig: {},
      layoutConfig: {},
      networkJSON: {},
      networkMetadata: {
        name: '',
        datasource: '',
        comments: []
      },

      expressionsLoading: true,
      networkLoading: true,

      // enrichment data state
      expressionTable: {},
      selectedClass: '',
      selectedFunction: {
        name: 'mean',
        func: _.mean
      },

      // search results state
      searchParam: '',
      searchResults: [],

    };

    const query = queryString.parse(props.location.search);
    const searchParam = query.q;
    const enrichmentsURI = query.uri;

    if (enrichmentsURI == null || searchParam == null) {
      const network = demoPathways[0].json;
      const demoExpressionTable = new ExpressionTable(demoExpressions, network.graph);
      const layoutConfig = getLayoutConfig(network.layout);
      const componentConfig = PaintViewConfig;

      this.state = {
        cy: make_cytoscape({headless: true}),
        componentConfig: componentConfig,
        layoutConfig: layoutConfig,
        networkJSON: network.graph,
        networkMetadata: {
          uri: network.graph.pathwayMetadata.uri,
          name: _.get(network, 'graph.pathwayMetadata.title.0', 'Unknown Network'),
          datasource: _.get(network, 'graph.pathwayMetadata.dataSource.0', 'Unknown Data Source'),
          comments: network.graph.pathwayMetadata.comments,
          organism: network.graph.pathwayMetadata.organism
        },
        networkLoading: false,
        rawExpressions: demoExpressions,
        expressionTable: demoExpressionTable,
        selectedClass: _.get(demoExpressionTable.classes, '0', ''),
        expressionsLoading: false,
        searchParam: searchParam,
        searchResults: demoPathways,

        selectedSearchResult: network.graph.pathwayMetadata.uri,
        selectedFunction: {
          name: 'mean',
          func: _.mean
        }

      };

      const selectedFn = this.state.selectedFunction.func;
      const selectedClass = this.state.selectedClass;
      this.state.cy.on('network-loaded', () => applyExpressionData(this.state.cy, demoExpressionTable, selectedClass, selectedFn));
    } else {
      fetch(enrichmentsURI)
      .then(res => res.json())
      .then(json => {
        const expressions = _.get(json.dataSetExpressionList, '0.expressions', []);

        getAugmentedSearchResults(searchParam, expressions).then(pathwayResults => {

          // pathway results are sorted by gene expression intersection (largest to smallest)
          // take the largest gene intersection by default

          // see if there is a result that matches exactly
          let candidatePathway = _.find(pathwayResults, pathway => pathway.json.graph.pathwayMetadata.title[0] === searchParam);

          if (candidatePathway == null) {
            candidatePathway = pathwayResults.sort((p0, p1) => p1.geneIntersection.length - p0.geneIntersection.length)[0];
          }

          const network = candidatePathway.json;

          const expressionTable = new ExpressionTable(json, candidatePathway.json.graph);
          const layoutConfig = getLayoutConfig(network.layout);
          const componentConfig = PaintViewConfig;

          this.setState({
            cy: make_cytoscape({headless: true}),
            componentConfig: componentConfig,
            layoutConfig: layoutConfig,
            networkJSON: network.graph,
            networkMetadata: {
              uri: network.graph.pathwayMetadata.uri,
              name: _.get(network, 'graph.pathwayMetadata.title.0', 'Unknown Network'),
              datasource: _.get(network, 'graph.pathwayMetadata.dataSource.0', 'Unknown Data Source'),
              comments: network.graph.pathwayMetadata.comments,
              organism: network.graph.pathwayMetadata.organism
            },
            networkLoading: false,
            rawExpressions: json,
            expressionTable: expressionTable,
            selectedClass: _.get(expressionTable.classes, '0', ''),
            expressionsLoading: false,
            searchParam: searchParam,
            searchResults: pathwayResults,

            selectedSearchResult: candidatePathway.json.graph.pathwayMetadata.uri

            }, () => {
              const selectedFn = this.state.selectedFunction.func;
              const selectedClass = this.state.selectedClass;
              this.state.cy.on('network-loaded', () => applyExpressionData(this.state.cy, expressionTable, selectedClass, selectedFn));
          });
        });
      });
    }
  }

  render() {
    const state = this.state;

    const baseView = h(BaseNetworkView.component, {
      // generic base view props
      layoutConfig: state.layoutConfig,
      componentConfig: state.componentConfig,
      cy: state.cy,
      networkJSON: state.networkJSON,
      networkMetadata: state.networkMetadata,

      // paint specific props needed by the paint menu
      rawExpressions: state.rawExpressions,
      expressionTable: state.expressionTable,
      selectedFunction: state.selectedFunction,
      selectedClass: state.selectedClass,
      activeMenu: paintMenuId,

      searchParam: state.searchParam,
      searchResults: state.searchResults,
      selectedSearchResult: state.selectedSearchResult

    });

    const loadingView = h(Loader, { loaded: !(state.expressionsLoading || state.networkLoading), options: { left: '50%', color: '#16A085' }});

    // create a view shell loading view e.g looks like the view but its not
    const content = state.expressionsLoading || state.networkLoading ? loadingView : baseView;

    return h('div', [content]);
  }
}

module.exports = Paint;<commit_msg>paint - apply expression data immediately instead of waiting for the layout to finish<commit_after>const React = require('react');
const h = require('react-hyperscript');
const queryString = require('query-string');
const _ = require('lodash');
const Loader = require('react-loader');

const { BaseNetworkView } = require('../../common/components');
const { getLayoutConfig } = require('../../common/cy/layout');
const make_cytoscape = require('../../common/cy');

const { ServerAPI } = require('../../services');

const { ExpressionTable, applyExpressionData } = require('./expression-table');
const PaintMenu = require('./paint-menu');

const demoExpressions = require('./demo-expressions.json');
const demoPathways = require('./demo-pathway-results.json');

const paintMenuId = 'paintMenu';
const PaintViewConfig = {
  toolbarButtons: BaseNetworkView.config.toolbarButtons.concat({
    icon: 'format_paint',
    id: 'showPaintMenu',
    type: 'activateMenu',
    menuId: 'paintMenu',
    description: 'View expression data'
  }),
  menus: BaseNetworkView.config.menus.concat({
    id: paintMenuId,
    func: props => h(PaintMenu, props)
  }),
  useSearchBar: false
};

const getAugmentedSearchResults = (searchParam, expressions) => {
  const geneQueries = _.chunk(expressions.map(expression => expression.geneName), 15)
  .map(chunk => ServerAPI.querySearch({q: chunk.join(' ')}));

  const searchQuery = ServerAPI.querySearch({q: searchParam});

  return Promise.all([...geneQueries, searchQuery]).then(searchResults => {
    const uniqueResults = _.uniqBy(_.flatten(searchResults), result => result.uri);

    const pathwaysJSON = uniqueResults.map(result => ServerAPI.getGraphAndLayout(result.uri, 'latest'));

    return Promise.all(pathwaysJSON).then(pathways => {
      const processed = pathways.map(pathway => {
        const macromolecules = pathway.graph.nodes.filter(node => node.data.class === 'macromolecule');
        const genesInPathway = _.flattenDeep(_.uniq([...macromolecules.map(node => node.data.label), ...macromolecules.map(node => node.data.geneSynonyms)]));
        const genesInExpressionData = expressions.map(expression => expression.geneName);

        return {
          json: pathway,
          geneIntersection: _.intersection(genesInPathway, genesInExpressionData)
        };
      });

      return processed;
    });
  });
};

class Paint extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      cy: {},
      componentConfig: {},
      layoutConfig: {},
      networkJSON: {},
      networkMetadata: {
        name: '',
        datasource: '',
        comments: []
      },

      expressionsLoading: true,
      networkLoading: true,

      // enrichment data state
      expressionTable: {},
      selectedClass: '',
      selectedFunction: {
        name: 'mean',
        func: _.mean
      },

      // search results state
      searchParam: '',
      searchResults: [],

    };

    const query = queryString.parse(props.location.search);
    const searchParam = query.q;
    const enrichmentsURI = query.uri;

    if (enrichmentsURI == null || searchParam == null) {
      const network = demoPathways[0].json;
      const demoExpressionTable = new ExpressionTable(demoExpressions, network.graph);
      const layoutConfig = getLayoutConfig(network.layout);
      const componentConfig = PaintViewConfig;

      this.state = {
        cy: make_cytoscape({headless: true}),
        componentConfig: componentConfig,
        layoutConfig: layoutConfig,
        networkJSON: network.graph,
        networkMetadata: {
          uri: network.graph.pathwayMetadata.uri,
          name: _.get(network, 'graph.pathwayMetadata.title.0', 'Unknown Network'),
          datasource: _.get(network, 'graph.pathwayMetadata.dataSource.0', 'Unknown Data Source'),
          comments: network.graph.pathwayMetadata.comments,
          organism: network.graph.pathwayMetadata.organism
        },
        networkLoading: false,
        rawExpressions: demoExpressions,
        expressionTable: demoExpressionTable,
        selectedClass: _.get(demoExpressionTable.classes, '0', ''),
        expressionsLoading: false,
        searchParam: searchParam,
        searchResults: demoPathways,

        selectedSearchResult: network.graph.pathwayMetadata.uri,
        selectedFunction: {
          name: 'mean',
          func: _.mean
        }

      };

      const selectedFn = this.state.selectedFunction.func;
      const selectedClass = this.state.selectedClass;
      this.state.cy.on('network-loaded', () => applyExpressionData(this.state.cy, demoExpressionTable, selectedClass, selectedFn));
    } else {
      fetch(enrichmentsURI)
      .then(res => res.json())
      .then(json => {
        const expressions = _.get(json.dataSetExpressionList, '0.expressions', []);

        getAugmentedSearchResults(searchParam, expressions).then(pathwayResults => {

          // pathway results are sorted by gene expression intersection (largest to smallest)
          // take the largest gene intersection by default

          // see if there is a result that matches exactly
          let candidatePathway = _.find(pathwayResults, pathway => pathway.json.graph.pathwayMetadata.title[0] === searchParam);

          if (candidatePathway == null) {
            candidatePathway = pathwayResults.sort((p0, p1) => p1.geneIntersection.length - p0.geneIntersection.length)[0];
          }

          const network = candidatePathway.json;

          const expressionTable = new ExpressionTable(json, candidatePathway.json.graph);
          const layoutConfig = getLayoutConfig(network.layout);
          const componentConfig = PaintViewConfig;

          this.setState({
            cy: make_cytoscape({headless: true}),
            componentConfig: componentConfig,
            layoutConfig: layoutConfig,
            networkJSON: network.graph,
            networkMetadata: {
              uri: network.graph.pathwayMetadata.uri,
              name: _.get(network, 'graph.pathwayMetadata.title.0', 'Unknown Network'),
              datasource: _.get(network, 'graph.pathwayMetadata.dataSource.0', 'Unknown Data Source'),
              comments: network.graph.pathwayMetadata.comments,
              organism: network.graph.pathwayMetadata.organism
            },
            networkLoading: false,
            rawExpressions: json,
            expressionTable: expressionTable,
            selectedClass: _.get(expressionTable.classes, '0', ''),
            expressionsLoading: false,
            searchParam: searchParam,
            searchResults: pathwayResults,

            selectedSearchResult: candidatePathway.json.graph.pathwayMetadata.uri

            }, () => {
              const selectedFn = this.state.selectedFunction.func;
              const selectedClass = this.state.selectedClass;
              applyExpressionData(this.state.cy, expressionTable, selectedClass, selectedFn);
          });
        });
      });
    }
  }

  render() {
    const state = this.state;

    const baseView = h(BaseNetworkView.component, {
      // generic base view props
      layoutConfig: state.layoutConfig,
      componentConfig: state.componentConfig,
      cy: state.cy,
      networkJSON: state.networkJSON,
      networkMetadata: state.networkMetadata,

      // paint specific props needed by the paint menu
      rawExpressions: state.rawExpressions,
      expressionTable: state.expressionTable,
      selectedFunction: state.selectedFunction,
      selectedClass: state.selectedClass,
      activeMenu: paintMenuId,

      searchParam: state.searchParam,
      searchResults: state.searchResults,
      selectedSearchResult: state.selectedSearchResult

    });

    const loadingView = h(Loader, { loaded: !(state.expressionsLoading || state.networkLoading), options: { left: '50%', color: '#16A085' }});

    // create a view shell loading view e.g looks like the view but its not
    const content = state.expressionsLoading || state.networkLoading ? loadingView : baseView;

    return h('div', [content]);
  }
}

module.exports = Paint;<|endoftext|>


<commit_before>/**
 * Represents an error from the Discord API.
 */
class DiscordAPIError extends Error {
  constructor(error) {
    super();
    const flattened = error.errors ? `\n${this.constructor.flattenErrors(error.errors).join('\n')}` : '';
    this.name = 'DiscordAPIError';
    this.message = `${error.message}${flattened}`;

    /**
     * HTTP error code returned by Discord
     * @type {number}
     */
    this.code = error.code;
  }

  /**
   * Flattens an errors object returned from the API into an array.
   * @param {Object} obj Discord errors object
   * @param {string} [key] Used internally to determine key names of nested fields
   * @returns {string[]}
   */
  static flattenErrors(obj, key = '') {
    let messages = [];

    for (const k of Object.keys(obj)) {
      const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;

      if (obj[k]._errors) {
        messages.push(`${newKey}: ${obj[k]._errors.map(e => e.message).join(' ')}`);
      } else {
        messages = messages.concat(this.flattenErrors(obj[k], newKey));
      }
    }

    return messages;
  }
}

module.exports = DiscordAPIError;
<commit_msg>fix infinte loop issue (#1488)<commit_after>/**
 * Represents an error from the Discord API.
 */
class DiscordAPIError extends Error {
  constructor(error) {
    super();
    const flattened = error.errors ? `\n${this.constructor.flattenErrors(error.errors).join('\n')}` : '';
    this.name = 'DiscordAPIError';
    this.message = `${error.message}${flattened}`;

    /**
     * HTTP error code returned by Discord
     * @type {number}
     */
    this.code = error.code;
  }

  /**
   * Flattens an errors object returned from the API into an array.
   * @param {Object} obj Discord errors object
   * @param {string} [key] Used internally to determine key names of nested fields
   * @returns {string[]}
   */
  static flattenErrors(obj, key = '') {
    let messages = [];

    for (const k of Object.keys(obj)) {
      const newKey = key ? isNaN(k) ? `${key}.${k}` : `${key}[${k}]` : k;

      if (obj[k]._errors) {
        messages.push(`${newKey}: ${obj[k]._errors.map(e => e.message).join(' ')}`);
      } else if (obj[k].code && obj[k].message) {
        messages.push(`${obj[k].code}: ${obj[k].message}`);
      } else {
        messages = messages.concat(this.flattenErrors(obj[k], newKey));
      }
    }

    return messages;
  }
}

module.exports = DiscordAPIError;
<|endoftext|>


<commit_before>/*
# harness
This file can be used to very quickly set up a mocha-based browser testing
environment, and is compatible with `grunt-mocha`. You'll just need to set the path
for three files in `requirejs`:

```
paths: {
  'thehelp-test': '<your path>',
  'thehelp-test-coverage': '<required for code coverage>',
  'grunt-mocha-bridge': '<required if using with grunt-mocha'
}
```
*/

define(['thehelp-test'], function(test) {
  /*jshint maxcomplexity: 8 */

  'use strict';

  // `setupMocha` prepares `mocha` for tests to be loaded. It must be run before
  // we run test-defining code. If we don't, `describe` and `it` will not yet be defined.
  test.mocha.setup('bdd');

  // Prepare and verify settings, put defaults in place.
  var thehelp = window.thehelp || {};
  var settings = thehelp.test || {};
  if (typeof settings.coverage === 'undefined') {
    settings.coverage = true;
  }
  if (typeof settings.blanket === 'undefined') {
    settings.blanket = {
      filter: '/src/',
      antifilter: '["/bower_components/", "node_modules", "/test/","/lib/"]'
    };
  }

  if (typeof settings.files === 'undefined') {
    throw new Error('Specify test files with an array at window.thehelp.test.files');
  }
  if (typeof settings.mochaCss === 'undefined') {
    throw new Error(
      'Provide the path to the mocha.css file window.thehelp.test.mochaCss'
    );
  }

  // `loadTests` actually pulls in your test files, then installs `window.runTests()`,
  // which adds the mocha div to your page and runs the tests.
  var loadTests = function loadTests(settings, mocha) {
    require(settings.files, function() {
      window.runTests = function() {
        if (!document.getElementById('mocha')) {
          //Doing this without jquery to reduce dependencies :0)
          var body = document.getElementsByTagName('body')[0];
          var div = document.createElement('div');
          div.setAttribute('id', 'mocha');
          body.appendChild(div);

          var head = document.getElementsByTagName('head')[0];
          var link = document.createElement('link');
          link.setAttribute('rel', 'stylesheet');
          link.setAttribute('href', settings.mochaCss);
          head.appendChild(link);
        }

        mocha.run();
        return true;
      };

      if (window.PHANTOMJS) {
        require(['grunt-mocha-bridge'], function() {
          mocha.run();
        });
      }
      else if (!settings.waitToRun) {
        window.runTests();
      }
    });
  };

  // Sets up code coverage if configured. No code coverage under phantomjs since we're not
  // using a grunt plugin that understands code coverage.
  if (settings.coverage && !window.PHANTOMJS) {
    require(['thehelp-test-coverage'], function(blanket) {

      if (settings.blanket) {
        //Doing this without lodash to reduce dependencies :0)
        var keys = Object.keys(settings.blanket);

        for (var i = 0, max = keys.length; i < max; i += 1) {
          var key = keys[i];
          blanket.options(key, settings.blanket[key]);
        }
      }

      loadTests(settings, test.mocha);
    });
  }
  else {
    loadTests(settings, test.mocha);
  }

});
<commit_msg>Add Function.prototype.bind polyfill for phantom<commit_after>/*
# harness
This file can be used to very quickly set up a mocha-based browser testing
environment, and is compatible with `grunt-mocha`. You'll just need to set the path
for three files in `requirejs`:

```
paths: {
  'thehelp-test': '<your path>',
  'thehelp-test-coverage': '<required for code coverage>',
  'grunt-mocha-bridge': '<required if using with grunt-mocha'
}
```
*/

define(['thehelp-test'], function(test) {
  /*jshint maxcomplexity: 8 */

  'use strict';

  // Because PhantomJS doesn't support function.bind we polyfill it
  // https://groups.google.com/forum/#!msg/phantomjs/r0hPOmnCUpc/uxusqsl2LNoJ
  // https://github.com/ariya/phantomjs/issues/10522
  Function.prototype.bind = Function.prototype.bind || function(target) {
    var _this = this;
    return function() {
      return _this.apply(target, arguments);
    };
  };

  // `setupMocha` prepares `mocha` for tests to be loaded. It must be run before
  // we run test-defining code. If we don't, `describe` and `it` will not yet be defined.
  test.mocha.setup('bdd');

  // Prepare and verify settings, put defaults in place.
  var thehelp = window.thehelp || {};
  var settings = thehelp.test || {};
  if (typeof settings.coverage === 'undefined') {
    settings.coverage = true;
  }
  if (typeof settings.blanket === 'undefined') {
    settings.blanket = {
      filter: '/src/',
      antifilter: '["/bower_components/", "node_modules", "/test/","/lib/"]'
    };
  }

  if (typeof settings.files === 'undefined') {
    throw new Error('Specify test files with an array at window.thehelp.test.files');
  }
  if (typeof settings.mochaCss === 'undefined') {
    throw new Error(
      'Provide the path to the mocha.css file window.thehelp.test.mochaCss'
    );
  }

  // `loadTests` actually pulls in your test files, then installs `window.runTests()`,
  // which adds the mocha div to your page and runs the tests.
  var loadTests = function loadTests(settings, mocha) {
    require(settings.files, function() {
      window.runTests = function() {
        if (!document.getElementById('mocha')) {
          //Doing this without jquery to reduce dependencies :0)
          var body = document.getElementsByTagName('body')[0];
          var div = document.createElement('div');
          div.setAttribute('id', 'mocha');
          body.appendChild(div);

          var head = document.getElementsByTagName('head')[0];
          var link = document.createElement('link');
          link.setAttribute('rel', 'stylesheet');
          link.setAttribute('href', settings.mochaCss);
          head.appendChild(link);
        }

        mocha.run();
        return true;
      };

      if (window.PHANTOMJS) {
        require(['grunt-mocha-bridge'], function() {
          mocha.run();
        });
      }
      else if (!settings.waitToRun) {
        window.runTests();
      }
    });
  };

  // Sets up code coverage if configured. No code coverage under phantomjs since we're not
  // using a grunt plugin that understands code coverage.
  if (settings.coverage && !window.PHANTOMJS) {
    require(['thehelp-test-coverage'], function(blanket) {

      if (settings.blanket) {
        //Doing this without lodash to reduce dependencies :0)
        var keys = Object.keys(settings.blanket);

        for (var i = 0, max = keys.length; i < max; i += 1) {
          var key = keys[i];
          blanket.options(key, settings.blanket[key]);
        }
      }

      loadTests(settings, test.mocha);
    });
  }
  else {
    loadTests(settings, test.mocha);
  }

});
<|endoftext|>


<commit_before>'use strict';

module.exports = {
  id: 'unnecessaryHeaders',
  title: 'Avoid Unnecessary Headers',
  description:
    "Do not send headers that you don't need. We look for p3p, cache-control and max-age, pragma and x-frame-options headers. Have a look at Andrew Betts - Headers for Hackers talk as a guide https://www.youtube.com/watch?v=k92ZbrY815c",
  weight: 1,
  tags: ['bestpractice', 'header'],
  processPage: function(page) {
    const offending = [];
    let score = 100;
    let advice = '';
    page.assets.forEach(function(asset) {
      if (asset.headers.response.p3p) {
        offending.push(asset.url);
        score -= 1;
        advice +=
          asset.url +
          ' has a obsolete p3p header:' +
          asset.headers.response.p3p +
          '. https://en.wikipedia.org/wiki/P3P. ';
      }

      if (
        asset.headers.response.expires &&
        asset.headers.response['cache-control'] &&
        asset.headers.response['cache-control'].indexOf('max-age' > -1)
      ) {
        // You don't need to set both expires and cache-control: max-age. Use just one!
        offending.push(asset.url);
        score -= 1;
        advice +=
          asset.url +
          ' has both an expires and a cache-control:max-age header. ';
      }

      if (
        asset.headers.response.pragma &&
        asset.headers.response.pragma.indexOf('no-cache') > -1
      ) {
        offending.push(asset.url);
        score -= 1;
        advice += asset.url + ' has pragma: no-cache response header. ';
      }

      if (
        asset.headers.response['x-frame-options'] &&
        asset.headers.response['x-frame-options'] === 'sameorigin'
      ) {
        offending.push(asset.url);
        score -= 1;
        advice +=
          asset.url +
          ' use a x-frame-options: sameorigin header, you should instead use a Content-Security-Policy header. ';
      }
    });

    return {
      score: Math.max(0, score),
      offending: offending,
      advice: advice
    };
  }
};
<commit_msg>cleaner output<commit_after>'use strict';

module.exports = {
  id: 'unnecessaryHeaders',
  title: 'Avoid Unnecessary Headers',
  description:
    "Do not send headers that you don't need. We look for p3p, cache-control and max-age, pragma and x-frame-options headers. Have a look at Andrew Betts - Headers for Hackers talk as a guide https://www.youtube.com/watch?v=k92ZbrY815c",
  weight: 1,
  tags: ['bestpractice', 'header'],
  processPage: function(page) {
    const offending = [];
    const p3pHeaders = [];
    const cacheHeaders = [];
    const pragmaHeaders = [];
    const xframeHeaders = [];
    let score = 100;
    let advice = '';
    page.assets.forEach(function(asset) {
      if (asset.headers.response.p3p) {
        offending.push(asset.url);
        p3pHeaders.push(asset.url);
        score -= 1;
      }

      if (
        asset.headers.response.expires &&
        asset.headers.response['cache-control'] &&
        asset.headers.response['cache-control'].indexOf('max-age' > -1)
      ) {
        // You don't need to set both expires and cache-control: max-age. Use just one!
        offending.push(asset.url);
        cacheHeaders.push(asset.url);
        score -= 1;
      }

      if (
        asset.headers.response.pragma &&
        asset.headers.response.pragma.indexOf('no-cache') > -1
      ) {
        offending.push(asset.url);
        pragmaHeaders.push(asset.url);
        score -= 1;
      }

      if (
        asset.headers.response['x-frame-options'] &&
        asset.headers.response['x-frame-options'] === 'sameorigin'
      ) {
        offending.push(asset.url);
        xframeHeaders.push(asset.url);
        score -= 1;
      }
    });

    if (p3pHeaders.length > 0) {
      advice +=
        'There are ' +
        p3pHeaders.length +
        ' response(s) that sets a p3p header. ';
    }
    if (cacheHeaders.length > 0) {
      advice +=
        'There are ' +
        cacheHeaders.length +
        ' response(s) that sets both a max-age and expires header. ';
    }
    if (pragmaHeaders.length > 0) {
      advice +=
        'There are ' +
        pragmaHeaders.length +
        ' response(s) that sets a pragma no-cache header (that is a request header). ';
    }
    if (xframeHeaders.length > 0) {
      advice +=
        'There are ' +
        xframeHeaders.length +
        ' response(s) that sets a x-frame-options:sameorigin header. ';
    }

    return {
      score: Math.max(0, score),
      offending: offending,
      advice: advice
    };
  }
};
<|endoftext|>


<commit_before>describe('collapse directive', function() {
  var scope, $compile, $animate;
  var element;

  beforeEach(module('ui.bootstrap.collapse'));
  beforeEach(module('ngAnimateMock'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$animate_) {
    scope = _$rootScope_;
    $compile = _$compile_;
    $animate = _$animate_;
  }));

  beforeEach(function() {
    element = $compile('
Some Content
')(scope);
    angular.element(document.body).append(element);
  });

  afterEach(function() {
    element.remove();
  });

  it('should be hidden on initialization if isCollapsed = true without transition', function() {
    scope.isCollapsed = true;
    scope.$digest();
    expect(element.height()).toBe(0);
  });

  it('should collapse if isCollapsed = true with animation on subsequent use', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.height()).toBe(0);
  });

  it('should be shown on initialization if isCollapsed = false without transition', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.height()).not.toBe(0);
  });

  it('should expand if isCollapsed = false with animation on subsequent use', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.height()).not.toBe(0);
  });

  it('should expand if isCollapsed = true with animation on subsequent uses', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.height()).toBe(0);
  });

  it('should change aria-expanded attribute', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-expanded')).toBe('true');

    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-expanded')).toBe('false');
  });

  it('should change aria-hidden attribute', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-hidden')).toBe('false');

    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-hidden')).toBe('true');
  });

  describe('dynamic content', function() {
    var element;

    beforeEach(function() {
      element = angular.element('

Initial content

Additional content

');
      $compile(element)(scope);
      angular.element(document.body).append(element);
    });

    afterEach(function() {
      element.remove();
    });

    it('should grow accordingly when content size inside collapse increases', function() {
      scope.exp = false;
      scope.isCollapsed = false;
      scope.$digest();
      var collapseHeight = element.height();
      scope.exp = true;
      $animate.flush();
      scope.$digest();
      expect(element.height()).toBeGreaterThan(collapseHeight);
    });

    it('should shrink accordingly when content size inside collapse decreases', function() {
      scope.exp = true;
      scope.isCollapsed = false;
      scope.$digest();
      var collapseHeight = element.height();
      scope.exp = false;
      scope.$digest();
      expect(element.height()).toBeLessThan(collapseHeight);
    });
  });
});
<commit_msg>chore(collapse): clean some stale test headers<commit_after>describe('collapse directive', function() {
  var element, scope, $compile, $animate;

  beforeEach(module('ui.bootstrap.collapse'));
  beforeEach(module('ngAnimateMock'));
  beforeEach(inject(function(_$rootScope_, _$compile_, _$animate_) {
    scope = _$rootScope_;
    $compile = _$compile_;
    $animate = _$animate_;
  }));

  beforeEach(function() {
    element = $compile('
Some Content
')(scope);
    angular.element(document.body).append(element);
  });

  afterEach(function() {
    element.remove();
  });

  it('should be hidden on initialization if isCollapsed = true', function() {
    scope.isCollapsed = true;
    scope.$digest();
    expect(element.height()).toBe(0);
  });

  it('should collapse if isCollapsed = true on subsequent use', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.height()).toBe(0);
  });

  it('should be shown on initialization if isCollapsed = false', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.height()).not.toBe(0);
  });

  it('should expand if isCollapsed = false on subsequent use', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.height()).not.toBe(0);
  });

  it('should expand if isCollapsed = true on subsequent uses', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.height()).toBe(0);
  });

  it('should change aria-expanded attribute', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-expanded')).toBe('true');

    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-expanded')).toBe('false');
  });

  it('should change aria-hidden attribute', function() {
    scope.isCollapsed = false;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-hidden')).toBe('false');

    scope.isCollapsed = true;
    scope.$digest();
    $animate.flush();
    expect(element.attr('aria-hidden')).toBe('true');
  });

  describe('dynamic content', function() {
    var element;

    beforeEach(function() {
      element = angular.element('

Initial content

Additional content

');
      $compile(element)(scope);
      angular.element(document.body).append(element);
    });

    afterEach(function() {
      element.remove();
    });

    it('should grow accordingly when content size inside collapse increases', function() {
      scope.exp = false;
      scope.isCollapsed = false;
      scope.$digest();
      var collapseHeight = element.height();
      scope.exp = true;
      $animate.flush();
      scope.$digest();
      expect(element.height()).toBeGreaterThan(collapseHeight);
    });

    it('should shrink accordingly when content size inside collapse decreases', function() {
      scope.exp = true;
      scope.isCollapsed = false;
      scope.$digest();
      var collapseHeight = element.height();
      scope.exp = false;
      scope.$digest();
      expect(element.height()).toBeLessThan(collapseHeight);
    });
  });
});
<|endoftext|>


<commit_before>/* eslint-env browser,jquery */
import $ from 'jquery';
import PhotoSwipe from 'photoswipe';
import PhotoSwipeUIDefault from 'photoswipe/dist/photoswipe-ui-default';

/**
 * 画像のポップアップ
 *
 * imgにdata-popup-urlが指定されていたが有効となる
 */
export default class ImagePopup {
  constructor(el) {
    this.el = el;
  }

  initialize() {
    this.attachPopupEvent();
    this.attachImageClickRequiredValidation();
  }

  deInitialize() {
  }

  initForDetail() {
    this.showImageDetail();
  }

  attachPopupEvent() {
    $(this.el).on('click', 'img[data-popup-url]', (e) => {
      const $targets = $('#content').find('img[data-popup-url]');
      const urls = $targets.map((i, imgEl) => ({ src: $(imgEl).data('popup-url'), w: 0, h: 0 }));
      const items = urls.toArray();
      const options = {
        history: false,
        focus: false,
        showAnimationDuration: 0,
        hideAnimationDuration: 0,
      };

      const container = document.getElementsByClassName('pswp')[0];
      const gallery = new PhotoSwipe(container, PhotoSwipeUIDefault, items, options);
      gallery.listen('imageLoadComplete', (index, item) => {
        const img = new Image();
        img.onload = () => {
          // itemのwidth, heightが必須なのでここで設定する
          item.w = img.width;
          item.h = img.height;
          gallery.updateSize(true);
        };
        img.src = item.src;
      });

      gallery.init();
      gallery.listen('afterChange', () => {
        const target = $(this.el).find('img[data-popup-url]').filter((i, imgEl) => {
          const src = $(imgEl).data('popup-url');
          return src === gallery.currItem.src;
        })[0];
        this.removeRelatedInputForPopupImage(target);
      });

      const clickUrl = $(e.target).data('popup-url');
      const clickedImageIndex = items.findIndex(item => item.src === clickUrl);
      gallery.goTo(clickedImageIndex);
      this.removeRelatedInputForPopupImage($targets[clickedImageIndex]);
    });
  }

  /** imgに対応するvalidation用のinputのIDを返す */
  getInputIdForPopupImage(imgEl) {
    return `${imgEl.id}-input`;
  }

  /** imgElに対応するvalidation用のinputを削除する。また、imgElにerrクラスが指定されていたら削除する */
  removeRelatedInputForPopupImage(imgEl) {
    $(imgEl).removeClass('err');
    const id = this.getInputIdForPopupImage(imgEl);
    $(`#${id}`).remove();

    // SPのエラー表示があれば消す
    if ($(imgEl).next().hasClass('formError')) {
      $(imgEl).next().remove();
    }
  }

  /**
   * 画像のエレメントに対応するinputを生成し、parsleyのrequired属性をつける
   * 画像がクリックされたら対応するinputを削除する
   */
  attachImageClickRequiredValidation() {
    $(this.el).find('img[data-popup-url]').each((i, el) => {
      const id = this.getInputIdForPopupImage(el);
      $(`<input type="text"
class="image-required-checkbox"
data-parsley-required="true"
id="${id}"
name="${id}"
readonly="readonly"
data-parsley-class-handler="#${el.id}"
data-parsley-required-message="クリックして詳細を確認してください"
/>`)
        .css('position', 'absolute')
        .css('left', '-1000px')
        .insertAfter(el);
    });
  }

  /**
   * 画像の詳細を表示する
   */
  showImageDetail() {
    $(this.el).find('img[data-popup-url]').each((i, imgEl) => {
      const $imgEl = $(imgEl);
      const url = $imgEl.data('popup-url');
      const clickRequired = $imgEl.data('image-click-required') === true;

      if (url) {
        const image = new Image();
        image.onload = () => {
          $('<span/>').addClass('detail-function').text(`ポップアップ ${image.width}x${image.height}`).insertAfter($imgEl);
        };
        image.src = url;
      }
      if (clickRequired) $('<span/>').addClass('detail-function').text('クリック必須').insertAfter($imgEl);
    });
  }
}

<commit_msg>画像がポップアップすると全てクリック必須になる不具合に対応<commit_after>/* eslint-env browser,jquery */
import $ from 'jquery';
import PhotoSwipe from 'photoswipe';
import PhotoSwipeUIDefault from 'photoswipe/dist/photoswipe-ui-default';

/**
 * 画像のポップアップ
 *
 * imgにdata-popup-urlが指定されていたが有効となる
 */
export default class ImagePopup {
  constructor(el) {
    this.el = el;
  }

  initialize() {
    this.attachPopupEvent();
    this.attachImageClickRequiredValidation();
  }

  deInitialize() {
  }

  initForDetail() {
    this.showImageDetail();
  }

  attachPopupEvent() {
    $(this.el).on('click', 'img[data-popup-url]', (e) => {
      const $targets = $('#content').find('img[data-popup-url]');
      const urls = $targets.map((i, imgEl) => ({ src: $(imgEl).data('popup-url'), w: 0, h: 0 }));
      const items = urls.toArray();
      const options = {
        history: false,
        focus: false,
        showAnimationDuration: 0,
        hideAnimationDuration: 0,
      };

      const container = document.getElementsByClassName('pswp')[0];
      const gallery = new PhotoSwipe(container, PhotoSwipeUIDefault, items, options);
      gallery.listen('imageLoadComplete', (index, item) => {
        const img = new Image();
        img.onload = () => {
          // itemのwidth, heightが必須なのでここで設定する
          item.w = img.width;
          item.h = img.height;
          gallery.updateSize(true);
        };
        img.src = item.src;
      });

      gallery.init();
      gallery.listen('afterChange', () => {
        const target = $(this.el).find('img[data-popup-url]').filter((i, imgEl) => {
          const src = $(imgEl).data('popup-url');
          return src === gallery.currItem.src;
        })[0];
        this.removeRelatedInputForPopupImage(target);
      });

      const clickUrl = $(e.target).data('popup-url');
      const clickedImageIndex = items.findIndex(item => item.src === clickUrl);
      gallery.goTo(clickedImageIndex);
      this.removeRelatedInputForPopupImage($targets[clickedImageIndex]);
    });
  }

  /** imgに対応するvalidation用のinputのIDを返す */
  getInputIdForPopupImage(imgEl) {
    return `${imgEl.id}-input`;
  }

  /** imgElに対応するvalidation用のinputを削除する。また、imgElにerrクラスが指定されていたら削除する */
  removeRelatedInputForPopupImage(imgEl) {
    $(imgEl).removeClass('err');
    const id = this.getInputIdForPopupImage(imgEl);
    $(`#${id}`).remove();

    // SPのエラー表示があれば消す
    if ($(imgEl).next().hasClass('formError')) {
      $(imgEl).next().remove();
    }
  }

  /** クリック必須かどうかを返す */
  isImageClickRequired($imgEl) {
    return $imgEl.data('image-click-required') === true;
  }

  /**
   * 画像のエレメントに対応するinputを生成し、parsleyのrequired属性をつける
   * 画像がクリックされたら対応するinputを削除する
   */
  attachImageClickRequiredValidation() {
    $(this.el).find('img[data-popup-url]').each((i, el) => {
      const $imgEl = $(el);
      if (!this.isImageClickRequired($imgEl)) return;

      const id = this.getInputIdForPopupImage(el);
      $(`<input type="text"
class="image-required-checkbox"
data-parsley-required="true"
id="${id}"
name="${id}"
readonly="readonly"
data-parsley-class-handler="#${el.id}"
data-parsley-required-message="クリックして詳細を確認してください"
/>`)
        .css('position', 'absolute')
        .css('left', '-1000px')
        .insertAfter(el);
    });
  }

  /**
   * 画像の詳細を表示する
   */
  showImageDetail() {
    $(this.el).find('img[data-popup-url]').each((i, imgEl) => {
      const $imgEl = $(imgEl);
      const url = $imgEl.data('popup-url');
      const clickRequired = this.isImageClickRequired($imgEl);

      if (url) {
        const image = new Image();
        image.onload = () => {
          $('<span/>').addClass('detail-function').text(`ポップアップ ${image.width}x${image.height}`).insertAfter($imgEl);
        };
        image.src = url;
      }
      if (clickRequired) $('<span/>').addClass('detail-function').text('クリック必須').insertAfter($imgEl);
    });
  }
}

<|endoftext|>