<commit_before>const fs = require('fs');

const filter = module.exports = {};

/**
 * Gets stats on a file, and filters it based on `submitTimeStart`
 * and `submitTimeEnd` filter parameters.
 *
 * @param filePath - Path to OSM file on the file system
 * @param filterObj - Parameters to filter by
 * @param cb - Callback object with `filePath` and `bool` value
 */
filter.file = function (filePath, filterObj, cb) {
    // if there is no filter
    if (typeof filterObj !== 'object' || filterObj === null) {
        cb(filePath, true);
        return;
    }

    fs.stat(filePath, function (err, stats) {
        // lets just let it pass through in err state
        if (err) {
            cb(filePath, true);
            return;
        }

        var submitTime = stats.birthtime.getTime();

        if (typeof filterObj.submitTimeStart === 'string' && typeof filterObj.submitTimeEnd === 'string') {
            var submitTimeStart = new Date(filterObj.submitTimeStart).getTime();
            var submitTimeEnd = new Date(filterObj.submitTimeEnd).getTime();
            // if invalid start time
            if (isNaN(submitTimeStart)) {
                cb(filePath, false);
                return;
            }
            // if invalid end time
            if (isNaN(submitTimeEnd)) {
                cb(filePath, false);
                return;
            }
            if (submitTime >= submitTimeStart && submitTime <= submitTimeEnd) {
                cb(filePath, true);
                return;
            }
            cb(filePath, false);
            return;
        }

        if (typeof filterObj.submitTimeStart === 'string') {
            var submitTimeStart = new Date(filterObj.submitTimeStart).getTime();
            // if invalid start time
            if (isNaN(submitTimeStart)) {
                cb(filePath, false);
                return;
            }
            if (submitTime >= submitTimeStart) {
                cb(filePath, true);
                return;
            }
            cb(filePath, false);
            return;
        }

        if (typeof filterObj.submitTimeEnd === 'string') {
            var submitTimeEnd = new Date(filterObj.submitTimeEnd).getTime();
            // if invalid end time
            if (isNaN(submitTimeEnd)) {
                cb(filePath, false);
                return;
            }
            if (submitTime <= submitTimeEnd) {
                cb(filePath, true);
                return;
            }
            cb(filePath, false);
            return;
        }

        // No submission time filter, pass through
        cb(filePath, true);

    });
};

filter.user = function (rootOsmElement, filterObj, cb) {
    cb(rootOsmElement, true);
};
<commit_msg>filters user #9<commit_after>const fs = require('fs');

const filter = module.exports = {};

/**
 * Gets stats on a file, and filters it based on `submitTimeStart`
 * and `submitTimeEnd` filter parameters.
 *
 * @param filePath - Path to OSM file on the file system
 * @param filterObj - Parameters to filter by
 * @param cb - Callback object with `filePath` and `bool` value
 */
filter.file = function (filePath, filterObj, cb) {
    // if there is no filter
    if (typeof filterObj !== 'object' || filterObj === null) {
        cb(filePath, true);
        return;
    }

    fs.stat(filePath, function (err, stats) {
        // lets just let it pass through in err state
        if (err) {
            cb(filePath, true);
            return;
        }

        var submitTime = stats.birthtime.getTime();

        if (typeof filterObj.submitTimeStart === 'string' && typeof filterObj.submitTimeEnd === 'string') {
            var submitTimeStart = new Date(filterObj.submitTimeStart).getTime();
            var submitTimeEnd = new Date(filterObj.submitTimeEnd).getTime();
            // if invalid start time
            if (isNaN(submitTimeStart)) {
                cb(filePath, false);
                return;
            }
            // if invalid end time
            if (isNaN(submitTimeEnd)) {
                cb(filePath, false);
                return;
            }
            if (submitTime >= submitTimeStart && submitTime <= submitTimeEnd) {
                cb(filePath, true);
                return;
            }
            cb(filePath, false);
            return;
        }

        if (typeof filterObj.submitTimeStart === 'string') {
            var submitTimeStart = new Date(filterObj.submitTimeStart).getTime();
            // if invalid start time
            if (isNaN(submitTimeStart)) {
                cb(filePath, false);
                return;
            }
            if (submitTime >= submitTimeStart) {
                cb(filePath, true);
                return;
            }
            cb(filePath, false);
            return;
        }

        if (typeof filterObj.submitTimeEnd === 'string') {
            var submitTimeEnd = new Date(filterObj.submitTimeEnd).getTime();
            // if invalid end time
            if (isNaN(submitTimeEnd)) {
                cb(filePath, false);
                return;
            }
            if (submitTime <= submitTimeEnd) {
                cb(filePath, true);
                return;
            }
            cb(filePath, false);
            return;
        }

        // No submission time filter, pass through
        cb(filePath, true);

    });
};

filter.user = function (rootOsmElement, filterObj, cb) {
    // if there is no filter
    if (typeof filterObj !== 'object' || filterObj === null) {
        cb(rootOsmElement, true);
        return;
    }
    if (typeof filterObj.user === 'string') {
        var userAttr = rootOsmElement.attr('user');
        // userAttr is not an attribute for the OSM edit, we dont want it
        if (userAttr === null) {
            cb(rootOsmElement, false);
            return;
        }
        var user = userAttr.value();
        if (user.toLowerCase() != filterObj.user.toLowerCase()) {
            cb(rootOsmElement, false);
            return;
        }
    }

    // no user filter, pass through
    cb(rootOsmElement, true);
};
<|endoftext|>


<commit_before>var orgName = "JavaPosseRoundup";
var roundupStart = new Date(2012, 2, 25);

var margin = {top: 20, right: 0, bottom: 50, left: 180},
    w = 1200 - margin.left - margin.right,
    h = 600 - margin.top - margin.bottom,
    tickHeight = 10;

// Scales. Note the inverted domain for the y-scale: bigger is up!
var x = d3.time.scale().rangeRound([0, w]),
    y = d3.scale.ordinal().rangePoints([0, h], .5),
    colors = d3.scale.category20();

// Axes.
var xAxis = d3.svg.axis().scale(x).tickSubdivide(true);
var yAxis = d3.svg.axis().scale(y).tickSize(0).tickPadding(5).orient("left");

// Add an SVG element with the desired dimensions and margin.
var svg = d3.select("#chart").append("svg")
    .attr("width", w + margin.left + margin.right)
    .attr("height", h + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Add the clip path.
svg.append("clipPath")
    .attr("id", "clip")
  .append("rect")
    .attr("width", w)
    .attr("height", h + 20);

// Need something in the background absorb the mouse events!
svg.append("rect")
    .attr("width", w)
    .attr("height", h)
    .style("fill", "#fff");

$.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", function(response) {
  var allCommits = [];
  var timelines = [];
  response.data.forEach(function(repo, i, array) {
    function gitSource(repo) {
      return repo.url + "/commits?callback=?";
    }
    $.getJSON(gitSource(repo), function(response) {
      var commits = response.data.map(function(r) {
        return {
          repo: repo.name,
          message: r.commit.message,
          authorDate: d3.time.format.iso.parse(r.commit.author.date),
          committerEmail: r.commit.committer.email,
          committerName: r.commit.committer.name,
          commitDate: d3.time.format.iso.parse(r.commit.committer.date),
          sha: r.sha
        }
      });
      var timeline = {
        repo: repo.name,
        earliest: commits[commits.length - 1].commitDate,
        latest: commits[0].commitDate
      };
      allCommits.push.apply(allCommits, commits);
      timelines.push(timeline);
      if (timelines.length == array.length) drawChart(allCommits, timelines);
    });
  });
});

function drawChart(allCommits, timelines) {

  x.domain([roundupStart, d3.time.day.ceil(new Date())]);

  y.domain(timelines.map(function(t) { return t.repo; }));

  // Add the x-axis.
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + (h + 20) + ")")
    .call(xAxis);

  // Add the y-axis.
  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);

  svg.selectAll(".commit")
      .data(allCommits)
    .enter().append("line")
      .attr("class", "commit")
      .attr("clip-path", "url(#clip)")
      .attr("x1", function(d) { return x(d.commitDate); })
      .attr("y1", function(d) { return Math.floor(y(d.repo)) - tickHeight; })
      .attr("x2", function(d) { return x(d.commitDate); })
      .attr("y2", function(d) { return Math.floor(y(d.repo)) + tickHeight; })
      .style("stroke", function(d) { return colors(d.committerEmail); })
      .on("mouseover", showCommitInfo);

  svg.selectAll(".timeline")
      .data(timelines)
    .enter().append("line")
      .attr("class", "timeline")
      .attr("clip-path", "url(#clip)")
      .attr("x1", function(d) { return x(d.earliest); })
      .attr("y1", function(d) { return Math.floor(y(d.repo)); })
      .attr("x2", function(d) { return x(d.latest); })
      .attr("y2", function(d) { return Math.floor(y(d.repo)); });

  svg.call(d3.behavior.zoom().x(x).on("zoom", zoom));

  function zoom() {
    svg.select(".x.axis").call(xAxis);
    svg.selectAll(".commit")
      .data(allCommits)
      .attr("x1", function(d) { return x(d.commitDate); })
      .attr("x2", function(d) { return x(d.commitDate); });
    svg.selectAll(".timeline")
      .data(timelines)
      .attr("x1", function(d) { return x(d.earliest); })
      .attr("x2", function(d) { return x(d.latest); });
    svg.selectAll(".committer, .message")
      .attr("x", function(d) { return x(d.commitDate); });
  }

  function showCommitInfo(d, i) {

    var committer = svg.selectAll(".committer")
      .data([d], function(d) { return d.sha; });

    committer.enter().append("text")
      .attr("class", "committer")
      .attr("clip-path", "url(#clip)")
      .attr("x", x(d.commitDate))
      .attr("y", y(d.repo))
      .attr("dx", -2)
      .attr("dy", 2 * tickHeight + 5)
      .attr("text-anchor", "end")
      .style("fill", colors(d.committerEmail))
      .text(d.committerName);

    committer.exit().remove();

    var message = svg.selectAll(".message")
      .data([d], function(d) { return d.sha; });

    message.enter().append("text")
      .attr("class", "message")
      .attr("clip-path", "url(#clip)")
      .attr("x", x(d.commitDate))
      .attr("y", y(d.repo))
      .attr("dx", 2)
      .attr("dy", 2 * tickHeight + 5)
      .text(d.message);

    message.exit().remove();
  }

}
<commit_msg>Replace hard-coded values with attributes on the div that contains the SVG element<commit_after>// Find the element where the commit timeline should be drawn
var commits = d3.select("#commits");

var orgName = commits.attr("orgName") || "JavaPosseRoundup";
var startDate = commits.attr("startDate") || "";
var width = commits.attr("width") || 1200;
var height = commits.attr("height") || 600;

var margin = {top: 20, right: 0, bottom: 50, left: 180},
    w = width - margin.left - margin.right,
    h = height - margin.top - margin.bottom;

// Scales. Note the inverted domain for the y-scale: bigger is up!
var x = d3.time.scale().rangeRound([0, w]),
    y = d3.scale.ordinal().rangePoints([0, h], .5),
    colors = d3.scale.category20();

// Axes.
var xAxis = d3.svg.axis().scale(x).tickSubdivide(true);
var yAxis = d3.svg.axis().scale(y).tickSize(0).tickPadding(5).orient("left");

// Add an SVG element with the desired dimensions and margin.
var svg = commits.append("svg")
    .attr("width", w + margin.left + margin.right)
    .attr("height", h + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// Add the clip path.
svg.append("clipPath")
    .attr("id", "clip")
  .append("rect")
    .attr("width", w)
    .attr("height", h + 20);

// Need something in the background absorb the mouse events!
svg.append("rect")
    .attr("width", w)
    .attr("height", h)
    .style("fill", "#fff");

$.getJSON("https://api.github.com/orgs/" + orgName + "/repos?callback=?", function(response) {
  var allCommits = [];
  var timelines = [];
  response.data.forEach(function(repo, i, array) {
    function gitSource(repo) {
      return repo.url + "/commits?callback=?";
    }
    $.getJSON(gitSource(repo), function(response) {
      var commits = response.data.map(function(r) {
        return {
          repo: repo.name,
          message: r.commit.message,
          authorDate: d3.time.format.iso.parse(r.commit.author.date),
          committerEmail: r.commit.committer.email,
          committerName: r.commit.committer.name,
          commitDate: d3.time.format.iso.parse(r.commit.committer.date),
          sha: r.sha
        }
      });
      var timeline = {
        repo: repo.name,
        earliest: commits[commits.length - 1].commitDate,
        latest: commits[0].commitDate
      };
      allCommits.push.apply(allCommits, commits);
      timelines.push(timeline);
      if (timelines.length == array.length) drawChart(allCommits, timelines);
    });
  });
});

function drawChart(allCommits, timelines) {

  var tickHeight = height / (timelines.length * 4 + 1);

  var earliestCommitDate = d3.min(timelines, function(d) { return d.earliest; });

  var start = d3.time.format("%Y-%m-%d").parse(startDate) || earliestCommitDate;

  x.domain([d3.time.day.floor(start), d3.time.day.ceil(new Date())]);

  y.domain(timelines.map(function(t) { return t.repo; }));

  // Add the x-axis.
  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + (h + 20) + ")")
    .call(xAxis);

  // Add the y-axis.
  svg.append("g")
    .attr("class", "y axis")
    .call(yAxis);

  svg.selectAll(".commit")
      .data(allCommits)
    .enter().append("line")
      .attr("class", "commit")
      .attr("clip-path", "url(#clip)")
      .attr("x1", function(d) { return x(d.commitDate); })
      .attr("y1", function(d) { return Math.floor(y(d.repo)) - tickHeight; })
      .attr("x2", function(d) { return x(d.commitDate); })
      .attr("y2", function(d) { return Math.floor(y(d.repo)) + tickHeight; })
      .style("stroke", function(d) { return colors(d.committerEmail); })
      .on("mouseover", showCommitInfo);

  svg.selectAll(".timeline")
      .data(timelines)
    .enter().append("line")
      .attr("class", "timeline")
      .attr("clip-path", "url(#clip)")
      .attr("x1", function(d) { return x(d.earliest); })
      .attr("y1", function(d) { return Math.floor(y(d.repo)); })
      .attr("x2", function(d) { return x(d.latest); })
      .attr("y2", function(d) { return Math.floor(y(d.repo)); });

  svg.call(d3.behavior.zoom().x(x).on("zoom", zoom));

  function zoom() {
    svg.select(".x.axis").call(xAxis);
    svg.selectAll(".commit")
      .data(allCommits)
      .attr("x1", function(d) { return x(d.commitDate); })
      .attr("x2", function(d) { return x(d.commitDate); });
    svg.selectAll(".timeline")
      .data(timelines)
      .attr("x1", function(d) { return x(d.earliest); })
      .attr("x2", function(d) { return x(d.latest); });
    svg.selectAll(".committer, .message")
      .attr("x", function(d) { return x(d.commitDate); });
  }

  function showCommitInfo(d, i) {

    var committer = svg.selectAll(".committer")
      .data([d], function(d) { return d.sha; });

    committer.enter().append("text")
      .attr("class", "committer")
      .attr("clip-path", "url(#clip)")
      .attr("x", x(d.commitDate))
      .attr("y", y(d.repo))
      .attr("dx", -2)
      .attr("dy", 2 * tickHeight + 5)
      .attr("text-anchor", "end")
      .style("fill", colors(d.committerEmail))
      .text(d.committerName);

    committer.exit().remove();

    var message = svg.selectAll(".message")
      .data([d], function(d) { return d.sha; });

    message.enter().append("text")
      .attr("class", "message")
      .attr("clip-path", "url(#clip)")
      .attr("x", x(d.commitDate))
      .attr("y", y(d.repo))
      .attr("dx", 2)
      .attr("dy", 2 * tickHeight + 5)
      .text(d.message);

    message.exit().remove();
  }

}
<|endoftext|>


<commit_before>import Animation from 'kittik-animation-basic';

const AVAILABLE_DIRECTIONS = ['bounceUp', 'bounceRight', 'bounceDown', 'bounceLeft', 'shakeX', 'shakeY'];

/**
 * Focus animation is responsible for attention seekers for your shape.
 *
 * @since 1.0.0
 */
export default class Focus extends Animation {
  constructor(options = {}) {
    super(options);

    this.setDirection(options.direction);
    this.setOffset(options.offset);
    this.setRepeat(options.repeat);
  }

  /**
   * Get total duration of the animation taking to attention repeat count.
   *
   * @returns {Number}
   */
  getDuration() {
    return this.get('duration') / this.getRepeat();
  }

  /**
   * Get direction of the animation.
   *
   * @returns {String}
   */
  getDirection() {
    return this.get('direction');
  }

  /**
   * Set new direction of the animation.
   *
   * @param {String} direction
   * @returns {Animation}
   */
  setDirection(direction = 'shakeX') {
    if (AVAILABLE_DIRECTIONS.indexOf(direction) === -1) throw new Error(`Unknown direction: ${direction}`);
    return this.set('direction', direction);
  }

  /**
   * Get interval value used for moving shape.
   *
   * @returns {Number}
   */
  getOffset() {
    return this.get('offset');
  }

  /**
   * Set interval value used for moving shape.
   *
   * @param {Number} [offset=5]
   * @returns {Animation}
   */
  setOffset(offset = 5) {
    return this.set('offset', offset);
  }

  /**
   * Get repeat count of the attractor.
   *
   * @returns {Number}
   */
  getRepeat() {
    return this.get('repeat');
  }

  /**
   * Set repeat count of the attractor.
   *
   * @param {Number} [repeat=1]
   * @returns {Animation}
   */
  setRepeat(repeat = 1) {
    return this.set('repeat', repeat);
  }

  /**
   * Animates shape with bounce effect based on direction.
   *
   * @param {Shape} shape
   * @param {String} direction
   * @returns {Promise}
   * @private
   */
  _animateBounce(shape, direction = 'bounceUp') {
    let startValue = shape.getY();
    let endValue = shape.getY();
    let property = 'y';

    switch (direction) {
      case 'bounceUp':
        startValue = shape.getY();
        endValue = shape.getY() - this.getOffset();
        property = 'y';
        break;
      case 'bounceRight':
        startValue = shape.getX();
        endValue = shape.getX() + this.getOffset();
        property = 'x';
        break;
      case 'bounceDown':
        startValue = shape.getY();
        endValue = shape.getY() + this.getOffset();
        property = 'y';
        break;
      case 'bounceLeft':
        startValue = shape.getX();
        endValue = shape.getX() - this.getOffset();
        property = 'x';
        break;
    }

    const length = this.getRepeat();
    const firstStep = () => this.animateProperty({shape, property, startValue, endValue});
    const secondStep = () => this.animateProperty({shape, property, startValue: endValue, endValue: startValue});
    const promise = Array.from({length}).reduce(promise => promise.then(firstStep).then(secondStep), Promise.resolve(shape));

    return promise;
  }

  /**
   * Animates shape with shake effect based on direction.
   *
   * @param {Shape} shape
   * @param {String} direction
   * @returns {Promise}
   * @private
   */
  _animateShake(shape, direction = 'shakeX') {
    let startValue = shape.getX();
    let leftValue = shape.getX();
    let rightValue = shape.getX();
    let property = 'x';

    switch (direction) {
      case 'shakeX':
        startValue = shape.getX();
        leftValue = shape.getX() - this.getOffset();
        rightValue = shape.getX() + this.getOffset();
        property = 'x';
        break;
      case 'shakeY':
        startValue = shape.getY();
        leftValue = shape.getY() - this.getOffset();
        rightValue = shape.getY() + this.getOffset();
        property = 'y';
        break;
    }

    const length = this.getRepeat();
    const firstStep = () => this.animateProperty({shape, property, startValue, endValue: leftValue});
    const secondStep = () => this.animateProperty({shape, property, startValue: leftValue, endValue: rightValue});
    const thirdStep = () => this.animateProperty({shape, property, startValue: rightValue, endValue: startValue});
    const promise = Array.from({length}).reduce(promise => promise.then(firstStep).then(secondStep).then(thirdStep), Promise.resolve(shape));

    return promise;
  }

  /**
   * Main method that calls when shape need to be animated.
   *
   * @override
   * @param {Shape} shape
   * @param {Cursor} cursor
   */
  animate(shape, cursor) {
    const isBounce = this.getDirection().indexOf('bounce') !== -1;

    if (isBounce) {
      return this._animateBounce(shape, this.getDirection());
    } else {
      return this._animateShake(shape, this.getDirection());
    }
  }

  /**
   * Serializes animation to Object representation.
   *
   * @returns {Object}
   */
  toObject() {
    const obj = super.toObject();

    Object.assign(obj.options, {
      direction: this.get('direction'),
      offset: this.get('offset'),
      repeat: this.get('repeat')
    });

    return obj;
  }
}
<commit_msg>refactor(animation): Refactor animate() method<commit_after>import Animation from 'kittik-animation-basic';

const AVAILABLE_DIRECTIONS = ['bounceUp', 'bounceRight', 'bounceDown', 'bounceLeft', 'shakeX', 'shakeY'];

/**
 * Focus animation is responsible for attention seekers for your shape.
 *
 * @since 1.0.0
 */
export default class Focus extends Animation {
  constructor(options = {}) {
    super(options);

    this.setDirection(options.direction);
    this.setOffset(options.offset);
    this.setRepeat(options.repeat);
  }

  /**
   * Get total duration of the animation taking to attention repeat count.
   *
   * @returns {Number}
   */
  getDuration() {
    return this.get('duration') / this.getRepeat();
  }

  /**
   * Get direction of the animation.
   *
   * @returns {String}
   */
  getDirection() {
    return this.get('direction');
  }

  /**
   * Set new direction of the animation.
   *
   * @param {String} direction
   * @returns {Animation}
   */
  setDirection(direction = 'shakeX') {
    if (AVAILABLE_DIRECTIONS.indexOf(direction) === -1) throw new Error(`Unknown direction: ${direction}`);
    return this.set('direction', direction);
  }

  /**
   * Get interval value used for moving shape.
   *
   * @returns {Number}
   */
  getOffset() {
    return this.get('offset');
  }

  /**
   * Set interval value used for moving shape.
   *
   * @param {Number} [offset=5]
   * @returns {Animation}
   */
  setOffset(offset = 5) {
    return this.set('offset', offset);
  }

  /**
   * Get repeat count of the attractor.
   *
   * @returns {Number}
   */
  getRepeat() {
    return this.get('repeat');
  }

  /**
   * Set repeat count of the attractor.
   *
   * @param {Number} [repeat=1]
   * @returns {Animation}
   */
  setRepeat(repeat = 1) {
    return this.set('repeat', repeat);
  }

  /**
   * Animates shape with bounce effect based on direction.
   *
   * @param {Shape} shape
   * @param {String} direction
   * @returns {Promise}
   * @private
   */
  _animateBounce(shape, direction = 'bounceUp') {
    let startValue = shape.getY();
    let endValue = shape.getY();
    let property = 'y';

    switch (direction) {
      case 'bounceUp':
        startValue = shape.getY();
        endValue = shape.getY() - this.getOffset();
        property = 'y';
        break;
      case 'bounceRight':
        startValue = shape.getX();
        endValue = shape.getX() + this.getOffset();
        property = 'x';
        break;
      case 'bounceDown':
        startValue = shape.getY();
        endValue = shape.getY() + this.getOffset();
        property = 'y';
        break;
      case 'bounceLeft':
        startValue = shape.getX();
        endValue = shape.getX() - this.getOffset();
        property = 'x';
        break;
    }

    const length = this.getRepeat();
    const firstStep = () => this.animateProperty({shape, property, startValue, endValue});
    const secondStep = () => this.animateProperty({shape, property, startValue: endValue, endValue: startValue});
    const promise = Array.from({length}).reduce(promise => promise.then(firstStep).then(secondStep), Promise.resolve(shape));

    return promise;
  }

  /**
   * Animates shape with shake effect based on direction.
   *
   * @param {Shape} shape
   * @param {String} direction
   * @returns {Promise}
   * @private
   */
  _animateShake(shape, direction = 'shakeX') {
    let startValue = shape.getX();
    let leftValue = shape.getX();
    let rightValue = shape.getX();
    let property = 'x';

    switch (direction) {
      case 'shakeX':
        startValue = shape.getX();
        leftValue = shape.getX() - this.getOffset();
        rightValue = shape.getX() + this.getOffset();
        property = 'x';
        break;
      case 'shakeY':
        startValue = shape.getY();
        leftValue = shape.getY() - this.getOffset();
        rightValue = shape.getY() + this.getOffset();
        property = 'y';
        break;
    }

    const length = this.getRepeat();
    const firstStep = () => this.animateProperty({shape, property, startValue, endValue: leftValue});
    const secondStep = () => this.animateProperty({shape, property, startValue: leftValue, endValue: rightValue});
    const thirdStep = () => this.animateProperty({shape, property, startValue: rightValue, endValue: startValue});
    const promise = Array.from({length}).reduce(promise => promise.then(firstStep).then(secondStep).then(thirdStep), Promise.resolve(shape));

    return promise;
  }

  /**
   * Main method that calls when shape need to be animated.
   *
   * @override
   * @param {Shape} shape
   * @param {Cursor} cursor
   */
  animate(shape, cursor) {
    const direction = this.getDirection();

    if (/bounce/.test(direction)) return this._animateBounce(shape, direction);
    if (/shake/.test(direction)) return this._animateShake(shape, direction);

    return Promise.reject('Unknown animation type');
  }

  /**
   * Serializes animation to Object representation.
   *
   * @returns {Object}
   */
  toObject() {
    const obj = super.toObject();

    Object.assign(obj.options, {
      direction: this.get('direction'),
      offset: this.get('offset'),
      repeat: this.get('repeat')
    });

    return obj;
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import icBEM from './utils/icBEM';
import prefixClass from './utils/prefixClass';
import './styles/Section.scss';

export const COMPONENT_NAME = prefixClass('section');
const ROOT_BEM = icBEM(COMPONENT_NAME);
export const BEM = {
    root: ROOT_BEM,
    title: ROOT_BEM.element('title'),
    body: ROOT_BEM.element('body'),
    desc: ROOT_BEM.element('desc'),
};

function Section({
    title,
    desc,
    verticalSpacing, // add margin to above and below

    bodySpacing, // add padding to body for components that are not row-based
    // React props
    className,
    children,
}) {
    // Class names
    const rootClassName = classNames(
        BEM.root
            .modifier('no-margin', !verticalSpacing)
            .toString(),
        className,
    );
    const bodyClassName = classNames(
        BEM.body
            .modifier('padded', bodySpacing)
            .toString(),
        className,
    );

    // Conditional parts
    const titleArea = title && (

            {title}

    );
    const descArea = desc && (

            {desc}

    );

    return (

            {titleArea}

                {children}

            {descArea}

    );
}

Section.propTypes = {
    title: PropTypes.node,
    desc: PropTypes.node,
    verticalSpacing: PropTypes.bool,
    bodySpacing: PropTypes.bool,
};

Section.defaultProps = {
    title: undefined,
    desc: undefined,
    verticalSpacing: true,
    bodySpacing: true,
};

export default Section;
<commit_msg>Fix
 body should not include class names from wrapper<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import icBEM from './utils/icBEM';
import prefixClass from './utils/prefixClass';
import './styles/Section.scss';

export const COMPONENT_NAME = prefixClass('section');
const ROOT_BEM = icBEM(COMPONENT_NAME);
export const BEM = {
    root: ROOT_BEM,
    title: ROOT_BEM.element('title'),
    body: ROOT_BEM.element('body'),
    desc: ROOT_BEM.element('desc'),
};

function Section({
    title,
    desc,
    verticalSpacing, // add margin to above and below

    bodySpacing, // add padding to body for components that are not row-based
    // React props
    className,
    children,
}) {
    // Class names
    const rootClassName = classNames(
        BEM.root
            .modifier('no-margin', !verticalSpacing)
            .toString(),
        className,
    );
    const bodyClassName = BEM.body
        .modifier('padded', bodySpacing)
        .toString();

    // Conditional parts
    const titleArea = title && (

            {title}

    );
    const descArea = desc && (

            {desc}

    );

    return (

            {titleArea}

                {children}

            {descArea}

    );
}

Section.propTypes = {
    title: PropTypes.node,
    desc: PropTypes.node,
    verticalSpacing: PropTypes.bool,
    bodySpacing: PropTypes.bool,
};

Section.defaultProps = {
    title: undefined,
    desc: undefined,
    verticalSpacing: true,
    bodySpacing: true,
};

export default Section;
<|endoftext|>


<commit_before>$(document).ready(function() {

  var score1;
  var score2;
  var score3;
  var score4;
  var score5;
  var commentField = $('#commentField');
  var address = $('#review-address');
  var user = $('#user');
  var reviewForm = $('.review-form');
  var reviewBtn = $('#reviewBtn');
  var reviewCatagories = ['Neighborhood', 'Cleanliness', 'Management Response', 'Up to Date', 'Noise'];

  // initaialize Google Maps

  var map;
  var marker;
  var infowindow;
  var messagewindow;

  function initMap(position) {
    var geocoder = new google.maps.Geocoder;
    var userLatLong = { lat: position.coords.latitude, lng: position.coords.longitude };
    map = new google.maps.Map(document.getElementById('map'), {
      zoom: 15,
      center: userLatLong
    });

    messagewindow = new google.maps.InfoWindow({
      content: document.getElementById('message')
    });

    var input = document.getElementById('pac-input');
    var searchBox = new google.maps.places.SearchBox(input);
    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(input);

    map.addListener('bounds_changed', function() {
      searchBox.setBounds(map.getBounds());
    });

    var markers = [];

    searchBox.addListener('places_changed', function() {
      var places = searchBox.getPlaces();

      if (places.length == 0) {
        return;
      }

      console.log(places.place_id);
      getDataID(places.place_id, 'building');
      markers.forEach(function(marker) {
        marker.setMap(null);
      });
      markers = [];

      var bounds = new google.maps.LatLngBounds();

      places.forEach(function(place) {
        if (!place.geometry) {
          console.log("Returned place contains no geometry");
          return;
        }

        checkPlace(place);

        if (place.geometry.viewport) {
          bounds.union(place.geometry.viewport);
        } else {
          bounds.extend(place.geometry.location);
        }
      });
      map.fitBounds(bounds);

    });

    // Map Legend creation
    var legend = document.getElementById('legend');
    var icons = {
      badPlace: {
        name: 'Poor Score',
        icon: {
          url: '/images/mr-poopy-one.png',
          width: '50px'
        }
      },
      goodPlace: {
        name: 'Good Score',
        icon: {
          url: '/images/mr-poopy-one.png',
          width: '50px'
        }
      }
    };
    for (var key in icons) {
      var type = icons[key];
      var name = type.name;
      var icon = type.icon;
      var div = document.createElement('div');
      div.innerHTML = name + ' ';
      legend.appendChild(div);
    }

    map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend);

    // Invokes getBuildings, filling in markers for each Buidling database entry
    getBuildings();
  }

  // Checks address searches from the search box against the database, if the record doesn't exits, it POSTs it to the DB.
  function checkPlace(place) {
    // console.log(place.rating);
    $.ajax({
      method: 'GET',
      url: '/api/building/' + place.place_id
    }).done(function(buildings) {
      if (buildings) {
        // console.log(buildings);
        infowindow = new google.maps.InfoWindow({
          content: buildings.place_id
        });
        newPlaceMarker(place);
        return
      } else {
        $.ajax({
          method: 'POST',
          url: '/api/building/',
          data: {
            address: place.formatted_address,
            place_id: place.place_id
          }
        }).done(function(buildings) {
          newPlaceMarker(place);
        });
      }
    });
  }

  // Adds Markers to the map.
  function newPlaceMarker(place) {
    var placesService = new google.maps.places.PlacesService(map);
    // console.log(place.rating);

    placesService.getDetails({
      placeId: place.place_id
    }, function(result, status) {
      // console.log(result.geometry.location);
      var marker = new google.maps.Marker({
        map: map,
        place: {
          placeId: place.place_id,
          location: result.geometry.location
        },
        // label: '💩',
        icon: {
          url: '/images/mr-poopy-one.png',
          scaledSize: new google.maps.Size(55, 55)
        }
      });

      google.maps.event.addListener(marker, 'click', function() {
        console.log(marker.place.placeId);
        getDataID(marker.place.placeId, 'building');
        reviewBtn.removeClass('hidden');
      });
    });

  }

  // When the Google API is loaded, it grabs the current position from the browswer and initializes the map
  GoogleMapsLoader.onLoad(function(google) {
    // console.log('I just loaded google maps api');
    navigator.geolocation.getCurrentPosition(initMap);
  });

  // Loads the Google Maps API
  GoogleMapsLoader.load();

  // API call functions
  function getDataID(id, type) {
    var queryUrl;
    switch (type) {
      case "user":
        queryUrl = "/api/user/" + id;
        break;
      case "building":
        queryUrl = "/api/building/" + id;
        break;
      case "review":
        queryUrl = "/api/review/" + id;
        break;
      default:
        return;
    }
    $.get(queryUrl, function(data) {
      switch (type) {
        case "user":
          pageUserRender(data);
          break;
        case "building":
          pageBuildingRender(data);
          break;
        case "review":
          pageReviewRender(data);
          break;
        default:
          return;
      }
    });
  }

  function getBuildings() {
    $.get("/api/building", renderBuildings);
  }

  function renderBuildings(data) {
    if (!data.length) {
      return;
    } else {
      data.forEach(function(building) {
        newPlaceMarker(building);
      });
    }
  }

  function pageBuildingRender(building) {
    // console.log(building);

    var reviewAddress = $('#review-address');
    var reviewData = $('.review-data');

    reviewAddress.html(building.address);
    reviewAddress.attr('data-placeId', building.place_id);

    var scoreList = [];
    scoreList[0] = 0;
    scoreList[1] = 0;
    scoreList[2] = 0;
    scoreList[3] = 0;
    scoreList[4] = 0;

    if (building.Reviews.length > 0) {
      var scoresDiv = $('
');
      var scoresTable = $('
');
      var tableHead = $('
');
      var tableRow = $('
');
      scoresTable.addClass('table');
      // scoresTable.addClass('table-bordered')

      // tableRow.append('Catagory	');
      // tableRow.append('Score	');
      tableHead.append(tableRow);
      scoresTable.append(tableHead);

      for (var i = 0; i < building.Reviews.length; i++) {
        var scores = JSON.parse(building.Reviews[i].scores);
        scoreList[0] += scores[0];
        scoreList[1] += scores[1];
        scoreList[2] += scores[2];
        scoreList[3] += scores[3];
        scoreList[4] += scores[4];
      }

      for (var i = 0; i < scoreList.length; i++) {
        scoreList[i] = scoreList[i] / building.Reviews.length;
        var scoreRow = $('
');
        var currentPercentage = scoreList[i]*20;

        scoreRow.addClass('score-container').append('' + reviewCatagories[i] + ': '+parseFloat(scoreList[i]).toFixed(1)+'');
        scoresTable.append(scoreRow);

        scoreRow.append('

');
        scoresTable.append(scoreRow);

      }
      scoresDiv.append(scoresTable)
      reviewData.html(scoresDiv);

      reviewData.append('
Comments:
');

      for (var i = 0; i < building.Reviews.length; i++) {
        reviewData.append('
'+building.Reviews[i].comment+'
');
      }
    } else {
      reviewData.html('No reviews yet, be the first one to review.');
    }

  }

  $("input:radio[name=score1]").click(function() {
    score1 = parseInt($(this).val());
  });

  $("input:radio[name=score2]").click(function() {
    score2 = parseInt($(this).val());
  });

  $("input:radio[name=score3]").click(function() {
    score3 = parseInt($(this).val());
  });
  $("input:radio[name=score4]").click(function() {
    score4 = parseInt($(this).val());
  });
  $("input:radio[name=score5]").click(function() {
    score5 = parseInt($(this).val());
  });

  $('#review-submit').on('click', function(event) {
    event.preventDefault();
    var addressId = address.data('placeid');
    var userId = user.data('id');
    // console.log(score1, score2, score3, score4, score5);
    // console.log('USER ID: ' + userId);
    // console.log('ADDRESS ID: ' + addressId);
    // console.log(commentField.val());
    if (score1 && score2 && score3 && score4 && score5) {
      var temp = [score1, score2, score3, score4, score5]
      var reviewScores = JSON.stringify(temp);
      $.ajax({
        method: 'POST',
        url: '/api/review/',
        data: {
          UserFbId: userId,
          BuildingPlaceId: addressId,
          comment: commentField.val(),
          scores: reviewScores
        }
      }).done(function(review) {
        getDataID(address.data('placeid'), 'building');
        reviewForm.addClass('hidden');
        reviewBtn.removeClass('hidden');

      });
    } else {
      $('#error').modal('toggle');
      reviewBtn.removeClass('hidden');
    }
  });

  reviewBtn.on('click', function() {
    // reviewBtn.addClass('hidden');
    $('#reviewForm').modal('toggle');
  });

  $('#logout').on('click', logout);

  function logout() {
    console.log('Logging Out ');
    document.cookie = 'connect.sid=; expires=Thu, 01-Jan-70 00:00:01 GMT; Path=/';
  }

});
<commit_msg>search<commit_after>$(document).ready(function() {

  var score1;
  var score2;
  var score3;
  var score4;
  var score5;
  var commentField = $('#commentField');
  var address = $('#review-address');
  var user = $('#user');
  var reviewForm = $('.review-form');
  var reviewBtn = $('#reviewBtn');
  var reviewCatagories = ['Neighborhood', 'Cleanliness', 'Management Response', 'Up to Date', 'Noise'];

  // initaialize Google Maps

  var map;
  var marker;
  var infowindow;
  var messagewindow;

  function initMap(position) {
    var geocoder = new google.maps.Geocoder;
    var userLatLong = { lat: position.coords.latitude, lng: position.coords.longitude };
    map = new google.maps.Map(document.getElementById('map'), {
      zoom: 15,
      center: userLatLong
    });

    messagewindow = new google.maps.InfoWindow({
      content: document.getElementById('message')
    });

    var input = document.getElementById('pac-input');
    var searchBox = new google.maps.places.SearchBox(input);
    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(input);

    map.addListener('bounds_changed', function() {
      searchBox.setBounds(map.getBounds());
    });

    var markers = [];

    searchBox.addListener('places_changed', function() {
      var places = searchBox.getPlaces();

      if (places.length == 0) {
        return;
      }

      console.log(places);
      getDataID(places.place_id, 'building');
      markers.forEach(function(marker) {
        marker.setMap(null);
      });
      markers = [];

      var bounds = new google.maps.LatLngBounds();

      places.forEach(function(place) {
        if (!place.geometry) {
          console.log("Returned place contains no geometry");
          return;
        }

        checkPlace(place);

        if (place.geometry.viewport) {
          bounds.union(place.geometry.viewport);
        } else {
          bounds.extend(place.geometry.location);
        }
      });
      map.fitBounds(bounds);

    });

    // Map Legend creation
    var legend = document.getElementById('legend');
    var icons = {
      badPlace: {
        name: 'Poor Score',
        icon: {
          url: '/images/mr-poopy-one.png',
          width: '50px'
        }
      },
      goodPlace: {
        name: 'Good Score',
        icon: {
          url: '/images/mr-poopy-one.png',
          width: '50px'
        }
      }
    };
    for (var key in icons) {
      var type = icons[key];
      var name = type.name;
      var icon = type.icon;
      var div = document.createElement('div');
      div.innerHTML = name + ' ';
      legend.appendChild(div);
    }

    map.controls[google.maps.ControlPosition.RIGHT_TOP].push(legend);

    // Invokes getBuildings, filling in markers for each Buidling database entry
    getBuildings();
  }

  // Checks address searches from the search box against the database, if the record doesn't exits, it POSTs it to the DB.
  function checkPlace(place) {
    // console.log(place.rating);
    $.ajax({
      method: 'GET',
      url: '/api/building/' + place.place_id
    }).done(function(buildings) {
      if (buildings) {
        // console.log(buildings);
        infowindow = new google.maps.InfoWindow({
          content: buildings.place_id
        });
        newPlaceMarker(place);
        return
      } else {
        $.ajax({
          method: 'POST',
          url: '/api/building/',
          data: {
            address: place.formatted_address,
            place_id: place.place_id
          }
        }).done(function(buildings) {
          newPlaceMarker(place);
        });
      }
    });
  }

  // Adds Markers to the map.
  function newPlaceMarker(place) {
    var placesService = new google.maps.places.PlacesService(map);
    // console.log(place.rating);

    placesService.getDetails({
      placeId: place.place_id
    }, function(result, status) {
      // console.log(result.geometry.location);
      var marker = new google.maps.Marker({
        map: map,
        place: {
          placeId: place.place_id,
          location: result.geometry.location
        },
        // label: '💩',
        icon: {
          url: '/images/mr-poopy-one.png',
          scaledSize: new google.maps.Size(55, 55)
        }
      });

      google.maps.event.addListener(marker, 'click', function() {
        console.log(marker.place.placeId);
        getDataID(marker.place.placeId, 'building');
        reviewBtn.removeClass('hidden');
      });
    });

  }

  // When the Google API is loaded, it grabs the current position from the browswer and initializes the map
  GoogleMapsLoader.onLoad(function(google) {
    // console.log('I just loaded google maps api');
    navigator.geolocation.getCurrentPosition(initMap);
  });

  // Loads the Google Maps API
  GoogleMapsLoader.load();

  // API call functions
  function getDataID(id, type) {
    var queryUrl;
    switch (type) {
      case "user":
        queryUrl = "/api/user/" + id;
        break;
      case "building":
        queryUrl = "/api/building/" + id;
        break;
      case "review":
        queryUrl = "/api/review/" + id;
        break;
      default:
        return;
    }
    $.get(queryUrl, function(data) {
      switch (type) {
        case "user":
          pageUserRender(data);
          break;
        case "building":
          pageBuildingRender(data);
          break;
        case "review":
          pageReviewRender(data);
          break;
        default:
          return;
      }
    });
  }

  function getBuildings() {
    $.get("/api/building", renderBuildings);
  }

  function renderBuildings(data) {
    if (!data.length) {
      return;
    } else {
      data.forEach(function(building) {
        newPlaceMarker(building);
      });
    }
  }

  function pageBuildingRender(building) {
    // console.log(building);

    var reviewAddress = $('#review-address');
    var reviewData = $('.review-data');

    reviewAddress.html(building.address);
    reviewAddress.attr('data-placeId', building.place_id);

    var scoreList = [];
    scoreList[0] = 0;
    scoreList[1] = 0;
    scoreList[2] = 0;
    scoreList[3] = 0;
    scoreList[4] = 0;

    if (building.Reviews.length > 0) {
      var scoresDiv = $('
');
      var scoresTable = $('
');
      var tableHead = $('
');
      var tableRow = $('
');
      scoresTable.addClass('table');
      // scoresTable.addClass('table-bordered')

      // tableRow.append('Catagory	');
      // tableRow.append('Score	');
      tableHead.append(tableRow);
      scoresTable.append(tableHead);

      for (var i = 0; i < building.Reviews.length; i++) {
        var scores = JSON.parse(building.Reviews[i].scores);
        scoreList[0] += scores[0];
        scoreList[1] += scores[1];
        scoreList[2] += scores[2];
        scoreList[3] += scores[3];
        scoreList[4] += scores[4];
      }

      for (var i = 0; i < scoreList.length; i++) {
        scoreList[i] = scoreList[i] / building.Reviews.length;
        var scoreRow = $('
');
        var currentPercentage = scoreList[i]*20;

        scoreRow.addClass('score-container').append('' + reviewCatagories[i] + ': '+parseFloat(scoreList[i]).toFixed(1)+'');
        scoresTable.append(scoreRow);

        scoreRow.append('

');
        scoresTable.append(scoreRow);

      }
      scoresDiv.append(scoresTable)
      reviewData.html(scoresDiv);

      reviewData.append('
Comments:
');

      for (var i = 0; i < building.Reviews.length; i++) {
        reviewData.append('
'+building.Reviews[i].comment+'
');
      }
    } else {
      reviewData.html('No reviews yet, be the first one to review.');
    }

  }

  $("input:radio[name=score1]").click(function() {
    score1 = parseInt($(this).val());
  });

  $("input:radio[name=score2]").click(function() {
    score2 = parseInt($(this).val());
  });

  $("input:radio[name=score3]").click(function() {
    score3 = parseInt($(this).val());
  });
  $("input:radio[name=score4]").click(function() {
    score4 = parseInt($(this).val());
  });
  $("input:radio[name=score5]").click(function() {
    score5 = parseInt($(this).val());
  });

  $('#review-submit').on('click', function(event) {
    event.preventDefault();
    var addressId = address.data('placeid');
    var userId = user.data('id');
    // console.log(score1, score2, score3, score4, score5);
    // console.log('USER ID: ' + userId);
    // console.log('ADDRESS ID: ' + addressId);
    // console.log(commentField.val());
    if (score1 && score2 && score3 && score4 && score5) {
      var temp = [score1, score2, score3, score4, score5]
      var reviewScores = JSON.stringify(temp);
      $.ajax({
        method: 'POST',
        url: '/api/review/',
        data: {
          UserFbId: userId,
          BuildingPlaceId: addressId,
          comment: commentField.val(),
          scores: reviewScores
        }
      }).done(function(review) {
        getDataID(address.data('placeid'), 'building');
        reviewForm.addClass('hidden');
        reviewBtn.removeClass('hidden');

      });
    } else {
      $('#error').modal('toggle');
      reviewBtn.removeClass('hidden');
    }
  });

  reviewBtn.on('click', function() {
    // reviewBtn.addClass('hidden');
    $('#reviewForm').modal('toggle');
  });

  $('#logout').on('click', logout);

  function logout() {
    console.log('Logging Out ');
    document.cookie = 'connect.sid=; expires=Thu, 01-Jan-70 00:00:01 GMT; Path=/';
  }

});
<|endoftext|>


<commit_before>describe("Authentication", function () {
    beforeEach(() => {
        cy.app("clean");
    });
    describe("admin", () => {
        it("can login", () => {
            //call a scenario in app_commands/scenarios
            cy.appScenario("admin");
            cy.visit("/users/sign_in");
            cy.get('input[type="email"]').type("administrator@mampf.edu");
            cy.get('input[type="password"]').type("test123456");
            cy.get('input[type="submit"]').click();
            cy.url().should("contain", "main/start");
            cy.contains("Veranstaltungen").should("exist");
        });
    });
});

describe("Clicker Admin", function () {
    beforeEach(() => {
        cy.app("clean");
        cy.appScenario("admin");
        cy.visit("/users/sign_in");
        cy.get('input[type="email"]').type("administrator@mampf.edu");
        cy.get('input[type="password"]').type("test123456");
        cy.get('input[type="submit"]').click();
    });

    it("can create clicker", () => {
        cy.visit("/administration");
        cy.get('a[title="Clicker anlegen"]').click();
        cy.get('input[name="clicker[title]"]').type("ErsterClicker");
        cy.get("div#new-clicker-area").contains("Speichern").click();
        cy.contains("ErsterClicker").should("exist");
    });

    it("can show clicker qr", () => {
        cy.appFactories([
            ['create', 'clicker', 'with_editor']
        ]).then((clickers) => {
            cy.visit(`/clickers/${clickers[0].id}/edit`);
            cy.contains("QR-Code zeigen").click();
            cy.get("li#clickerQRCode").should("exist");
        });

    });
});<commit_msg>Add profile image test to admin<commit_after>describe("Authentication", function () {
    beforeEach(() => {
        cy.app("clean");
    });
    describe("admin", () => {
        it("can login", () => {
            //call a scenario in app_commands/scenarios
            cy.appScenario("admin");
            cy.visit("/users/sign_in");
            cy.get('input[type="email"]').type("administrator@mampf.edu");
            cy.get('input[type="password"]').type("test123456");
            cy.get('input[type="submit"]').click();
            cy.url().should("contain", "main/start");
            cy.contains("Veranstaltungen").should("exist");
        });
        it("can set profile image", () => {
            cy.appScenario("admin");
            cy.visit("/users/sign_in");
            cy.get('input[type="email"]').type("administrator@mampf.edu");
            cy.get('input[type="password"]').type("test123456");
            cy.get('input[type="submit"]').click();
            cy.visit(`/administration/profile`);
            cy.contains("Profile Image").should("exist");
            const yourFixturePath = 'files/image.png';
            cy.get('input[name="files[]"]').attachFile(yourFixturePath);
            cy.wait(100);
            cy.contains("Speichern").click();
            cy.contains("image.png").should("exist");
        });
    });
});

describe("Clicker Admin", function () {
    beforeEach(() => {
        cy.app("clean");
        cy.appScenario("admin");
        cy.visit("/users/sign_in");
        cy.get('input[type="email"]').type("administrator@mampf.edu");
        cy.get('input[type="password"]').type("test123456");
        cy.get('input[type="submit"]').click();
    });

    it("can create clicker", () => {
        cy.visit("/administration");
        cy.get('a[title="Clicker anlegen"]').click();
        cy.get('input[name="clicker[title]"]').type("ErsterClicker");
        cy.get("div#new-clicker-area").contains("Speichern").click();
        cy.contains("ErsterClicker").should("exist");
    });

    it("can show clicker qr", () => {
        cy.appFactories([
            ['create', 'clicker', 'with_editor']
        ]).then((clickers) => {
            cy.visit(`/clickers/${clickers[0].id}/edit`);
            cy.contains("QR-Code zeigen").click();
            cy.get("li#clickerQRCode").should("exist");
        });

    });
});<|endoftext|>


<commit_before>// INITIALIZE SOCKET

var socket = io();

// BIND BROWSER EVENTS TO SOCKET EVENT EMITTERS
function afterReady() {
    // Loads registration modal on ready
    $('.modal-content').load('views/register.html', function() {
        $('.modal').modal({
            show: true,
            backdrop: 'static',
            keyboard: false,
        });
    });

    // Insert content dynamically so that a common modal can be used
    $('.modal').on('show.bs.modal', function(e) {
        var dir = $(e.relatedTarget).attr('href');

        // insert content for modals called via click
        if (dir !== undefined) {
            var $modal = $(this);
            var $content = $modal.find('.modal-content');

            // clears content
            $content.html('');

            // load new content
            $('.modal-content').load(dir);
        }
    });

    // User registration handler
    $('.modal').on('submit', 'form', function(e) {
        e.preventDefault();
        console.log(e);

        // Send data
        var $input = $(this).find('input');
        var val = $input.val();
        var id = $(this).attr('id');

        if (id === 'registration') {
            console.log('emit registration event');
            register(val);
        } else if (id === 'create-room') {
            console.log('emit create rm event');
            createRoom(val);
        } else if (id === 'join-room') {
            console.log('emit join room event');
            joinRoom(val);
        }
    });

    // Validation visuals based on keystroke
    var waiting;
    $('.modal').on('keyup', 'form input', function() {
        var $form = $('.modal form');
        var $self = $(this);

        // Wait for user to 'finish' typing
        clearTimeout(waiting);
        waiting = setTimeout(function() {

            checkInput($form.attr('id'), $self.val());

        }, 500);
    });

    // Send message event handler

    $('#send-message').click(function(e) {
        console.log("clicking send")
        e.preventDefault();

        var msg = $('#message').val().trim();

        if (msg !== '') {
            console.log(msg);
            sendMsg(msg);
            receiveMsg({msg: msg});
            $('#message').val('');
        }
    });
}

// SOCKET EVENT LISTENERS

socket.on('message', function(data) {
    console.log('receiving msg');
    receiveMsg(data);
});

socket.on('rm-update-success', function(data) {
    console.log('success');

    $('.modal').modal('hide');
    $('.message-history').append("
" + data.msg);
    $('#rm-name').text(data.room.name);
});

socket.on('form-accept', function() {
    console.log('form ok')
    var $form = $('.modal form');

    // Input box visuals
    var $formGroup = $form.find('.form-group');
    var $icon = $form.find('.glyphicon');
    $formGroup
        .removeClass('has-error')
        .removeClass('has-success')
        .addClass('has-success');

    $icon
        .removeClass('glyphicon-remove')
        .removeClass('glyphicon-ok')
        .addClass('glyphicon-ok');

    // Remove any alert message
    $form.find('.alert').remove();

    // Enable submit button
    $form.find('button[type="submit"]').prop('disabled', false);
});

socket.on('form-reject', function(errMsg) {
    console.log('form not ok')
    var $form = $('.modal form');

    // Input box visuals
    var $formGroup = $form.find('.form-group');
    var $icon = $form.find('.glyphicon');

    $form.find('.form-group')
        .removeClass('has-success')
        .removeClass('has-error')
        .addClass('has-error');

    $form.find('.glyphicon')
        .removeClass('glyphicon-ok')
        .removeClass('glyphicon-remove')
        .addClass('glyphicon-remove');

    // Add/replace alert message
    var $alert = $form.find('.alert');
    var m = "
" + errMsg + "
";

    if ($alert.length === 0) { // New error
        $form.find('.form-group').after(m);
    } else { // Recurring error
        $alert.html(errMsg);
    }

    // Disable submit button
    $form.find('button[type="submit"]').prop('disabled', true);
});

// SOCKET EVENT EMITTERS

function register(nickname) {
    socket.emit('register', nickname);
}

function createRoom(roomName) {
    socket.emit('create-room', roomName);
}

function joinRoom(roomName) {
    socket.emit('join-room', roomName);
}

function checkInput(formName, val) {
    socket.emit('validate', formName, val);
}

function sendMsg(msg) {
    socket.emit('message', msg);
}

// HELPER
function receiveMsg(data) {
    console.log('receive msg fn');
    if (data.id !== socket.id && data.id !== undefined) {
        console.log('from others')
        $('.message-history').append("
" + data.name + ": " + data.msg);
    } else {
        console.log('from yourself')
        $('.message-history').append("
you: " + data.msg);
    }
}

// Init
$(document).ready(afterReady);
<commit_msg>Reduce validation timeout<commit_after>// INITIALIZE SOCKET

var socket = io();

// BIND BROWSER EVENTS TO SOCKET EVENT EMITTERS
function afterReady() {
    // Loads registration modal on ready
    $('.modal-content').load('views/register.html', function() {
        $('.modal').modal({
            show: true,
            backdrop: 'static',
            keyboard: false,
        });
    });

    // Insert content dynamically so that a common modal can be used
    $('.modal').on('show.bs.modal', function(e) {
        var dir = $(e.relatedTarget).attr('href');

        // insert content for modals called via click
        if (dir !== undefined) {
            var $modal = $(this);
            var $content = $modal.find('.modal-content');

            // clears content
            $content.html('');

            // load new content
            $('.modal-content').load(dir);
        }
    });

    // User registration handler
    $('.modal').on('submit', 'form', function(e) {
        e.preventDefault();
        console.log(e);

        // Send data
        var $input = $(this).find('input');
        var val = $input.val();
        var id = $(this).attr('id');

        if (id === 'registration') {
            console.log('emit registration event');
            register(val);
        } else if (id === 'create-room') {
            console.log('emit create rm event');
            createRoom(val);
        } else if (id === 'join-room') {
            console.log('emit join room event');
            joinRoom(val);
        }
    });

    // Validation visuals based on keystroke
    var waiting;
    $('.modal').on('keyup', 'form input', function() {
        var $form = $('.modal form');
        var $self = $(this);

        // Wait for user to 'finish' typing
        clearTimeout(waiting);
        waiting = setTimeout(function() {

            checkInput($form.attr('id'), $self.val());

        }, 250);
    });

    // Send message event handler

    $('#send-message').click(function(e) {
        console.log("clicking send")
        e.preventDefault();

        var msg = $('#message').val().trim();

        if (msg !== '') {
            console.log(msg);
            sendMsg(msg);
            receiveMsg({msg: msg});
            $('#message').val('');
        }
    });
}

// SOCKET EVENT LISTENERS

socket.on('message', function(data) {
    console.log('receiving msg');
    receiveMsg(data);
});

socket.on('rm-update-success', function(data) {
    console.log('success');

    $('.modal').modal('hide');
    $('.message-history').append("
" + data.msg);
    $('#rm-name').text(data.room.name);
});

socket.on('form-accept', function() {
    console.log('form ok')
    var $form = $('.modal form');

    // Input box visuals
    var $formGroup = $form.find('.form-group');
    var $icon = $form.find('.glyphicon');
    $formGroup
        .removeClass('has-error')
        .removeClass('has-success')
        .addClass('has-success');

    $icon
        .removeClass('glyphicon-remove')
        .removeClass('glyphicon-ok')
        .addClass('glyphicon-ok');

    // Remove any alert message
    $form.find('.alert').remove();

    // Enable submit button
    $form.find('button[type="submit"]').prop('disabled', false);
});

socket.on('form-reject', function(errMsg) {
    console.log('form not ok')
    var $form = $('.modal form');

    // Input box visuals
    var $formGroup = $form.find('.form-group');
    var $icon = $form.find('.glyphicon');

    $form.find('.form-group')
        .removeClass('has-success')
        .removeClass('has-error')
        .addClass('has-error');

    $form.find('.glyphicon')
        .removeClass('glyphicon-ok')
        .removeClass('glyphicon-remove')
        .addClass('glyphicon-remove');

    // Add/replace alert message
    var $alert = $form.find('.alert');
    var m = "
" + errMsg + "
";

    if ($alert.length === 0) { // New error
        $form.find('.form-group').after(m);
    } else { // Recurring error
        $alert.html(errMsg);
    }

    // Disable submit button
    $form.find('button[type="submit"]').prop('disabled', true);
});

// SOCKET EVENT EMITTERS

function register(nickname) {
    socket.emit('register', nickname);
}

function createRoom(roomName) {
    socket.emit('create-room', roomName);
}

function joinRoom(roomName) {
    socket.emit('join-room', roomName);
}

function checkInput(formName, val) {
    socket.emit('validate', formName, val);
}

function sendMsg(msg) {
    socket.emit('message', msg);
}

// HELPER
function receiveMsg(data) {
    console.log('receive msg fn');
    if (data.id !== socket.id && data.id !== undefined) {
        console.log('from others')
        $('.message-history').append("
" + data.name + ": " + data.msg);
    } else {
        console.log('from yourself')
        $('.message-history').append("
you: " + data.msg);
    }
}

// Init
$(document).ready(afterReady);
<|endoftext|>


<commit_before>$(function() {

    /**
     * Is mobile device
     */

    window.isMobile = (typeof window.orientation !== 'undefined');
    window.isMobile = true;

    /**
     * Page loader
     */

    window.isLeaving = false;
    window.loader = {
        html: $('#page-loader'),
        show: function()
        {
            $('body').addClass('fixed');
            window.loader.html.addClass('active');
        },
        hide: function()
        {
            $('body').removeClass('fixed');
            window.loader.html.removeClass('active');
        }
    };

    if(window.isMobile) {

        // listen all outgoing link
        $('a').on('click', function()
        {
            var href = $(this).attr('href');
            if(href && href != '#') {
                window.isLeaving = true;
            }
        });

        // display loader when leaving
        $(window).on('beforeunload', function()
        {
            if(window.isLeaving) {
                window.loader.show();
            }
        });

    }

    /**
     * Redirect function
     */

    window.redirect = function(to)
    {
        window.isLeaving = true;
        window.location.replace(to);
    };

    /**
     * Reload function
     */

    window.reload = function()
    {
        window.isLeaving = true;
        window.location.reload();
    };

});<commit_msg>Fix debug<commit_after>$(function() {

    /**
     * Is mobile device
     */

    window.isMobile = (typeof window.orientation !== 'undefined');

    /**
     * Page loader
     */

    window.isLeaving = false;
    window.loader = {
        html: $('#page-loader'),
        show: function()
        {
            $('body').addClass('fixed');
            window.loader.html.addClass('active');
        },
        hide: function()
        {
            $('body').removeClass('fixed');
            window.loader.html.removeClass('active');
        }
    };

    if(window.isMobile) {

        // listen all outgoing link
        $('a').on('click', function()
        {
            var href = $(this).attr('href');
            if(href && href != '#') {
                window.isLeaving = true;
            }
        });

        // display loader when leaving
        $(window).on('beforeunload', function()
        {
            if(window.isLeaving) {
                window.loader.show();
            }
        });

    }

    /**
     * Redirect function
     */

    window.redirect = function(to)
    {
        window.isLeaving = true;
        window.location.replace(to);
    };

    /**
     * Reload function
     */

    window.reload = function()
    {
        window.isLeaving = true;
        window.location.reload();
    };

});<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import { canvas } from '@instructure/ui-themes/lib'
import './support/sinon/sinon-qunit-1.0.0'

const fixturesDiv = document.createElement('div')
fixturesDiv.id = 'fixtures'
document.body.appendChild(fixturesDiv)

if (!window.ENV) window.ENV = {}

// setup the inst-ui default theme
canvas.use()

const requireAll = context => context.keys().map(context)

if (process.env.JSPEC_PATH) {
  let isFile = false
  try {
    isFile = __webpack_modules__[require.resolveWeak(`../../${process.env.JSPEC_PATH}`)]
  } catch (e) {}
  if (isFile) {
    require(`../../${process.env.JSPEC_PATH}`)
  } else {
    requireAll(require.context(`../../${process.env.JSPEC_PATH}`))
  }
} else {
  if (!process.env.JSPEC_GROUP || (process.env.JSPEC_GROUP === 'coffee')) {
    // run specs for ember screenreader gradebook
    requireAll(require.context('../../app/coffeescripts', !!'includeSubdirectories', /\.spec.coffee$/))

    requireAll(require.context('../coffeescripts', !!'includeSubdirectories', /Spec.js$/))
    requireAll(require.context('../coffeescripts', !!'includeSubdirectories', /Spec.coffee$/))
  }

  // Run the js tests in 2 different groups, half in each.
  // In testing, the letter "q" was the midpoint. If one of these takes a lot
  // longer than the other, we can adjust which letter of the alphabet we split on
  if (!process.env.JSPEC_GROUP || (process.env.JSPEC_GROUP === 'js1')) {
    requireAll(require.context('./jsx', !!'includeSubdirectories', /[a-q]Spec$/))
  }
  if (!process.env.JSPEC_GROUP || (process.env.JSPEC_GROUP === 'js2')) {
    requireAll(require.context('./jsx', !!'includeSubdirectories', /[^a-q]Spec$/))
  }
}
<commit_msg>spec: set html dir attribute in base config<commit_after>/*
 * Copyright (C) 2015 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import { canvas } from '@instructure/ui-themes/lib'
import './support/sinon/sinon-qunit-1.0.0'

document.dir = 'ltr'
const fixturesDiv = document.createElement('div')
fixturesDiv.id = 'fixtures'
document.body.appendChild(fixturesDiv)

if (!window.ENV) window.ENV = {}

// setup the inst-ui default theme
canvas.use()

const requireAll = context => context.keys().map(context)

if (process.env.JSPEC_PATH) {
  let isFile = false
  try {
    isFile = __webpack_modules__[require.resolveWeak(`../../${process.env.JSPEC_PATH}`)]
  } catch (e) {}
  if (isFile) {
    require(`../../${process.env.JSPEC_PATH}`)
  } else {
    requireAll(require.context(`../../${process.env.JSPEC_PATH}`))
  }
} else {
  if (!process.env.JSPEC_GROUP || (process.env.JSPEC_GROUP === 'coffee')) {
    // run specs for ember screenreader gradebook
    requireAll(require.context('../../app/coffeescripts', !!'includeSubdirectories', /\.spec.coffee$/))

    requireAll(require.context('../coffeescripts', !!'includeSubdirectories', /Spec.js$/))
    requireAll(require.context('../coffeescripts', !!'includeSubdirectories', /Spec.coffee$/))
  }

  // Run the js tests in 2 different groups, half in each.
  // In testing, the letter "q" was the midpoint. If one of these takes a lot
  // longer than the other, we can adjust which letter of the alphabet we split on
  if (!process.env.JSPEC_GROUP || (process.env.JSPEC_GROUP === 'js1')) {
    requireAll(require.context('./jsx', !!'includeSubdirectories', /[a-q]Spec$/))
  }
  if (!process.env.JSPEC_GROUP || (process.env.JSPEC_GROUP === 'js2')) {
    requireAll(require.context('./jsx', !!'includeSubdirectories', /[^a-q]Spec$/))
  }
}
<|endoftext|>


<commit_before>(function($){

var confirms = [],
	prompts = [],
	currentDocument,
	currentHref,
	lookingForNewDocument = false,
	urlWithoutHash = function(url){
		return url.replace(/\#.*$/, "");
	},
	absolutize = function(url){
		var f = steal.File(url);
		return f.protocol() ? f.path : f.joinFrom(steal.pageUrl().dir(), true);
	},
	// returns true if url matches current window's url
	isCurrentPage = function(url){
		var pathname = urlWithoutHash(FuncUnit.win.location.pathname),
			href = urlWithoutHash(FuncUnit.win.location.href),
			url = urlWithoutHash(url);
		// must strip off hash from URLs
		if( pathname === url || href === url ){
			return true;
		}
		return false;
	};
/**
 * @add FuncUnit
 */
$.extend(FuncUnit,{
	/**
	 * @attribute browsers
	 * Used to configure the browsers selenium uses to run FuncUnit tests.  See the
	 * [funcunit.selenium Selenium] page for more information.
	 */

	// open is a method
	/**
	 * Opens a page.  It will error if the page can't be opened before timeout. If a URL begins with "//", pages are opened
	 * from the FuncUnit root (the root folder where funcunit is located)
	 * ### Example

    S.open("//app/app.html")

	 *
	 * @param {String} path a full or partial url to open.
	 * @param {Function} success
	 * @param {Number} timeout
	 */
	open: function( path, success, timeout ) {
		var fullPath = FuncUnit.getAbsolutePath(path),
		temp;
		if(typeof success != 'function'){
			timeout = success;
			success = undefined;
		}
		FuncUnit.add({
			method: function(success, error){ //function that actually does stuff, if this doesn't call success by timeout, error will be called, or can call error itself
				steal.dev.log("Opening " + path)
				FuncUnit._open(fullPath, error);
				FuncUnit._onload(function(){
					success()
				}, error);
			},
			success: success,
			error: "Page " + path + " not loaded in time!",
			timeout: timeout || 30000
		});
	},
	_open: function(url){
		hasSteal = false;
		// this will determine if this is supposed to open within a frame
		FuncUnit.frame =  $('#funcunit_app').length? $('#funcunit_app')[0]: null;

		// if the first time ..
		if (newPage) {
			if(FuncUnit.frame){
				FuncUnit.win = FuncUnit.frame.contentWindow;
				FuncUnit.win.location = url;
			}
			else{
				// giving a large height forces it to not open in a new tab and just opens to the window's height
				var width = $(window).width();
				FuncUnit.win = window.open(url, "funcunit",  "height=1000,toolbar=yes,status=yes,width="+width/2+",left="+width/2);
				// This is mainly for opera. Other browsers will hit the unload event and close the popup.
				// This block breaks in IE (which never reaches it) because after closing a window, it throws access
				// denied any time you try to access it, even after reopening.
				if(FuncUnit.win.___FUNCUNIT_OPENED){
					FuncUnit.win.close();
					FuncUnit.win = window.open(url, "funcunit",  "height=1000,toolbar=yes,status=yes,left="+width/2);
				}

				if(!FuncUnit.win){
					throw "Could not open a popup window.  Your popup blocker is probably on.  Please turn it off and try again";
				}
			}
		}
		// otherwise, change the frame's url
		else {
			lookingForNewDocument = true;
			if(isCurrentPage(url)){
				// set the hash and reload
				FuncUnit.win.location.hash = url.split('#')[1] || '';
				FuncUnit.win.location.reload(true);
			} else {
				// setting the location forces a reload; IE freaks out if you try to do both
				FuncUnit.win.location = url;
			}
			// setting to null b/c opera uses the same document
			currentDocument = null;
		}
		lookingForNewDocument = true;
	},
	/**
	 * When a browser's native confirm dialog is used, this method is used to repress the dialog and simulate
	 * clicking OK or Cancel.  Alerts are repressed by default in FuncUnit application windows.
	 * @codestart
	 * S.confirm(true);
	 * @codeend
	 * @param {Boolean} answer true if you want to click OK, false otherwise
	 */
	confirm: function(answer){
		confirms.push(!!answer)
	},
	/**
	 * When a browser's native prompt dialog is used, this method is used to repress the dialog and simulate
	 * clicking typing something into the dialog.
	 * @codestart
	 * S.prompt("Harry Potter");
	 * @codeend
	 * @param {String} answer Whatever you want to simulate a user typing in the prompt box
	 */
	prompt: function(answer){
		prompts.push(answer)
	},
	_opened: function(){
		if (!this._isOverridden("alert")) {
			FuncUnit.win.alert = function(){}
		}

		if (!this._isOverridden("confirm")) {
			FuncUnit.win.confirm = function(){
				var res = confirms.shift();
				return res;
			}
		}

		if (!this._isOverridden("prompt")) {
			FuncUnit.win.prompt = function(){
				return prompts.shift();
			}
		}
	},
	_isOverridden:function(type) {
		return !(/(native code)|(source code not available)/.test(FuncUnit.win[type]));
	},
	_onload: function(success, error){
		// saver reference to success
		loadSuccess = function(){
			if(FuncUnit.win.steal){
				hasSteal = true;
			}
			// called when load happens ... here we check for steal
			// console.log("success", (FuncUnit.win.steal && FuncUnit.win.steal.isReady) || !hasSteal,
				// "isReady", (FuncUnit.win.steal && FuncUnit.win.steal.isReady));
			if((FuncUnit.win.steal && FuncUnit.win.steal.isReady) || !hasSteal){
				success();
			}else{
				setTimeout(arguments.callee, 200)
			}

		}

		// we only need to do this setup stuff once ...
		if (!newPage) {
			return;
		}
		newPage = false;

		if (FuncUnit.support.readystate)
		{
			poller();
		}
		else {
			unloadLoader();
		}

	},
	/**
	 * @hide
	 * Gets a path, will use steal if present
	 * @param {String} path
	 */
	getAbsolutePath: function( path ) {
		if ( /^\/\//.test(path) ){
			return steal.File(absolutize(steal.root.path)).join(path.substr(2)) + '';
		} else {
			return absolutize(path);
		}
	},
	/**
	 * @attribute win
	 * Use this to refer to the window of the application page.  You can also
	 * reference window.document.
	 * @codestart
	 * S(S.window).innerWidth(function(w){
	 *   ok(w > 1000, "window is more than 1000 px wide")
	 * })
	 * @codeend
	 */
	win: null,
	// for feature detection
	support: {
		readystate: "readyState" in document
	},
	/**
	 * Used to evaluate code in the application page.
	 * @param {String} str the code to evaluate
	 * @return {Object} the result of the evaluated code
	 */
	eval: function(str){
		return FuncUnit.win.eval(str)
	},
	// return true if document is currently loaded, false if its loading
	// actions check this
	documentLoaded: function(){
		var loaded = FuncUnit.win.document.readyState === "complete" &&
				     FuncUnit.win.location.href != "about:blank";
		return loaded;
	},
	// return true if new document found
	checkForNewDocument: function(){
		var documentFound = ((FuncUnit.win.document !== currentDocument && // new document
							!FuncUnit.win.___FUNCUNIT_OPENED) // hasn't already been marked loaded
							// covers opera case after you click a link, since document doesn't change in opera
							|| (currentHref != FuncUnit.win.location.href)) && // url is different
							FuncUnit.documentLoaded(); // fully loaded
		if(documentFound){
			// reset flags
			lookingForNewDocument = false;
			currentDocument = FuncUnit.win.document;
			currentHref = FuncUnit.win.location.href;

			// mark it as opened
			FuncUnit.win.___FUNCUNIT_OPENED = true;
			// reset confirm, prompt, alert
			FuncUnit._opened();
		}

		return documentFound;
	}
});

	//don't do any of this if in rhino
	if (navigator.userAgent.match(/Rhino/)) {
		return;
	}

	FuncUnit.win = null;
	var newPage = true,
		hasSteal = false,
		unloadLoader,
		loadSuccess,
		firstLoad = true,
		onload = function(){
			FuncUnit.win.document.documentElement.tabIndex = 0;
			setTimeout(function(){
				FuncUnit.win.focus();
				var ls = loadSuccess
				loadSuccess = null;
				if (ls) {
					ls();
				}
			}, 0);
			Syn.unbind(FuncUnit.win, "load", onload);
		},
		onunload = function(){
			FuncUnit.stop = true;
			removeListeners();
			setTimeout(unloadLoader, 0)

		},
		removeListeners = function(){
			Syn.unbind(FuncUnit.win, "unload", onunload);
			Syn.unbind(FuncUnit.win, "load", onload);
		}
	unloadLoader = function(){
		if(!firstLoad) // dont remove the first run, fixes issue in FF 3.6
			removeListeners();

		Syn.bind(FuncUnit.win, "load", onload);

		//listen for unload to re-attach
		Syn.bind(FuncUnit.win, "unload", onunload)
	}

	//check for window location change, documentChange, then readyState complete -> fire load if you have one
	var newDocument = false,
		poller = function(){
			var ls;
			// right after setting a new hash and reloading, IE barfs on this occassionally (only the first time)
			try{
				if(FuncUnit.win && FuncUnit.win.document == null){
					return;
				}
			}catch(e){
				setTimeout(arguments.callee, 500);
				return;
			}

			if (lookingForNewDocument && FuncUnit.checkForNewDocument() ) {

				ls = loadSuccess;

				loadSuccess = null;
				if (ls) {
					FuncUnit.win.focus();
					FuncUnit.win.document.documentElement.tabIndex = 0;

					ls();
				}
			}

		setTimeout(arguments.callee, 500)
	}

	// All browsers except Opera close the app window on a reload.  This is to fix the case the URL to be opened
	// has a hash.  In this case, window.open doesn't cause a reload if you reuse an existing popup, so we need to close.
	$(window).unload(function(){
		FuncUnit.win && FuncUnit.win.close();
	});

})(window.jQuery || window.FuncUnit.jQuery)
<commit_msg>added alternative code to open a full window browser during tests<commit_after>(function($){

var confirms = [],
	prompts = [],
	currentDocument,
	currentHref,
	lookingForNewDocument = false,
	urlWithoutHash = function(url){
		return url.replace(/\#.*$/, "");
	},
	absolutize = function(url){
		var f = steal.File(url);
		return f.protocol() ? f.path : f.joinFrom(steal.pageUrl().dir(), true);
	},
	// returns true if url matches current window's url
	isCurrentPage = function(url){
		var pathname = urlWithoutHash(FuncUnit.win.location.pathname),
			href = urlWithoutHash(FuncUnit.win.location.href),
			url = urlWithoutHash(url);
		// must strip off hash from URLs
		if( pathname === url || href === url ){
			return true;
		}
		return false;
	};
/**
 * @add FuncUnit
 */
$.extend(FuncUnit,{
	/**
	 * @attribute browsers
	 * Used to configure the browsers selenium uses to run FuncUnit tests.  See the
	 * [funcunit.selenium Selenium] page for more information.
	 */

	// open is a method
	/**
	 * Opens a page.  It will error if the page can't be opened before timeout. If a URL begins with "//", pages are opened
	 * from the FuncUnit root (the root folder where funcunit is located)
	 * ### Example

    S.open("//app/app.html")

	 *
	 * @param {String} path a full or partial url to open.
	 * @param {Function} success
	 * @param {Number} timeout
	 */
	open: function( path, success, timeout ) {
		var fullPath = FuncUnit.getAbsolutePath(path),
		temp;
		if(typeof success != 'function'){
			timeout = success;
			success = undefined;
		}
		FuncUnit.add({
			method: function(success, error){ //function that actually does stuff, if this doesn't call success by timeout, error will be called, or can call error itself
				steal.dev.log("Opening " + path)
				FuncUnit._open(fullPath, error);
				FuncUnit._onload(function(){
					success()
				}, error);
			},
			success: success,
			error: "Page " + path + " not loaded in time!",
			timeout: timeout || 30000
		});
	},
	_open: function(url){
		hasSteal = false;
		// this will determine if this is supposed to open within a frame
		FuncUnit.frame =  $('#funcunit_app').length? $('#funcunit_app')[0]: null;

		// if the first time ..
		if (newPage) {
			if(FuncUnit.frame){
				FuncUnit.win = FuncUnit.frame.contentWindow;
				FuncUnit.win.location = url;
			}
			else{
				// giving a large height forces it to not open in a new tab and just opens to the window's height
				var width = $(window).width();
        FuncUnit.win = window.open(url, "funcunit",  "height=1000,toolbar=yes,status=yes,width="+width/2+",left="+width/2);
				//FuncUnit.win = window.open(url, "funcunit",  "height=1000,toolbar=yes,status=yes,width="+width);
				// This is mainly for opera. Other browsers will hit the unload event and close the popup.
				// This block breaks in IE (which never reaches it) because after closing a window, it throws access
				// denied any time you try to access it, even after reopening.
				if(FuncUnit.win.___FUNCUNIT_OPENED){
					FuncUnit.win.close();
					FuncUnit.win = window.open(url, "funcunit",  "height=1000,toolbar=yes,status=yes,left="+width/2);
				}

				if(!FuncUnit.win){
					throw "Could not open a popup window.  Your popup blocker is probably on.  Please turn it off and try again";
				}
			}
		}
		// otherwise, change the frame's url
		else {
			lookingForNewDocument = true;
			if(isCurrentPage(url)){
				// set the hash and reload
				FuncUnit.win.location.hash = url.split('#')[1] || '';
				FuncUnit.win.location.reload(true);
			} else {
				// setting the location forces a reload; IE freaks out if you try to do both
				FuncUnit.win.location = url;
			}
			// setting to null b/c opera uses the same document
			currentDocument = null;
		}
		lookingForNewDocument = true;
	},
	/**
	 * When a browser's native confirm dialog is used, this method is used to repress the dialog and simulate
	 * clicking OK or Cancel.  Alerts are repressed by default in FuncUnit application windows.
	 * @codestart
	 * S.confirm(true);
	 * @codeend
	 * @param {Boolean} answer true if you want to click OK, false otherwise
	 */
	confirm: function(answer){
		confirms.push(!!answer)
	},
	/**
	 * When a browser's native prompt dialog is used, this method is used to repress the dialog and simulate
	 * clicking typing something into the dialog.
	 * @codestart
	 * S.prompt("Harry Potter");
	 * @codeend
	 * @param {String} answer Whatever you want to simulate a user typing in the prompt box
	 */
	prompt: function(answer){
		prompts.push(answer)
	},
	_opened: function(){
		if (!this._isOverridden("alert")) {
			FuncUnit.win.alert = function(){}
		}

		if (!this._isOverridden("confirm")) {
			FuncUnit.win.confirm = function(){
				var res = confirms.shift();
				return res;
			}
		}

		if (!this._isOverridden("prompt")) {
			FuncUnit.win.prompt = function(){
				return prompts.shift();
			}
		}
	},
	_isOverridden:function(type) {
		return !(/(native code)|(source code not available)/.test(FuncUnit.win[type]));
	},
	_onload: function(success, error){
		// saver reference to success
		loadSuccess = function(){
			if(FuncUnit.win.steal){
				hasSteal = true;
			}
			// called when load happens ... here we check for steal
			// console.log("success", (FuncUnit.win.steal && FuncUnit.win.steal.isReady) || !hasSteal,
				// "isReady", (FuncUnit.win.steal && FuncUnit.win.steal.isReady));
			if((FuncUnit.win.steal && FuncUnit.win.steal.isReady) || !hasSteal){
				success();
			}else{
				setTimeout(arguments.callee, 200)
			}

		}

		// we only need to do this setup stuff once ...
		if (!newPage) {
			return;
		}
		newPage = false;

		if (FuncUnit.support.readystate)
		{
			poller();
		}
		else {
			unloadLoader();
		}

	},
	/**
	 * @hide
	 * Gets a path, will use steal if present
	 * @param {String} path
	 */
	getAbsolutePath: function( path ) {
		if ( /^\/\//.test(path) ){
			return steal.File(absolutize(steal.root.path)).join(path.substr(2)) + '';
		} else {
			return absolutize(path);
		}
	},
	/**
	 * @attribute win
	 * Use this to refer to the window of the application page.  You can also
	 * reference window.document.
	 * @codestart
	 * S(S.window).innerWidth(function(w){
	 *   ok(w > 1000, "window is more than 1000 px wide")
	 * })
	 * @codeend
	 */
	win: null,
	// for feature detection
	support: {
		readystate: "readyState" in document
	},
	/**
	 * Used to evaluate code in the application page.
	 * @param {String} str the code to evaluate
	 * @return {Object} the result of the evaluated code
	 */
	eval: function(str){
		return FuncUnit.win.eval(str)
	},
	// return true if document is currently loaded, false if its loading
	// actions check this
	documentLoaded: function(){
		var loaded = FuncUnit.win.document.readyState === "complete" &&
				     FuncUnit.win.location.href != "about:blank";
		return loaded;
	},
	// return true if new document found
	checkForNewDocument: function(){
		var documentFound = ((FuncUnit.win.document !== currentDocument && // new document
							!FuncUnit.win.___FUNCUNIT_OPENED) // hasn't already been marked loaded
							// covers opera case after you click a link, since document doesn't change in opera
							|| (currentHref != FuncUnit.win.location.href)) && // url is different
							FuncUnit.documentLoaded(); // fully loaded
		if(documentFound){
			// reset flags
			lookingForNewDocument = false;
			currentDocument = FuncUnit.win.document;
			currentHref = FuncUnit.win.location.href;

			// mark it as opened
			FuncUnit.win.___FUNCUNIT_OPENED = true;
			// reset confirm, prompt, alert
			FuncUnit._opened();
		}

		return documentFound;
	}
});

	//don't do any of this if in rhino
	if (navigator.userAgent.match(/Rhino/)) {
		return;
	}

	FuncUnit.win = null;
	var newPage = true,
		hasSteal = false,
		unloadLoader,
		loadSuccess,
		firstLoad = true,
		onload = function(){
			FuncUnit.win.document.documentElement.tabIndex = 0;
			setTimeout(function(){
				FuncUnit.win.focus();
				var ls = loadSuccess
				loadSuccess = null;
				if (ls) {
					ls();
				}
			}, 0);
			Syn.unbind(FuncUnit.win, "load", onload);
		},
		onunload = function(){
			FuncUnit.stop = true;
			removeListeners();
			setTimeout(unloadLoader, 0)

		},
		removeListeners = function(){
			Syn.unbind(FuncUnit.win, "unload", onunload);
			Syn.unbind(FuncUnit.win, "load", onload);
		}
	unloadLoader = function(){
		if(!firstLoad) // dont remove the first run, fixes issue in FF 3.6
			removeListeners();

		Syn.bind(FuncUnit.win, "load", onload);

		//listen for unload to re-attach
		Syn.bind(FuncUnit.win, "unload", onunload)
	}

	//check for window location change, documentChange, then readyState complete -> fire load if you have one
	var newDocument = false,
		poller = function(){
			var ls;
			// right after setting a new hash and reloading, IE barfs on this occassionally (only the first time)
			try{
				if(FuncUnit.win && FuncUnit.win.document == null){
					return;
				}
			}catch(e){
				setTimeout(arguments.callee, 500);
				return;
			}

			if (lookingForNewDocument && FuncUnit.checkForNewDocument() ) {

				ls = loadSuccess;

				loadSuccess = null;
				if (ls) {
					FuncUnit.win.focus();
					FuncUnit.win.document.documentElement.tabIndex = 0;

					ls();
				}
			}

		setTimeout(arguments.callee, 500)
	}

	// All browsers except Opera close the app window on a reload.  This is to fix the case the URL to be opened
	// has a hash.  In this case, window.open doesn't cause a reload if you reuse an existing popup, so we need to close.
	$(window).unload(function(){
		FuncUnit.win && FuncUnit.win.close();
	});

})(window.jQuery || window.FuncUnit.jQuery)
<|endoftext|>