<commit_before>/*!
 * QUnit CLI Boilerplate v1.3.0
 * Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
 * Based on a gist by Jörn Zaefferer <https://gist.github.com/722381>
 * Available under MIT license <http://mths.be/mit>
 */
;(function(root) {
  'use strict';

  /** Detect free variable `exports` */
  var freeExports = typeof exports == 'object' && exports;

  /** Detect free variable `global`, from Node.js or Browserified code, and use it as `root` */
  var freeGlobal = typeof global == 'object' && global;
  if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
    root = freeGlobal;
  }

  /*--------------------------------------------------------------------------*/

  /**
   * Installs the CLI boilerplate additions on the given `context` object.
   *
   * @memberOf exports
   * @param {Object} context The context object.
   */
  function runInContext(context) {
    // exit early if no `context` is provided or if `QUnit` does not exist
    if (!context || !context.QUnit) {
      return;
    }

    /**
     * Schedules timer-based callbacks.
     *
     * @private
     * @param {Function|string} fn The function to call.
     * @param {number} delay The number of milliseconds to delay the `fn` call.
     * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
     * @param {boolean} repeated A flag to specify whether `fn` is called repeatedly.
     * @returns {number} The the ID of the timeout.
     */
    function schedule(fn, delay, args, repeated) {
      // Rhino 1.7RC4 will error assigning `task` below
      // https://bugzilla.mozilla.org/show_bug.cgi?id=775566
      var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, {
        'run': function() {
          fn.apply(context, args);
        }
      });
      // support non-functions
      if (typeof fn != 'function') {
        fn = (function(code) {
          code = String(code);
          return function() { eval(code); };
        }(fn));
      }
      // used by setInterval
      if (repeated) {
        timer.schedule(task, delay, delay);
      }
      // used by setTimeout
      else {
        timer.schedule(task, delay);
      }
      return counter;
    }

    /**
     * Clears the delay set by `setInterval` or `setTimeout`.
     *
     * @memberOf context
     * @param {number} id The ID of the timeout to be cleared.
     */
    function clearTimer(id) {
      if (ids[id]) {
        ids[id].cancel();
        timer.purge();
        delete ids[id];
      }
    }

    /**
     * Executes a code snippet or function repeatedly, with a delay between each call.
     *
     * @memberOf context
     * @param {Function|string} fn The function to call or string to evaluate.
     * @param {number} delay The number of milliseconds to delay each `fn` call.
     * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
     * @returns {number} The the ID of the timeout.
     */
    function setInterval(fn, delay) {
      return schedule(fn, delay, slice.call(arguments, 2), true);
    }

    /**
     * Executes a code snippet or a function after specified delay.
     *
     * @memberOf context
     * @param {Function|string} fn The function to call or string to evaluate.
     * @param {number} delay The number of milliseconds to delay the `fn` call.
     * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
     * @returns {number} The the ID of the timeout.
     */
    function setTimeout(fn, delay) {
      return schedule(fn, delay, slice.call(arguments, 2));
    }

    /*------------------------------------------------------------------------*/

    /** Used to report the test module for failing tests */
    var moduleName,
        modulePrinted;

    /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */
    var console = context.console || (context.console = { 'log': context.print });

    /** Used as a horizontal rule in console output */
    var hr = '----------------------------------------';

    /** Used by `logInline` to clear previously logged messages */
    var prevLine = '';

    /** Shorten `context.QUnit.QUnit` to `context.QUnit` */
    var QUnit = context.QUnit = context.QUnit.QUnit || context.QUnit;

    /**
     * Logs an inline message to standard output.
     *
     * @private
     * @param {string} text The text to log.
     */
    var logInline = (function() {
      // exit early if not Node.js
      if (!(typeof process == 'object' && process && process.on && process.stdout)) {
        return function() {};
      }
      // cleanup any inline logs when exited via `ctrl+c`
      process.on('SIGINT', function() {
        logInline('');
        process.exit();
      });
      return function(text) {
        var blankLine = Array(prevLine.length + 1).join(' ');
        if (text.length > hr.length) {
          text = text.slice(0, hr.length - 3) + '...';
        }
        prevLine = text;
        process.stdout.write(text + blankLine.slice(text.length) + '\r');
      }
    }());

    /**
     * A logging callback triggered when all testing is completed.
     *
     * @memberOf QUnit
     * @param {Object} details An object with properties `failed`, `passed`, `runtime`, and `total`.
     */
    QUnit.done(function() {
      var ran;
      return function(details) {
        // stop `asyncTest()` from erroneously calling `done()` twice in
        // environments w/o timeouts
        if (ran) {
          return;
        }
        ran = true;

        logInline('');
        console.log(hr);
        console.log('    PASS: ' + details.passed + '  FAIL: ' + details.failed + '  TOTAL: ' + details.total);
        console.log('    Finished in ' + details.runtime + ' milliseconds.');
        console.log(hr);

        // exit out of Narhwal, Rhino, or Ringo
        try {
          quit();
        } catch(e) { }

        // exit out of Node.js or PhantomJS
        try {
          var process = context.process || context.phantom;
          if (details.failed) {
            console.error('Error: ' + details.failed + ' of ' + details.total + ' tests failed.');
            process.exit(1);
          } else {
            process.exit(0);
          }
        } catch(e) { }
      };
    }());

    /**
     * A logging callback triggered after every assertion.
     *
     * @memberOf QUnit
     * @param {Object} details An object with properties `actual`, `expected`, `message`, and `result`.
     */
    QUnit.log(function(details) {
      var expected = details.expected,
          result = details.result,
          type = typeof expected != 'undefined' ? 'EQ' : 'OK';

      var assertion = [
        result ? 'PASS' : 'FAIL',
        type,
        details.message || 'ok'
      ];

      if (!result && type == 'EQ') {
        assertion.push('Expected: ' + expected + ', Actual: ' + details.actual);
      }
      QUnit.config.testStats.assertions.push(assertion.join(' | '));
    });

    /**
     * A logging callback triggered at the start of every test module.
     *
     * @memberOf QUnit
     * @param {Object} details An object with property `name`.
     */
    QUnit.moduleStart(function(details) {
      var newModuleName = details.name;
      if (moduleName != newModuleName) {
        moduleName = newModuleName;
        modulePrinted = false;
      }
    });

    /**
     * Converts an object into a string representation.
     *
     * @memberOf QUnit
     * @type Function
     * @param {Object} object The object to stringify.
     * @returns {string} The result string.
     */
    QUnit.jsDump.parsers.object = (function() {
      var func = QUnit.jsDump.parsers.object;
      return function(object) {
        // fork to support Rhino's error objects
        if (typeof object.rhinoException == 'object') {
          return object.name +
            ' { message: "' + object.message +
            '", fileName: "' + object.fileName +
            '", lineNumber: ' + object.lineNumber + ' }';
        }
        return func(object);
      };
    }());

    /**
     * A logging callback triggered after a test is completed.
     *
     * @memberOf QUnit
     * @param {Object} details An object with properties `failed`, `name`, `passed`, and `total`.
     */
    QUnit.testDone(function(details) {
      var assertions = QUnit.config.testStats.assertions,
          testName = details.name;

      if (details.failed > 0) {
        logInline('');
        if (!modulePrinted) {
          modulePrinted = true;
          console.log(hr);
          console.log(moduleName);
          console.log(hr);
        }
        console.log(' FAIL - '+ testName);
        assertions.forEach(function(value) {
          console.log('    ' + value);
        });
      } else {
        logInline('Testing ' + moduleName + '...');
      }
      assertions.length = 0;
    });

    /**
     * An object used to hold information about the current running test.
     *
     * @memberOf QUnit.config
     * @type Object
     */
    QUnit.config.testStats = {

      /**
       * An array of test summaries.
       *
       * @memberOf QUnit.config.testStats
       * @type Array
       */
      'assertions': []
    };

    /*------------------------------------------------------------------------*/

    // Timeout fallbacks based on the work of Andrea Giammarchi and Weston C.
    // https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js
    // http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout
    try {
      var counter = 0,
          ids = {},
          slice = Array.prototype.slice,
          timer = new java.util.Timer;

      context.clearInterval =
      context.clearTimeout = clearTimer;
      context.setInterval = setInterval;
      context.setTimeout = setTimeout;
    } catch(e) { }

    // expose shortcuts
    // exclude `module` because some environments have it as a built-in object
    ('asyncTest deepEqual equal equals expect notDeepEqual notEqual notStrictEqual ' +
     'ok raises same start stop strictEqual test throws').replace(/\S+/g, function(methodName) {
      context[methodName] = QUnit[methodName];
    });

    // must call `QUnit.start()` in the test file if not loaded in a browser
    if (!context.document || context.phantom) {
      QUnit.config.autostart = false;
      QUnit.init();
    }
  }

  /*--------------------------------------------------------------------------*/

  // expose QUnit CLIB
  if (freeExports && !freeExports.nodeType) {
    freeExports.runInContext = runInContext;
  } else {
    runInContext(root);
  }
}(this));
<commit_msg>Avoid `logInline` when running on Windows.<commit_after>/*!
 * QUnit CLI Boilerplate v1.3.0
 * Copyright 2011-2012 John-David Dalton <http://allyoucanleet.com/>
 * Based on a gist by Jörn Zaefferer <https://gist.github.com/722381>
 * Available under MIT license <http://mths.be/mit>
 */
;(function(root) {
  'use strict';

  /** Detect free variable `exports` */
  var freeExports = typeof exports == 'object' && exports;

  /** Detect free variable `global`, from Node.js or Browserified code, and use it as `root` */
  var freeGlobal = typeof global == 'object' && global;
  if (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal) {
    root = freeGlobal;
  }

  /*--------------------------------------------------------------------------*/

  /**
   * Installs the CLI boilerplate additions on the given `context` object.
   *
   * @memberOf exports
   * @param {Object} context The context object.
   */
  function runInContext(context) {
    // exit early if no `context` is provided or if `QUnit` does not exist
    if (!context || !context.QUnit) {
      return;
    }

    /**
     * Schedules timer-based callbacks.
     *
     * @private
     * @param {Function|string} fn The function to call.
     * @param {number} delay The number of milliseconds to delay the `fn` call.
     * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
     * @param {boolean} repeated A flag to specify whether `fn` is called repeatedly.
     * @returns {number} The the ID of the timeout.
     */
    function schedule(fn, delay, args, repeated) {
      // Rhino 1.7RC4 will error assigning `task` below
      // https://bugzilla.mozilla.org/show_bug.cgi?id=775566
      var task = ids[++counter] = new JavaAdapter(java.util.TimerTask, {
        'run': function() {
          fn.apply(context, args);
        }
      });
      // support non-functions
      if (typeof fn != 'function') {
        fn = (function(code) {
          code = String(code);
          return function() { eval(code); };
        }(fn));
      }
      // used by setInterval
      if (repeated) {
        timer.schedule(task, delay, delay);
      }
      // used by setTimeout
      else {
        timer.schedule(task, delay);
      }
      return counter;
    }

    /**
     * Clears the delay set by `setInterval` or `setTimeout`.
     *
     * @memberOf context
     * @param {number} id The ID of the timeout to be cleared.
     */
    function clearTimer(id) {
      if (ids[id]) {
        ids[id].cancel();
        timer.purge();
        delete ids[id];
      }
    }

    /**
     * Executes a code snippet or function repeatedly, with a delay between each call.
     *
     * @memberOf context
     * @param {Function|string} fn The function to call or string to evaluate.
     * @param {number} delay The number of milliseconds to delay each `fn` call.
     * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
     * @returns {number} The the ID of the timeout.
     */
    function setInterval(fn, delay) {
      return schedule(fn, delay, slice.call(arguments, 2), true);
    }

    /**
     * Executes a code snippet or a function after specified delay.
     *
     * @memberOf context
     * @param {Function|string} fn The function to call or string to evaluate.
     * @param {number} delay The number of milliseconds to delay the `fn` call.
     * @param [arg1, arg2, ...] Arguments to invoke `fn` with.
     * @returns {number} The the ID of the timeout.
     */
    function setTimeout(fn, delay) {
      return schedule(fn, delay, slice.call(arguments, 2));
    }

    /*------------------------------------------------------------------------*/

    /** Used to report the test module for failing tests */
    var moduleName,
        modulePrinted;

    /** Add `console.log()` support for Narwhal, Rhino, and RingoJS */
    var console = context.console || (context.console = { 'log': context.print });

    /** Used as a horizontal rule in console output */
    var hr = '----------------------------------------';

    /** Used by `logInline` to clear previously logged messages */
    var prevLine = '';

    /** Shorten `context.QUnit.QUnit` to `context.QUnit` */
    var QUnit = context.QUnit = context.QUnit.QUnit || context.QUnit;

    /**
     * Logs an inline message to standard output.
     *
     * @private
     * @param {string} text The text to log.
     */
    var logInline = (function() {
      // exit early if not Node.js
      if (!(typeof process == 'object' && process &&
          process.on && process.stdout && process.platform != 'win32')) {
        return function() {};
      }
      // cleanup any inline logs when exited via `ctrl+c`
      process.on('SIGINT', function() {
        logInline('');
        process.exit();
      });
      return function(text) {
        var blankLine = Array(prevLine.length + 1).join(' ');
        if (text.length > hr.length) {
          text = text.slice(0, hr.length - 3) + '...';
        }
        prevLine = text;
        process.stdout.write(text + blankLine.slice(text.length) + '\r');
      }
    }());

    /**
     * A logging callback triggered when all testing is completed.
     *
     * @memberOf QUnit
     * @param {Object} details An object with properties `failed`, `passed`, `runtime`, and `total`.
     */
    QUnit.done(function() {
      var ran;
      return function(details) {
        // stop `asyncTest()` from erroneously calling `done()` twice in
        // environments w/o timeouts
        if (ran) {
          return;
        }
        ran = true;

        logInline('');
        console.log(hr);
        console.log('    PASS: ' + details.passed + '  FAIL: ' + details.failed + '  TOTAL: ' + details.total);
        console.log('    Finished in ' + details.runtime + ' milliseconds.');
        console.log(hr);

        // exit out of Narhwal, Rhino, or RingoJS
        try {
          quit();
        } catch(e) { }

        // exit out of Node.js or PhantomJS
        try {
          var process = context.process || context.phantom;
          if (details.failed) {
            console.error('Error: ' + details.failed + ' of ' + details.total + ' tests failed.');
            process.exit(1);
          } else {
            process.exit(0);
          }
        } catch(e) { }
      };
    }());

    /**
     * A logging callback triggered after every assertion.
     *
     * @memberOf QUnit
     * @param {Object} details An object with properties `actual`, `expected`, `message`, and `result`.
     */
    QUnit.log(function(details) {
      var expected = details.expected,
          result = details.result,
          type = typeof expected != 'undefined' ? 'EQ' : 'OK';

      var assertion = [
        result ? 'PASS' : 'FAIL',
        type,
        details.message || 'ok'
      ];

      if (!result && type == 'EQ') {
        assertion.push('Expected: ' + expected + ', Actual: ' + details.actual);
      }
      QUnit.config.testStats.assertions.push(assertion.join(' | '));
    });

    /**
     * A logging callback triggered at the start of every test module.
     *
     * @memberOf QUnit
     * @param {Object} details An object with property `name`.
     */
    QUnit.moduleStart(function(details) {
      var newModuleName = details.name;
      if (moduleName != newModuleName) {
        moduleName = newModuleName;
        modulePrinted = false;
      }
    });

    /**
     * Converts an object into a string representation.
     *
     * @memberOf QUnit
     * @type Function
     * @param {Object} object The object to stringify.
     * @returns {string} The result string.
     */
    QUnit.jsDump.parsers.object = (function() {
      var func = QUnit.jsDump.parsers.object;
      return function(object) {
        // fork to support Rhino's error objects
        if (typeof object.rhinoException == 'object') {
          return object.name +
            ' { message: "' + object.message +
            '", fileName: "' + object.fileName +
            '", lineNumber: ' + object.lineNumber + ' }';
        }
        return func(object);
      };
    }());

    /**
     * A logging callback triggered after a test is completed.
     *
     * @memberOf QUnit
     * @param {Object} details An object with properties `failed`, `name`, `passed`, and `total`.
     */
    QUnit.testDone(function(details) {
      var assertions = QUnit.config.testStats.assertions,
          testName = details.name;

      if (details.failed > 0) {
        logInline('');
        if (!modulePrinted) {
          modulePrinted = true;
          console.log(hr);
          console.log(moduleName);
          console.log(hr);
        }
        console.log(' FAIL - '+ testName);
        assertions.forEach(function(value) {
          console.log('    ' + value);
        });
      } else {
        logInline('Testing ' + moduleName + '...');
      }
      assertions.length = 0;
    });

    /**
     * An object used to hold information about the current running test.
     *
     * @memberOf QUnit.config
     * @type Object
     */
    QUnit.config.testStats = {

      /**
       * An array of test summaries.
       *
       * @memberOf QUnit.config.testStats
       * @type Array
       */
      'assertions': []
    };

    /*------------------------------------------------------------------------*/

    // Timeout fallbacks based on the work of Andrea Giammarchi and Weston C.
    // https://github.com/WebReflection/wru/blob/master/src/rhinoTimers.js
    // http://stackoverflow.com/questions/2261705/how-to-run-a-javascript-function-asynchronously-without-using-settimeout
    try {
      var counter = 0,
          ids = {},
          slice = Array.prototype.slice,
          timer = new java.util.Timer;

      context.clearInterval =
      context.clearTimeout = clearTimer;
      context.setInterval = setInterval;
      context.setTimeout = setTimeout;
    } catch(e) { }

    // expose shortcuts
    // exclude `module` because some environments have it as a built-in object
    ('asyncTest deepEqual equal equals expect notDeepEqual notEqual notStrictEqual ' +
     'ok raises same start stop strictEqual test throws').replace(/\S+/g, function(methodName) {
      context[methodName] = QUnit[methodName];
    });

    // must call `QUnit.start()` in the test file if not loaded in a browser
    if (!context.document || context.phantom) {
      QUnit.config.autostart = false;
      QUnit.init();
    }
  }

  /*--------------------------------------------------------------------------*/

  // expose QUnit CLIB
  if (freeExports && !freeExports.nodeType) {
    freeExports.runInContext = runInContext;
  } else {
    runInContext(root);
  }
}(this));
<|endoftext|>


<commit_before>const {utils, Base} = JParticles;
const {random, abs, PI} = Math;
const twicePI = PI * 2;
const {
    pInt, limitRandom, calcSpeed,
    scaleValue, getCss, offset,
    isElement, modifyPrototype,
    defineReadOnlyProperty
} = utils;

/**
 * 检查元素或其祖先节点的属性是否等于预给值
 * @param elem {element} 起始元素
 * @param property {string} css属性
 * @param value {string} css属性值
 * @returns {boolean}
 */
function checkParentsProperty(elem, property, value) {
    while (elem = elem.offsetParent) {
        if (getCss(elem, property) === value) {
            return true;
        }
    }
    return false;
}

function eventHandler(eventType) {
    const {num, range, eventElem} = this.set;

    if (num > 0 && range > 0) {

        // 使用传递过来的关键字判断绑定事件还是移除事件
        eventType = eventType === 'pause' ? 'off' : 'on';
        utils[eventType](eventElem, 'mousemove', this.moveHandler);
    }
}

class Particle extends Base {

    static defaultConfig = {

        // 粒子个数，默认为容器宽度的 0.12 倍
        // 传入 (0, 1) 显示容器宽度相应倍数的个数，传入 [1, +∞) 显示具体个数
        num: .12,

        // 粒子最大半径(0, +∞)
        maxR: 2.4,

        // 粒子最小半径(0, +∞)
        minR: .6,

        // 粒子最大运动速度(0, +∞)
        maxSpeed: 1,

        // 粒子最小运动速度(0, +∞)
        minSpeed: 0,

        // 两点连线的最大值
        // 在 range 范围内的两点距离小于 distance，则两点之间连线
        distance: 130,

        // 线段的宽度
        lineWidth: .2,

        // 定位点的范围，范围越大连线越多，当 range 等于 0 时，不连线，相关值无效
        range: 160,

        // 改变定位点坐标的事件元素，null 表示 canvas 画布，或传入原生元素对象，如 document 等
        eventElem: null
    };

    get version() {
        return '3.0.0';
    }

    constructor(selector, options) {
        super(Particle, selector, options);
    }

    init() {
        const {num, range, eventElem} = this.set;

        if (num > 0) {
            if (range > 0) {

                // 设置移动事件元素
                if (!isElement(eventElem) && eventElem !== document) {
                    this.set.eventElem = this.c;
                }

                // 定位点坐标
                this.posX = random() * this.cw;
                this.posY = random() * this.ch;
                this.event();
            }
            this.createDots();
            this.draw();
        }
    }

    createDots() {
        const {cw, ch, color} = this;
        const {num, maxR, minR, maxSpeed, minSpeed} = this.set;
        let realNumber = pInt(scaleValue(num, cw));
        let dots = this.dots = [];
        let r;

        while (realNumber--) {
            r = limitRandom(maxR, minR);
            dots.push({
                r,
                x: limitRandom(cw - r, r),
                y: limitRandom(ch - r, r),
                vx: calcSpeed(maxSpeed, minSpeed),
                vy: calcSpeed(maxSpeed, minSpeed),
                color: color()
            });
        }
    }

    draw() {
        const {cw, ch, cxt, paused} = this;
        const {num, range, lineWidth, opacity} = this.set;

        if (num <= 0) return;

        cxt.clearRect(0, 0, cw, ch);

        // 当canvas宽高改变的时候，全局属性需要重新设置
        cxt.lineWidth = lineWidth;
        cxt.globalAlpha = opacity;

        this.dots.forEach(dot => {
            const r = dot.r;

            cxt.save();
            cxt.beginPath();
            cxt.arc(dot.x, dot.y, r, 0, twicePI);
            cxt.fillStyle = dot.color;
            cxt.fill();
            cxt.restore();

            // 暂停的时候，vx和vy保持不变，
            // 处理自适应窗口变化时出现粒子移动的状态
            if (!paused) {
                dot.x += dot.vx;
                dot.y += dot.vy;

                const x = dot.x;
                const y = dot.y;

                if (x + r >= cw || x - r <= 0) {
                    dot.vx *= -1;
                }

                if (y + r >= ch || y - r <= 0) {
                    dot.vy *= -1;
                }
            }
        });

        // 当连接范围小于 0 时，不连接线段
        if (range > 0) {
            this.connectDots();
        }

        this.requestAnimationFrame();
    }

    connectDots() {
        const {dots, cxt, posX, posY} = this;
        const {distance, range} = this.set;
        const length = dots.length;

        dots.forEach((dot, i) => {
            const x = dot.x;
            const y = dot.y;
            const color = dot.color;

            while (++i < length) {
                const sibDot = dots[i];
                const sx = sibDot.x;
                const sy = sibDot.y;

                if (abs(x - sx) <= distance &&
                    abs(y - sy) <= distance &&
                    (abs(x - posX) <= range &&
                    abs(y - posY) <= range ||
                    abs(sx - posX) <= range &&
                    abs(sy - posY) <= range)) {
                    cxt.save();
                    cxt.beginPath();
                    cxt.moveTo(x, y);
                    cxt.lineTo(sx, sy);
                    cxt.strokeStyle = color;
                    cxt.stroke();
                    cxt.restore();
                }
            }
        });
    }

    getElemOffset() {
        return (this.elemOffset = this.elemOffset ? offset(this.set.eventElem) : null);
    }

    event() {
        const {eventElem} = this.set;

        if (eventElem !== document) {
            this.elemOffset = true;
        }

        // move 事件处理函数
        this.moveHandler = function (e) {
            this.posX = e.pageX;
            this.posY = e.pageY;

            // 动态计算 elemOffset 值
            if (this.getElemOffset()) {

                // 动态判断祖先节点是否具有固定定位，有则使用client计算
                if (checkParentsProperty(eventElem, 'position', 'fixed')) {
                    this.posX = e.clientX;
                    this.posY = e.clientY;
                }
                this.posX -= this.elemOffset.left;
                this.posY -= this.elemOffset.top;
            }
        }.bind(this);

        // 添加 move 事件
        eventHandler.call(this);
    }

    resize() {
        utils.resize(this, (scaleX, scaleY) => {
            const {num, range} = this.set;
            if (num > 0 && range > 0) {
                this.posX *= scaleX;
                this.posY *= scaleY;
                this.getElemOffset();
            }
        });
    }
}

// 修改原型 pause, open 方法
modifyPrototype(Particle.prototype, 'pause, open', eventHandler);

// 使用防止属性被更改的 appendProperty 方法，
// 挂载插件到 JParticles 对象上。
defineReadOnlyProperty(Particle, 'particle');<commit_msg>modify the variable scope<commit_after>const {utils, Base} = JParticles;
const {random, abs, PI} = Math;
const twicePI = PI * 2;
const {
    pInt, limitRandom, calcSpeed,
    scaleValue, getCss, offset,
    isElement, modifyPrototype,
    defineReadOnlyProperty
} = utils;

/**
 * 检查元素或其祖先节点的属性是否等于预给值
 * @param elem {element} 起始元素
 * @param property {string} css属性
 * @param value {string} css属性值
 * @returns {boolean}
 */
function checkParentsProperty(elem, property, value) {
    while (elem = elem.offsetParent) {
        if (getCss(elem, property) === value) {
            return true;
        }
    }
    return false;
}

function eventHandler(eventType) {
    const {num, range, eventElem} = this.set;

    if (num > 0 && range > 0) {

        // 使用传递过来的关键字判断绑定事件还是移除事件
        eventType = eventType === 'pause' ? 'off' : 'on';
        utils[eventType](eventElem, 'mousemove', this.moveHandler);
    }
}

class Particle extends Base {

    static defaultConfig = {

        // 粒子个数，默认为容器宽度的 0.12 倍
        // (0, 1) 显示为容器宽度相应倍数的个数，[1, +∞) 显示具体个数
        num: .12,

        // 粒子最大半径(0, +∞)
        maxR: 2.4,

        // 粒子最小半径(0, +∞)
        minR: .6,

        // 粒子最大运动速度(0, +∞)
        maxSpeed: 1,

        // 粒子最小运动速度(0, +∞)
        minSpeed: 0,

        // 两点连线的最大值
        // 在 range 范围内的两点距离小于 distance，则两点之间连线
        distance: 130,

        // 线段的宽度
        lineWidth: .2,

        // 定位点的范围，范围越大连线越多
        // 当 range 等于 0 时，不连线，相关值无效
        range: 160,

        // 改变定位点坐标的事件元素
        // null 表示 canvas 画布，或传入原生元素对象，如 document 等
        eventElem: null
    };

    get version() {
        return '3.0.0';
    }

    constructor(selector, options) {
        super(Particle, selector, options);
    }

    init() {
        const {num, range, eventElem} = this.set;

        if (num > 0) {
            if (range > 0) {

                // 设置移动事件元素
                if (!isElement(eventElem) && eventElem !== document) {
                    this.set.eventElem = this.c;
                }

                // 定位点坐标
                this.posX = random() * this.cw;
                this.posY = random() * this.ch;
                this.event();
            }
            this.createDots();
            this.draw();
        }
    }

    createDots() {
        const {cw, ch, color} = this;
        const {num, maxR, minR, maxSpeed, minSpeed} = this.set;
        let realNumber = pInt(scaleValue(num, cw));
        let dots = this.dots = [];

        while (realNumber--) {
            let r = limitRandom(maxR, minR);
            dots.push({
                r,
                x: limitRandom(cw - r, r),
                y: limitRandom(ch - r, r),
                vx: calcSpeed(maxSpeed, minSpeed),
                vy: calcSpeed(maxSpeed, minSpeed),
                color: color()
            });
        }
    }

    draw() {
        const {cw, ch, cxt, paused} = this;
        const {num, range, lineWidth, opacity} = this.set;

        if (num <= 0) return;

        cxt.clearRect(0, 0, cw, ch);

        // 当canvas宽高改变的时候，全局属性需要重新设置
        cxt.lineWidth = lineWidth;
        cxt.globalAlpha = opacity;

        this.dots.forEach(dot => {
            const r = dot.r;

            cxt.save();
            cxt.beginPath();
            cxt.arc(dot.x, dot.y, r, 0, twicePI);
            cxt.fillStyle = dot.color;
            cxt.fill();
            cxt.restore();

            // 暂停的时候，vx和vy保持不变，
            // 处理自适应窗口变化时出现粒子移动的状态
            if (!paused) {
                dot.x += dot.vx;
                dot.y += dot.vy;

                const x = dot.x;
                const y = dot.y;

                if (x + r >= cw || x - r <= 0) {
                    dot.vx *= -1;
                }

                if (y + r >= ch || y - r <= 0) {
                    dot.vy *= -1;
                }
            }
        });

        // 当连接范围小于 0 时，不连接线段
        if (range > 0) {
            this.connectDots();
        }

        this.requestAnimationFrame();
    }

    connectDots() {
        const {dots, cxt, posX, posY} = this;
        const {distance, range} = this.set;
        const length = dots.length;

        dots.forEach((dot, i) => {
            const x = dot.x;
            const y = dot.y;
            const color = dot.color;

            while (++i < length) {
                const sibDot = dots[i];
                const sx = sibDot.x;
                const sy = sibDot.y;

                if (abs(x - sx) <= distance &&
                    abs(y - sy) <= distance &&
                    (abs(x - posX) <= range &&
                    abs(y - posY) <= range ||
                    abs(sx - posX) <= range &&
                    abs(sy - posY) <= range)) {
                    cxt.save();
                    cxt.beginPath();
                    cxt.moveTo(x, y);
                    cxt.lineTo(sx, sy);
                    cxt.strokeStyle = color;
                    cxt.stroke();
                    cxt.restore();
                }
            }
        });
    }

    getElemOffset() {
        return (this.elemOffset = this.elemOffset ? offset(this.set.eventElem) : null);
    }

    event() {
        const {eventElem} = this.set;

        if (eventElem !== document) {
            this.elemOffset = true;
        }

        // move 事件处理函数
        this.moveHandler = function (e) {
            this.posX = e.pageX;
            this.posY = e.pageY;

            // 动态计算 elemOffset 值
            if (this.getElemOffset()) {

                // 动态判断祖先节点是否具有固定定位，有则使用client计算
                if (checkParentsProperty(eventElem, 'position', 'fixed')) {
                    this.posX = e.clientX;
                    this.posY = e.clientY;
                }
                this.posX -= this.elemOffset.left;
                this.posY -= this.elemOffset.top;
            }
        }.bind(this);

        // 添加 move 事件
        eventHandler.call(this);
    }

    resize() {
        utils.resize(this, (scaleX, scaleY) => {
            const {num, range} = this.set;
            if (num > 0 && range > 0) {
                this.posX *= scaleX;
                this.posY *= scaleY;
                this.getElemOffset();
            }
        });
    }
}

// 修改原型 pause, open 方法
modifyPrototype(Particle.prototype, 'pause, open', eventHandler);

// 使用防止属性被更改的 appendProperty 方法，
// 挂载插件到 JParticles 对象上。
defineReadOnlyProperty(Particle, 'particle');<|endoftext|>


<commit_before>
(function poll() {
  if ( typeof ynabToolKit !== "undefined" && ynabToolKit.pageReady === true ) {

    ynabToolKit.highlightNegativesNegative = new function ()  {

      this.invoke = function() {
        var availableBalances = $('.budget-table-cell-available-div.user-data');
        var categoryBalance = {};

        $(availableBalances).each(function () {
          categoryBalance = $(this).find('.user-data.currency').html(); // get the value
            categoryBalance = Number( categoryBalance.replace(/[^\d.-]/g, '') ); // force data type as number

          if ( categoryBalance < 0 ) {

              if ( $(this).find('.user-data.currency').hasClass('cautious') ) {
                $(this).find('.user-data.currency').removeClass('cautious').addClass('negative')
              };
          };

        });
      },

      this.observe = function(changedNodes) {

        if (changedNodes.has('budget-inspector')) {
          // The user has changed their budget row selection
          ynabToolKit.highlightNegativesNegative.invoke();
        }
      };

    };
    ynabToolKit.highlightNegativesNegative.invoke(); // call itself once

  } else {
    setTimeout(poll, 250);
  }
})();

<commit_msg>Refactor highlight negatives<commit_after>(function poll() {
  if ( typeof ynabToolKit !== "undefined" && ynabToolKit.pageReady === true ) {
    ynabToolKit.highlightNegativesNegative = (function(){

      // Supporting functions,
      // or variables, etc

      return {
        invoke: function() {
          var availableBalances = $('.budget-table-cell-available-div.user-data');
          var categoryBalance = {};

          $(availableBalances).each(function () {
            categoryBalance = $(this).find('.user-data.currency').html(); // get the value
              categoryBalance = Number( categoryBalance.replace(/[^\d.-]/g, '') ); // force data type as number

            if ( categoryBalance < 0 ) {

                if ( $(this).find('.user-data.currency').hasClass('cautious') ) {
                  $(this).find('.user-data.currency').removeClass('cautious').addClass('negative');
                }
            }

          });
        },

        observe: function(changedNodes) {
          if (changedNodes.has('budget-inspector')) {
            // The user has changed their budget row selection
            ynabToolKit.highlightNegativesNegative.invoke();
          }
        }
      };
    })(); // Keep feature functions contained within this object

    ynabToolKit.highlightNegativesNegative.invoke(); // call itself once

  } else {
    setTimeout(poll, 250);
  }
})();
<|endoftext|>


<commit_before>"use strict";

const child_process = require("child_process");
const fs = require("fs");
const os = require("os");
const path = require("path");
const process = require("process");

const tldsort = require("./libs/tldsort");
const parseRuleset = require("./libs/parseRuleset");
const splitDomainsByTlds = require("./libs/splitDomainsByTlds");
const XHTTPSE_PATH = process.env.XHTTPSE_PATH || "../bin/xhttpse";

// check the number of arguments
if (process.argv.length <= 2) {
  console.log("Usage: node ruleset-update.js [FILE]");
  process.exit();
}

// create a backup of the input FILE if it does not exist
const filename = path.normalize(process.argv[2]);
const backupFilename = filename + ".bak";
if (!fs.existsSync(backupFilename)) {
  fs.copyFileSync(filename, backupFilename);
}

// read the content of the input FILE
const xmlContent = fs.readFileSync(filename, { encoding: "utf8" });
const ruleset = parseRuleset(xmlContent);

// sort domains by top-level domains alphabetically, remove duplicates
ruleset.target.hosts = tldsort(ruleset.target.hosts);

// handle multiple TLDs rulesets
const domainGroups = splitDomainsByTlds(ruleset.target.hosts);
for (const domains of domainGroups) {
  // create temporary file
  const tmpFilename = path.join(
    os.tmpdir(),
    "FUXMERANDM-" + Date.now().toString()
  );

  fs.writeFileSync(tmpFilename, domains.join("\n"), {
    encoding: "utf8",
  });

  // construct commands from raw parts to rewrite the ruleset completely
  const rawCommandParts = [
    XHTTPSE_PATH,
    "--num-threads " + os.cpus().length,
    ruleset.securecookie ? "--securecookie" : null,
    "--name " +
      (domainGroups.length === 1
        ? `'${ruleset.metadata.name}'`
        : `'${domains[0]}'`),
    "-o " +
      (domainGroups.length === 1
        ? `'${filename}'`
        : `'${path.dirname(filename)}/${domains[0]}.xml'`),
    `'${tmpFilename}'`,
  ];

  const command = rawCommandParts.filter((part) => part != null).join(" ");
  child_process.execSync(command);

  // remove the original file if neccessary
  if (domainGroups.length > 1) {
    fs.unlinkSync(filename);
  }

  // remove temporary file
  fs.unlinkSync(tmpFilename);
}
<commit_msg>Update ruleset-update.js (#30)<commit_after>"use strict";

const child_process = require("child_process");
const fs = require("fs");
const os = require("os");
const path = require("path");
const process = require("process");

const tldsort = require("./libs/tldsort");
const parseRuleset = require("./libs/parseRuleset");
const splitDomainsByTlds = require("./libs/splitDomainsByTlds");
const XHTTPSE_PATH = process.env.XHTTPSE_PATH || "../bin/xhttpse";

// check the number of arguments
if (process.argv.length <= 2) {
  console.log("Usage: node ruleset-update.js [FILE]");
  process.exit();
}

// create a backup of the input FILE if it does not exist
// path.normalize mess up files with underscore in their names
const filename = process.argv[2];
const backupFilename = filename + ".bak";
if (!fs.existsSync(backupFilename)) {
  fs.copyFileSync(filename, backupFilename);
}

// read the content of the input FILE
const xmlContent = fs.readFileSync(filename, { encoding: "utf8" });
const ruleset = parseRuleset(xmlContent);

// sort domains by top-level domains alphabetically, remove duplicates
ruleset.target.hosts = tldsort(ruleset.target.hosts);

// handle multiple TLDs rulesets
const domainGroups = splitDomainsByTlds(ruleset.target.hosts);
for (const domains of domainGroups) {
  // create temporary file
  const tmpFilename = path.join(
    os.tmpdir(),
    "FUXMERANDM-" + Date.now().toString()
  );

  fs.writeFileSync(tmpFilename, domains.join("\n"), {
    encoding: "utf8",
  });

  // construct commands from raw parts to rewrite the ruleset completely
  const rawCommandParts = [
    XHTTPSE_PATH,
    "--num-threads " + os.cpus().length,
    ruleset.securecookie ? "--securecookie" : null,
    "--name " +
      (domainGroups.length === 1
        ? `'${ruleset.metadata.name}'`
        : `'${domains[0]}'`),
    "-o " +
      (domainGroups.length === 1
        ? `'${filename}'`
        : `'${path.dirname(filename)}/${domains[0]}.xml'`),
    `'${tmpFilename}'`,
  ];

  const command = rawCommandParts.filter((part) => part != null).join(" ");
  child_process.execSync(command);

  // remove the original file if neccessary
  if (domainGroups.length > 1) {
    fs.unlinkSync(filename);
  }

  // remove temporary file
  fs.unlinkSync(tmpFilename);
}
<|endoftext|>


<commit_before>// gulp plugins
var gulp         = require('gulp');
var gutil        = require('gulp-util');
var rename       = require('gulp-rename');
var htmlhint     = require('gulp-htmlhint');
var htmlmin      = require('gulp-htmlmin');
var emailBuilder = require('gulp-email-builder');
var gulpif       = require('gulp-if');
var litmus       = require('gulp-litmus');

// native/npm modules
var fs          = require('fs');
var path        = require('path');
var browserSync = require('browser-sync').create();
var es          = require('event-stream');

// local modules
var args          = require('./lib/args');
var config        = require('./configs/main');
var rewriteRules  = require('./configs/rewriteRules');
var litmusConfig  = require('./configs/litmus');
var sendMail      = require('./lib/sendMail');
var minify        = require('./lib/htmlMinify');
var template      = require('./lib/lodashTemplate');
var appendQuery   = require('./lib/appendQuery');
var modifyHtml    = require('./lib/modifyHtml');
var emailTemplate = require('./lib/emailTemplate');

// command line arguments
var argLitmus   = args.litmus;
var argSendmail = args.mail;
var notInternal = argLitmus || argSendmail;

/**
* Pass through stream to go to next pipe
*
* @returns {Object} a stream
*/

function passThrough(){
  return es.map(function(file, cb){
    cb(null, file);
  });
}

/**
* Get the base directory
*
* @param {Object} file - vinyl file object
*
* @returns {String} the base path
*/

function getBase(file){
  return file.base;
}

/**
* Updates the base to the parent directory of file.path
*
* @returns {Object} a stream that updates the base to the parent directory
*/

function updateBase(){
  return es.map(function(file, cb){
    file.base = path.dirname(file.path);
    cb(null, file);
  });
}

// Build production email
gulp.task('buildEmail', function(){
  gulp.watch(config.fileGlob).on('change', function(evt){

    var email = emailTemplate(evt.path);

    return gulp.src(email.file)
      .pipe(template(email.obj))
      .on('error', gutil.log)
      .pipe(htmlhint(config.hint))
      .pipe(htmlhint.reporter())
      .pipe(emailBuilder({encodeSpecialChars: true}))
      .pipe(appendQuery())
      .pipe(gulpif(notInternal, passThrough(), modifyHtml()))
      .pipe(gulpif(notInternal, passThrough(), minify(config.minify)))
      .pipe(updateBase())
      .pipe(gulpif(notInternal, passThrough(), rename(config.dest)))
      .pipe(gulpif(notInternal, passThrough(), gulp.dest(getBase)))
      .pipe(browserSync.stream())
      .pipe(gulpif(notInternal, passThrough(), gulp.dest(config.testPath)))
      .pipe(gulpif(argLitmus, litmus(litmusConfig)))
      .pipe(gulpif(argSendmail, sendMail()))
      .on('error', gutil.log);

  });
});

// Sync devices and browsers
gulp.task('browserSync', ['buildEmail'], function(){

  //the directory to our working environment
  var base = path.resolve(config.base);

  // init browser-sync
  browserSync.init({
    open: !notInternal,
    rewriteRules: rewriteRules,
    server: {
      baseDir: base,
      index: config.dest,
      directory: true
    }
  });

});

gulp.task('default', ['buildEmail', 'browserSync']);

<commit_msg>adding server task<commit_after>// gulp plugins
var gulp         = require('gulp');
var gutil        = require('gulp-util');
var rename       = require('gulp-rename');
var htmlhint     = require('gulp-htmlhint');
var htmlmin      = require('gulp-htmlmin');
var emailBuilder = require('gulp-email-builder');
var gulpif       = require('gulp-if');
var litmus       = require('gulp-litmus');

// native/npm modules
var fs          = require('fs');
var path        = require('path');
var browserSync = require('browser-sync').create();
var es          = require('event-stream');

// local modules
var args          = require('./lib/args');
var config        = require('./configs/main');
var rewriteRules  = require('./configs/rewriteRules');
var litmusConfig  = require('./configs/litmus');
var sendMail      = require('./lib/sendMail');
var minify        = require('./lib/htmlMinify');
var template      = require('./lib/lodashTemplate');
var appendQuery   = require('./lib/appendQuery');
var modifyHtml    = require('./lib/modifyHtml');
var emailTemplate = require('./lib/emailTemplate');

// command line arguments
var argLitmus   = args.litmus;
var argSendmail = args.mail;
var notInternal = argLitmus || argSendmail;

/**
* Pass through stream to go to next pipe
*
* @returns {Object} a stream
*/

function passThrough(){
  return es.map(function(file, cb){
    cb(null, file);
  });
}

/**
* Get the base directory
*
* @param {Object} file - vinyl file object
*
* @returns {String} the base path
*/

function getBase(file){
  return file.base;
}

/**
* Updates the base to the parent directory of file.path
*
* @returns {Object} a stream that updates the base to the parent directory
*/

function updateBase(){
  return es.map(function(file, cb){
    file.base = path.dirname(file.path);
    cb(null, file);
  });
}

// Build production email
gulp.task('buildEmail', function(){
  gulp.watch(config.fileGlob).on('change', function(evt){

    var email = emailTemplate(evt.path);

    return gulp.src(email.file)
      .pipe(template(email.obj))
      .on('error', gutil.log)
      .pipe(htmlhint(config.hint))
      .pipe(htmlhint.reporter())
      .pipe(emailBuilder({encodeSpecialChars: true}))
      .pipe(appendQuery())
      .pipe(gulpif(notInternal, passThrough(), modifyHtml()))
      .pipe(gulpif(notInternal, passThrough(), minify(config.minify)))
      .pipe(updateBase())
      .pipe(gulpif(notInternal, passThrough(), rename(config.dest)))
      .pipe(gulpif(notInternal, passThrough(), gulp.dest(getBase)))
      .pipe(browserSync.stream())
      .pipe(gulpif(notInternal, passThrough(), gulp.dest(config.testPath)))
      .pipe(gulpif(argLitmus, litmus(litmusConfig)))
      .pipe(gulpif(argSendmail, sendMail()))
      .on('error', gutil.log);

  });
});

// Sync devices and browsers
gulp.task('browserSync', ['buildEmail'], function(){

  //the directory to our working environment
  var base = path.resolve(config.base);

  // init browser-sync
  browserSync.init({
    open: !notInternal,
    rewriteRules: rewriteRules,
    server: {
      baseDir: base,
      index: config.dest,
      directory: true
    }
  });

});

gulp.task('server', function(){

  //the directory to our working environment
  var base = path.resolve(config.base);

  // init browser-sync
  browserSync.init({
    open: !notInternal,
    rewriteRules: rewriteRules,
    server: {
      baseDir: base,
      index: config.dest,
      directory: true
    }
  });
});

gulp.task('default', ['buildEmail', 'browserSync']);

<|endoftext|>


<commit_before>const path = require("path")
const { URL } = require("url")
const test = require("ava")
const webdriver = require("selenium-webdriver")
const { By, until, Key } = webdriver
const moment = require("moment")
const _includes = require("lodash/includes")
const _isRegExp = require("lodash/isRegExp")
const chalk = require("chalk")

let capabilities = {}
const testEnv =
  (process.env.GIT_TAG_NAME && "remote") || process.env.TEST_ENV || "local"
if (testEnv === "local") {
  // This gives us access to send Chrome commands.
  require("chromedriver")
} else {
  // Set capabilities for BrowserStack
  require("./keep-alive.js")
  capabilities = require("../../config/browserstack.config.js")
}

// Webdriver's promise manager only made sense before Node had async/await support.
// Now it's a deprecated legacy feature, so we should use the simpler native Node support instead.
webdriver.promise.USE_PROMISE_MANAGER = false

console.log(
  chalk.bold.cyan(
    `These tests assume that you have recently run ${path.resolve(
      __dirname,
      "..",
      "..",
      "..",
      "insertBaseData-mssql.sql"
    )} on your database instance`
  )
)

function debugLog(...args) {
  if (process.env.DEBUG_LOG === "true") {
    console.log(chalk.grey("[DEBUG]"), ...args)
  }
}

// We use the before hook to put helpers on t.context and set up test scaffolding.
test.beforeEach(t => {
  let builder = new webdriver.Builder()
  if (testEnv === "local") {
    const chrome = require("selenium-webdriver/chrome")
    builder = builder
      .forBrowser("chrome")
      .setChromeOptions(new chrome.Options().headless())
      /*
       * If we don't explicitly define ServiceBuilder for ChromeDriver it uses a default ServiceBuilder
       * which is a singleton, shared amongst different driver instances. As a result, the same
       * ChromeDriver server process is used by different drivers. When driver.quit() is called by
       * one of the drivers, that process is terminated. As a result even though all assertions pass,
       * the afterEach.always hook reports intermittent errors. By explicitly defining a new ServiceBuilder
       * here we enforce the creation of a separate ChromeDriver server child process for each driver instance.
       */
      .setChromeService(new chrome.ServiceBuilder())
  } else {
    capabilities.name = t.title.replace(/^beforeEach hook for /, "")
    builder = builder
      .usingServer("http://hub-cloud.browserstack.com/wd/hub")
      .withCapabilities(capabilities)
  }
  t.context.driver = builder.build()

  const shortWaitMs = moment.duration(1, "seconds").asMilliseconds()
  const mediumWaitMs = moment.duration(3, "seconds").asMilliseconds()
  const longWaitMs = moment.duration(9, "seconds").asMilliseconds()

  t.context.By = By
  t.context.until = until
  t.context.shortWaitMs = shortWaitMs
  t.context.mediumWaitMs = mediumWaitMs
  t.context.longWaitMs = longWaitMs
  t.context.Key = Key

  // This method is a helper so we don't have to keep repeating the hostname.
  // Passing the authentication through the querystring is a hack so we can
  // pass the information along via window.fetch.
  t.context.get = async(pathname, userPw) => {
    const credentials = userPw || "erin"
    const urlToGet = `${process.env.SERVER_URL}${pathname}?user=${credentials}&pass=${credentials}`
    debugLog("Getting URL", urlToGet)
    await t.context.driver.get(urlToGet)
    await t.context.waitForLoadingFinished()

    // If we have a page-wide error message, we would like to cleanly fail the test on that.
    try {
      await t.context.driver.wait(
        until.elementTextIs(
          await t.context.$(".not-found-text", shortWaitMs),
          "There was an error processing this request. Please contact an administrator."
        ),
        shortWaitMs
      )
      throw new Error(
        "The API returned a 500. Do you need to restart the server?"
      )
    } catch (e) {
      // NoSuchElementError: If we didn't find the "not found text" element, then we are not seeing a page-wide failure.
      // StaleElementReferenceError: If we found the "not found text" element but it's no longer on the page,
      //      then we are not seeing a page-wide failure.
      // TimeoutError: If we found the "not found text" element, but the text was never the backend error message,
      //      then we are not seeing a page-wide failure.
      if (
        !_includes(
          ["NoSuchElementError", "StaleElementReferenceError", "TimeoutError"],
          e.name
        )
      ) {
        throw e
      }
    }
  }

  t.context.waitForLoadingFinished = async() => {
    await t.context.assertElementNotPresent(
      t,
      "div.loader",
      "Loading indicator should disappear",
      mediumWaitMs
    )
  }

  // For debugging purposes.
  t.context.waitForever = async() => {
    console.log(chalk.red("Waiting forever so you can debug..."))
    await t.context.driver.wait(() => {})
  }

  t.context.$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find element: $('${cssSelector}')`)
    const waitTimeoutMs = timeoutMs || longWaitMs
    const locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementLocated(locator),
      waitTimeoutMs,
      `Could not find element by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElement(locator)
  }
  t.context.$$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find elements: $$('${cssSelector}')`)
    const waitTimeoutMs = timeoutMs || longWaitMs
    const locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementsLocated(locator),
      waitTimeoutMs,
      `Could not find elements by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElements(locator)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its contents.
  t.context.assertElementText = async(t, $elem, expectedText, message) => {
    try {
      const untilCondition = _isRegExp(expectedText)
        ? until.elementTextMatches($elem, expectedText)
        : until.elementTextIs($elem, expectedText)

      await t.context.driver.wait(untilCondition, longWaitMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not have the text we expected, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    const actualText = (await $elem.getText()).trim()
    if (_isRegExp(expectedText)) {
      t.regex(actualText, expectedText, actualText, message)
    } else {
      t.is(actualText, expectedText, message)
    }
  }

  t.context.assertElementTextIsInt = (t, $elem, message) =>
    t.context.assertElementText(t, $elem, /^\d+$/)

  t.context.assertElementNotPresent = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    const waitTimeoutMs = timeoutMs || longWaitMs
    try {
      await t.context.driver.wait(
        async() => {
          const loopDelay = 250
          try {
            return !(await t.context.$(cssSelector, loopDelay))
          } catch (e) {
            // Hilariously, when Selenium can't find an element, sometimes it throws TimeoutError,
            // and sometimes it throws NoSuchElementError.
            if (e.name === "TimeoutError" || e.name === "NoSuchElementError") {
              return true
            }
            throw e
          }
        },
        waitTimeoutMs,
        `Element was still present after ${waitTimeoutMs} milliseconds`
      )
    } catch (e) {
      if (e.name === "TimeoutError") {
        t.fail(
          `Element with css selector '${cssSelector}' was still present after ${waitTimeoutMs} milliseconds: ${message}`
        )
      } else {
        throw e
      }
    }
    t.pass(message || "Element was not present")
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its enabled status
  t.context.assertElementEnabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    const waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), true, message)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting it's disabled status
  t.context.assertElementDisabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    const waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), false, message)
  }

  t.context.getCurrentPathname = async() => {
    const currentUrl = await t.context.driver.getCurrentUrl()
    return new URL(currentUrl).pathname
  }

  t.context.pageHelpers = {
    async goHomeAndThenToReportsPage() {
      await t.context.get("/")

      const $createButton = await t.context.$("#createButton")
      await $createButton.click()
    },
    async clickTodayButton() {
      const $todayButton = await t.context.driver.findElement(
        By.xpath('//button/span[text()="Today"]')
      )
      await $todayButton.click()
    },
    async clickNextMonthDate() {
      const $nextMonthButton = await t.context.driver.findElement(
        By.xpath('//button/span[icon="chevron-right"]')
      )
      await $nextMonthButton.click()
      const $nextMonthDate = await t.context.driver.findElement(
        By.xpath('//div[class="DayPicker-Day--selected"]')
      )
      await $nextMonthDate.click()
    },
    async chooseAdvancedSelectOption(inputSelector, text) {
      const popoverSelector = `${inputSelector}-popover`
      const $advancedSelectInput = await t.context.$(inputSelector)
      await $advancedSelectInput.click()
      await $advancedSelectInput.sendKeys(text)
      await t.context.driver.sleep(shortWaitMs) // give the advanced select some time to send the request (debounce!)
      t.context.waitForLoadingFinished()
      const $advancedSelectSuggestion = await t.context.$(
        `${popoverSelector} tbody tr:first-child td input`
      )
      await $advancedSelectSuggestion.click()
      return $advancedSelectInput
    },
    async writeInForm(inputSelector, text, delay) {
      const $meetingGoalInput = await t.context.$(inputSelector)
      await $meetingGoalInput.sendKeys(text)
      if (delay) {
        await t.context.driver.sleep(delay) // wait e.g. for Draftail to save the editor contents
      }
    },
    async assertReportShowStatusText(t, text) {
      await t.context.assertElementText(
        t,
        await t.context.$(".report-show h4"),
        text
      )
    },
    async clickMyOrgLink() {
      const $myOrgLink = await t.context.$("#my-organization")
      await t.context.driver.wait(t.context.until.elementIsVisible($myOrgLink))
      await $myOrgLink.click()
      await t.context.waitForLoadingFinished()
    },
    async clickFormBottomSubmit() {
      const $formBottomSubmit = await t.context.$("#formBottomSubmit")
      await t.context.driver.wait(
        t.context.until.elementIsVisible($formBottomSubmit)
      )
      await $formBottomSubmit.click()
    },
    async clickPersonNameFromSupportedPositionsFieldset(personName) {
      const $supportedPositionsRows = await t.context.$$(
        "#supportedPositions table tbody tr"
      )
      for (const $row of $supportedPositionsRows) {
        const [$billetCell, $advisorCell] = await $row.findElements(
          By.css("td")
        )
        await t.context.driver.wait(
          until.elementIsVisible($billetCell),
          mediumWaitMs
        )
        await $billetCell.getText()
        await t.context.driver.wait(
          until.elementIsVisible($advisorCell),
          mediumWaitMs
        )
        const advisorText = await $advisorCell.getText()
        if (advisorText === personName) {
          const $advisorLink = await $advisorCell.findElement(By.css("a"))
          await t.context.driver.wait(
            until.elementIsVisible($advisorLink),
            mediumWaitMs
          )
          await $advisorLink.click()
          await t.context.driver.wait(
            until.stalenessOf($advisorLink),
            mediumWaitMs
          )
          return
        }
      }
      t.fail(
        `Could not find a row with person name = '${personName}'` +
          "in the supported positions table. " +
          "Please fix the database to be the way this test expects."
      )
    }
  }
})

// Shut down the browser when we are done.
test.afterEach.always(async t => {
  if (t.context.driver) {
    await t.context.driver.quit()
  }
})

/**
 * Technically speaking, everything should be wrapped in a wait() block to give
 * the the browser time to run JS to update the page. In practice, this does not
 * always seem to be necessary, since the JS can run very fast. If the tests are flaky,
 * this would be a good thing to investigate.
 *
 * Also, I suspect that we may see a bunch of stale element errors as React replaces
 * DOM nodes. To fix this, we should use a model of passing around CSS selectors instead
 * of element references, and always query for the element at the last possible moment.
 */

module.exports = test
module.exports.debugLog = debugLog
<commit_msg>Send back test result to BrowserStack<commit_after>const path = require("path")
const { URL } = require("url")
const test = require("ava")
const webdriver = require("selenium-webdriver")
const { By, until, Key } = webdriver
const moment = require("moment")
const _includes = require("lodash/includes")
const _isRegExp = require("lodash/isRegExp")
const chalk = require("chalk")
const fetch = require("cross-fetch")

let capabilities = {}
const testEnv =
  (process.env.GIT_TAG_NAME && "remote") || process.env.TEST_ENV || "local"
if (testEnv === "local") {
  // This gives us access to send Chrome commands.
  require("chromedriver")
} else {
  // Set capabilities for BrowserStack
  require("./keep-alive.js")
  capabilities = require("../../config/browserstack.config.js")
}

// Webdriver's promise manager only made sense before Node had async/await support.
// Now it's a deprecated legacy feature, so we should use the simpler native Node support instead.
webdriver.promise.USE_PROMISE_MANAGER = false

console.log(
  chalk.bold.cyan(
    `These tests assume that you have recently run ${path.resolve(
      __dirname,
      "..",
      "..",
      "..",
      "insertBaseData-mssql.sql"
    )} on your database instance`
  )
)

function debugLog(...args) {
  if (process.env.DEBUG_LOG === "true") {
    console.log(chalk.grey("[DEBUG]"), ...args)
  }
}

// We use the before hook to put helpers on t.context and set up test scaffolding.
test.beforeEach(t => {
  let builder = new webdriver.Builder()
  if (testEnv === "local") {
    const chrome = require("selenium-webdriver/chrome")
    builder = builder
      .forBrowser("chrome")
      .setChromeOptions(new chrome.Options().headless())
      /*
       * If we don't explicitly define ServiceBuilder for ChromeDriver it uses a default ServiceBuilder
       * which is a singleton, shared amongst different driver instances. As a result, the same
       * ChromeDriver server process is used by different drivers. When driver.quit() is called by
       * one of the drivers, that process is terminated. As a result even though all assertions pass,
       * the afterEach.always hook reports intermittent errors. By explicitly defining a new ServiceBuilder
       * here we enforce the creation of a separate ChromeDriver server child process for each driver instance.
       */
      .setChromeService(new chrome.ServiceBuilder())
  } else {
    capabilities.name = t.title.replace(/^beforeEach hook for /, "")
    builder = builder
      .usingServer("http://hub-cloud.browserstack.com/wd/hub")
      .withCapabilities(capabilities)
  }
  t.context.driver = builder.build()

  const shortWaitMs = moment.duration(1, "seconds").asMilliseconds()
  const mediumWaitMs = moment.duration(3, "seconds").asMilliseconds()
  const longWaitMs = moment.duration(9, "seconds").asMilliseconds()

  t.context.By = By
  t.context.until = until
  t.context.shortWaitMs = shortWaitMs
  t.context.mediumWaitMs = mediumWaitMs
  t.context.longWaitMs = longWaitMs
  t.context.Key = Key

  // This method is a helper so we don't have to keep repeating the hostname.
  // Passing the authentication through the querystring is a hack so we can
  // pass the information along via window.fetch.
  t.context.get = async(pathname, userPw) => {
    const credentials = userPw || "erin"
    const urlToGet = `${process.env.SERVER_URL}${pathname}?user=${credentials}&pass=${credentials}`
    debugLog("Getting URL", urlToGet)
    await t.context.driver.get(urlToGet)
    await t.context.waitForLoadingFinished()

    // If we have a page-wide error message, we would like to cleanly fail the test on that.
    try {
      await t.context.driver.wait(
        until.elementTextIs(
          await t.context.$(".not-found-text", shortWaitMs),
          "There was an error processing this request. Please contact an administrator."
        ),
        shortWaitMs
      )
      throw new Error(
        "The API returned a 500. Do you need to restart the server?"
      )
    } catch (e) {
      // NoSuchElementError: If we didn't find the "not found text" element, then we are not seeing a page-wide failure.
      // StaleElementReferenceError: If we found the "not found text" element but it's no longer on the page,
      //      then we are not seeing a page-wide failure.
      // TimeoutError: If we found the "not found text" element, but the text was never the backend error message,
      //      then we are not seeing a page-wide failure.
      if (
        !_includes(
          ["NoSuchElementError", "StaleElementReferenceError", "TimeoutError"],
          e.name
        )
      ) {
        throw e
      }
    }
  }

  t.context.waitForLoadingFinished = async() => {
    await t.context.assertElementNotPresent(
      t,
      "div.loader",
      "Loading indicator should disappear",
      mediumWaitMs
    )
  }

  // For debugging purposes.
  t.context.waitForever = async() => {
    console.log(chalk.red("Waiting forever so you can debug..."))
    await t.context.driver.wait(() => {})
  }

  t.context.$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find element: $('${cssSelector}')`)
    const waitTimeoutMs = timeoutMs || longWaitMs
    const locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementLocated(locator),
      waitTimeoutMs,
      `Could not find element by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElement(locator)
  }
  t.context.$$ = async(cssSelector, timeoutMs) => {
    debugLog(`Find elements: $$('${cssSelector}')`)
    const waitTimeoutMs = timeoutMs || longWaitMs
    const locator = By.css(cssSelector)
    await t.context.driver.wait(
      until.elementsLocated(locator),
      waitTimeoutMs,
      `Could not find elements by css selector ${cssSelector} within ${waitTimeoutMs} milliseconds`
    )
    return t.context.driver.findElements(locator)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its contents.
  t.context.assertElementText = async(t, $elem, expectedText, message) => {
    try {
      const untilCondition = _isRegExp(expectedText)
        ? until.elementTextMatches($elem, expectedText)
        : until.elementTextIs($elem, expectedText)

      await t.context.driver.wait(untilCondition, longWaitMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not have the text we expected, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    const actualText = (await $elem.getText()).trim()
    if (_isRegExp(expectedText)) {
      t.regex(actualText, expectedText, actualText, message)
    } else {
      t.is(actualText, expectedText, message)
    }
  }

  t.context.assertElementTextIsInt = (t, $elem, message) =>
    t.context.assertElementText(t, $elem, /^\d+$/)

  t.context.assertElementNotPresent = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    const waitTimeoutMs = timeoutMs || longWaitMs
    try {
      await t.context.driver.wait(
        async() => {
          const loopDelay = 250
          try {
            return !(await t.context.$(cssSelector, loopDelay))
          } catch (e) {
            // Hilariously, when Selenium can't find an element, sometimes it throws TimeoutError,
            // and sometimes it throws NoSuchElementError.
            if (e.name === "TimeoutError" || e.name === "NoSuchElementError") {
              return true
            }
            throw e
          }
        },
        waitTimeoutMs,
        `Element was still present after ${waitTimeoutMs} milliseconds`
      )
    } catch (e) {
      if (e.name === "TimeoutError") {
        t.fail(
          `Element with css selector '${cssSelector}' was still present after ${waitTimeoutMs} milliseconds: ${message}`
        )
      } else {
        throw e
      }
    }
    t.pass(message || "Element was not present")
  }

  // A helper method to combine waiting for an element to have rendered and then asserting on its enabled status
  t.context.assertElementEnabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    const waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), true, message)
  }

  // A helper method to combine waiting for an element to have rendered and then asserting it's disabled status
  t.context.assertElementDisabled = async(
    t,
    cssSelector,
    message,
    timeoutMs
  ) => {
    const waitTimeoutMs = timeoutMs || longWaitMs
    try {
      var elem = await t.context.$(cssSelector, waitTimeoutMs)
    } catch (e) {
      // If we got a TimeoutError because the element did not load, just swallow it here
      // and let the assertion on blow up instead. That will produce a clearer error message.
      if (e.name !== "TimeoutError") {
        throw e
      }
    }
    t.is(await elem.isEnabled(), false, message)
  }

  t.context.getCurrentPathname = async() => {
    const currentUrl = await t.context.driver.getCurrentUrl()
    return new URL(currentUrl).pathname
  }

  t.context.pageHelpers = {
    async goHomeAndThenToReportsPage() {
      await t.context.get("/")

      const $createButton = await t.context.$("#createButton")
      await $createButton.click()
    },
    async clickTodayButton() {
      const $todayButton = await t.context.driver.findElement(
        By.xpath('//button/span[text()="Today"]')
      )
      await $todayButton.click()
    },
    async clickNextMonthDate() {
      const $nextMonthButton = await t.context.driver.findElement(
        By.xpath('//button/span[icon="chevron-right"]')
      )
      await $nextMonthButton.click()
      const $nextMonthDate = await t.context.driver.findElement(
        By.xpath('//div[class="DayPicker-Day--selected"]')
      )
      await $nextMonthDate.click()
    },
    async chooseAdvancedSelectOption(inputSelector, text) {
      const popoverSelector = `${inputSelector}-popover`
      const $advancedSelectInput = await t.context.$(inputSelector)
      await $advancedSelectInput.click()
      await $advancedSelectInput.sendKeys(text)
      await t.context.driver.sleep(shortWaitMs) // give the advanced select some time to send the request (debounce!)
      t.context.waitForLoadingFinished()
      const $advancedSelectSuggestion = await t.context.$(
        `${popoverSelector} tbody tr:first-child td input`
      )
      await $advancedSelectSuggestion.click()
      return $advancedSelectInput
    },
    async writeInForm(inputSelector, text, delay) {
      const $meetingGoalInput = await t.context.$(inputSelector)
      await $meetingGoalInput.sendKeys(text)
      if (delay) {
        await t.context.driver.sleep(delay) // wait e.g. for Draftail to save the editor contents
      }
    },
    async assertReportShowStatusText(t, text) {
      await t.context.assertElementText(
        t,
        await t.context.$(".report-show h4"),
        text
      )
    },
    async clickMyOrgLink() {
      const $myOrgLink = await t.context.$("#my-organization")
      await t.context.driver.wait(t.context.until.elementIsVisible($myOrgLink))
      await $myOrgLink.click()
      await t.context.waitForLoadingFinished()
    },
    async clickFormBottomSubmit() {
      const $formBottomSubmit = await t.context.$("#formBottomSubmit")
      await t.context.driver.wait(
        t.context.until.elementIsVisible($formBottomSubmit)
      )
      await $formBottomSubmit.click()
    },
    async clickPersonNameFromSupportedPositionsFieldset(personName) {
      const $supportedPositionsRows = await t.context.$$(
        "#supportedPositions table tbody tr"
      )
      for (const $row of $supportedPositionsRows) {
        const [$billetCell, $advisorCell] = await $row.findElements(
          By.css("td")
        )
        await t.context.driver.wait(
          until.elementIsVisible($billetCell),
          mediumWaitMs
        )
        await $billetCell.getText()
        await t.context.driver.wait(
          until.elementIsVisible($advisorCell),
          mediumWaitMs
        )
        const advisorText = await $advisorCell.getText()
        if (advisorText === personName) {
          const $advisorLink = await $advisorCell.findElement(By.css("a"))
          await t.context.driver.wait(
            until.elementIsVisible($advisorLink),
            mediumWaitMs
          )
          await $advisorLink.click()
          await t.context.driver.wait(
            until.stalenessOf($advisorLink),
            mediumWaitMs
          )
          return
        }
      }
      t.fail(
        `Could not find a row with person name = '${personName}'` +
          "in the supported positions table. " +
          "Please fix the database to be the way this test expects."
      )
    }
  }
})

// Shut down the browser when we are done.
test.afterEach.always(async t => {
  if (t.context.driver) {
    if (testEnv !== "local") {
      // Send back test result to BrowserStack
      const session = await t.context.driver.getSession()
      const url = `https://api.browserstack.com/automate/sessions/${session.getId()}.json`
      const options = {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
          Authorization:
            "Basic " +
            Buffer.from(
              `${capabilities["browserstack.user"]}:${capabilities["browserstack.key"]}`
            ).toString("base64")
        },
        body: JSON.stringify({
          status: t.passed ? "passed" : "failed"
        })
      }
      await fetch(url, options)
    }

    await t.context.driver.quit()
  }
})

/**
 * Technically speaking, everything should be wrapped in a wait() block to give
 * the the browser time to run JS to update the page. In practice, this does not
 * always seem to be necessary, since the JS can run very fast. If the tests are flaky,
 * this would be a good thing to investigate.
 *
 * Also, I suspect that we may see a bunch of stale element errors as React replaces
 * DOM nodes. To fix this, we should use a model of passing around CSS selectors instead
 * of element references, and always query for the element at the last possible moment.
 */

module.exports = test
module.exports.debugLog = debugLog
<|endoftext|>


<commit_before>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds"<commit_msg>You feeling lucky punk?<commit_after>var child_process = require('child_process')
var fs = require('fs');
var max_sleep = 300
var step = 10
//Added a comment that achieves no real goal.
//Non-blocking loop
//Adjustable step
//Status line
//Add a rough time indicator
//First cut at libfaketime support

var commit_messages = [
  "Fixing an important issue with the universe.",
  "Someone poisoned the waterhole!",
  "You feeling lucky punk?",
  "Five bullets or six?",
  "Fixed memory parsing error #1337",
  "Cleaned out the intertubes",
  "AAAAAAAAAAARGH!",
  "Fixed a typo",
  "Updated the readme",
  "Fixing a typo added in the last readme update",
  "GET A HAIRCUT!",
  "Updated the readme again",
  "Found an issue with the letter 'a', so I fixed it.",
  "Incompatible dimensions found, Cthuhlu Error 666",
  "Who you calling a fool?",
  "How many more of these can I make?",
  "Fixing an error introduced in the last commit",
  "Re-updating the readme.",
  "Work in Progress",
  "WIP",
  "Quick fix",
  "Grammar fix",
  "Fixing punctuation",
  "Correcting style",
  "Logic error",
  "Almost there",
  "Nearly got it",
  "Not quite working",
  "Ooops!",
  "Did I do that?",
  "Commit all the required changes",
  "Really commit all the required changes",
  "Should work now",
  "Back out the last commit",
  "Reverting changes",
  "One more time...",
  "Re-commiting last",
  "One more User Story done",
  "Can work my way through the Kanban",
  "Sprint complete",
  "Well that seemed pretty easy",
  "What do you mean you can't pack up the commit?",
  "Merge in master",
  "Fast-forwarding changes"
]

if ( process.argv[ 2 ] && process.argv[ 3 ] ) {
  var inFile = process.argv[ 2 ]
  var outFile = process.argv[ 3 ]
  if (inFile == outFile) {
    console.error("Aborted: infile and outfile must be different")
    return(-1);
  }
  if (process.argv [ 4 ]) max_sleep = process.argv [ 4 ]
  if (process.argv [ 5 ]) step = parseInt(process.argv [ 5 ])
  console.info("Writing from %s to %s, with up to %s seconds between commits of %s bytes", inFile, outFile, max_sleep, step)
  var outFD = fs.openSync(outFile, 'w')
  fs.readFile(inFile, function(err,data) {
    var length = data.length
    var commit_count = Math.ceil(length / step)
    var commit_time = commit_count * max_sleep / 2
    var commit_time_hour = Math.floor(commit_time / 3600)
    var commit_time_rem = commit_time - (commit_time_hour * 3600)
    var commit_time_min = Math.floor(commit_time_rem / 60)
    var commit_time_sec = commit_time_rem - (commit_time_min * 60)
    console.info("Input file is %s Bytes, this will generate %s commits and take approximately %s hours %s minutes %s seconds",<|endoftext|>


<commit_before>define(["Test", "Tone/core/EventEmitter"], function (Test, EventEmitter) {

	describe("EventEmitter", function(){

		it ("can be created and disposed", function(){
			var emitter = new EventEmitter();
			emitter.dispose();
			Test.wasDisposed(emitter);
		});

		it ("can bind events", function(done){
			var emitter = new EventEmitter();
			emitter.on("something", function(){
				done();
				emitter.dispose();
			});
			emitter.trigger("something");
		});

		it ("can unbind events", function(){
			var emitter = new EventEmitter();
			var callback = function(){
				throw new Error("should call this");
			};
			emitter.on("something", callback);
			emitter.off("something", callback);
			emitter.trigger("something");
			emitter.dispose();
		});

		it ("can pass arguments to the callback", function(done){
			var emitter = new EventEmitter();
			emitter.on("something", function(arg0, arg1){
				expect(arg0).to.equal("A");
				expect(arg1).to.equal("B");
				emitter.dispose();
				done();
			});
			emitter.trigger("something", "A", "B");
		});

		it ("can mixin its methods to another object", function(done){
			var emitter = {};
			EventEmitter.mixin(emitter);
			emitter.on("test", done);
			emitter.trigger("test");
		});
	});
});<commit_msg>testing new `off` feature<commit_after>define(["Test", "Tone/core/EventEmitter"], function (Test, EventEmitter) {

	describe("EventEmitter", function(){

		it ("can be created and disposed", function(){
			var emitter = new EventEmitter();
			emitter.dispose();
			Test.wasDisposed(emitter);
		});

		it ("can bind events", function(done){
			var emitter = new EventEmitter();
			emitter.on("something", function(){
				done();
				emitter.dispose();
			});
			emitter.trigger("something");
		});

		it ("can unbind events", function(){
			var emitter = new EventEmitter();
			var callback = function(){
				throw new Error("should call this");
			};
			emitter.on("something", callback);
			emitter.off("something", callback);
			emitter.trigger("something");
			emitter.dispose();
		});

		it ("removes all events when no callback is given", function(){
			var emitter = new EventEmitter();
			emitter.on("something", function(){
				throw new Error("should call this");
			});
			emitter.on("something", function(){
				throw new Error("should call this");
			});
			emitter.off("something");
			emitter.trigger("something");
			emitter.dispose();
		});

		it ("can pass arguments to the callback", function(done){
			var emitter = new EventEmitter();
			emitter.on("something", function(arg0, arg1){
				expect(arg0).to.equal("A");
				expect(arg1).to.equal("B");
				emitter.dispose();
				done();
			});
			emitter.trigger("something", "A", "B");
		});

		it ("can mixin its methods to another object", function(done){
			var emitter = {};
			EventEmitter.mixin(emitter);
			emitter.on("test", done);
			emitter.trigger("test");
		});
	});
});<|endoftext|>


<commit_before>import { storiesOf } from '@storybook/vue';
import { linkTo } from '@storybook/addon-links';

import StoryRouter from 'storybook-router';

const TargetComponent = {
  template: '
The target component
'
};

storiesOf('Links', module)
  .add('target story', () => TargetComponent);

storiesOf('Links', module)
  .addDecorator(StoryRouter({'/target': linkTo('Links', 'target story')}))
  .add('standard router-link', () => ({
    template: `

        <router-link to="/target">String Link</router-link>
        <router-link v-bind:to="'target'">Javascript link</router-link>

`,
  }))
  .add('button with function', () => ({
    template: '<button v-on:click="goToTargetStory">Button With Function</button>',
    methods: {
      goToTargetStory: function() {
        this.$router.push('/target');
      }
    },
  }));

storiesOf('Links', module)
  .addDecorator(StoryRouter(
    {'/component': linkTo('Links', 'target story')},
    {routes: [
      {
        path: '/component',
        name: 'comp',
        component: TargetComponent
      }
    ]}))
  .add('named route', () => ({
    template: `<router-link :to="{name: 'comp'}">Named link</router-link>`,
  }));

storiesOf('Links', module)
  .addDecorator(StoryRouter(
    {'/target': linkTo('Links', 'target story')},
    {routes: [
      { path: '/anothertarget', redirect: '/target' }
    ]}))
  .add('router-link with redirect', () => ({
    template: `

        <router-link to="/anothertarget">Link to a redirected target</router-link>

`,
  }));
<commit_msg>Add an example of relative links using vue-router<commit_after>import { storiesOf } from '@storybook/vue';
import { linkTo } from '@storybook/addon-links';

import StoryRouter from 'storybook-router';

const TargetComponent = {
  template: '
The target component
'
};

storiesOf('Links', module)
  .add('target story', () => TargetComponent);

storiesOf('Links', module)
  .addDecorator(StoryRouter({'/target': linkTo('Links', 'target story')}))
  .add('standard router-link', () => ({
    template: `

        <router-link to="/target">String Link</router-link>
        <router-link v-bind:to="'target'">Javascript link</router-link>

`,
  }))
  .add('button with function', () => ({
    template: '<button v-on:click="goToTargetStory">Button With Function</button>',
    methods: {
      goToTargetStory: function() {
        this.$router.push('/target');
      }
    },
  }));

storiesOf('Links', module)
  .addDecorator(StoryRouter(
    {'/component': linkTo('Links', 'target story')},
    {routes: [
      {
        path: '/component',
        name: 'comp',
        component: TargetComponent
      }
    ]}))
  .add('named route', () => ({
    template: `<router-link :to="{name: 'comp'}">Named link</router-link>`,
  }));

storiesOf('Links', module)
  .addDecorator(StoryRouter(
    {'/target': linkTo('Links', 'target story')},
    {routes: [
      { path: '/anothertarget', redirect: '/target' }
    ]}))
  .add('router-link with redirect', () => ({
    template: `

        <router-link to="/anothertarget">Link to a redirected target</router-link>

`,
  }));

storiesOf('Links', module)
  .addDecorator(
    StoryRouter({'/base/inner': linkTo('Links', 'target story')}, {initialEntry: '/base'}))
  .add('router-link relative path', () => ({
    template: `<router-link to="inner" append>A relative path Link</router-link>`,
  }));
<|endoftext|>


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

    angular
        .module('homeControl')
        .controller('devicesController', devicesController)
        .factory('devices', ['$http', function($http){
            var o = {
              devices: []
            };

            o.getAll = function() {
                return $http.get('/devices');
            };

            o.create = function(post) {
              return $http.post('/devices', post).success(function(data){
                o.devices.push(data);
              });
            };
            o.delete = function(deviceId) {
              return $http.delete('/devices', {id: deviceId});
            };

            return o;
        }]);

    devicesController.$inject = ['resolvedDevices', 'devices', 'resolvedTimers'];

    function devicesController(resolvedDevices, devices, resolvedTimers) {
        var vm = this;
        vm.list_of_devices = resolvedDevices.data;
        vm.list_of_timers = resolvedTimers.data;
        activate();

        function activate() { }

        vm.addDevice = function () {
          var num_devices = vm.list_of_devices.length + 1
          var device = {name: "NoName Device " + num_devices,
                        timers:null
                        };
          vm.list_of_devices.push(device);

          devices.create(device);
        };

        vm.removeDevice = function (device) {
          var num_devices = vm.list_of_devices.length - 1
          var index = vm.list_of_devices.indexOf(device);
          console.log(device);
          vm.list_of_devices.splice(index, 1);
          devices.delete(device._id);
        };

        vm.saveDevice = function () {
          console.log('Save');
        };
    }
})();<commit_msg>Fixed delete device so that device is removed from database<commit_after>(function () {
    'use strict';

    angular
        .module('homeControl')
        .controller('devicesController', devicesController)
        .factory('devices', ['$http', function($http){
            var o = {
              devices: []
            };

            o.getAll = function() {
                return $http.get('/devices').success(function(data){
                  o.devices = angular.copy(data);
                });
            };

            o.create = function(post) {
              return $http.post('/devices', post).success(function(data){
                o.devices.push(data);
              });
            };

            o.delete = function(device) {
              return $http.delete('/devices/' + device._id).success(function(data){
                var index = o.devices.indexOf(device);
                o.devices.splice(index, 1);
              });
            };

            return o;
        }]);

    devicesController.$inject = ['resolvedDevices', 'devices', 'resolvedTimers'];

    function devicesController(resolvedDevices, devices, resolvedTimers) {
        var vm = this;
        vm.list_of_devices = resolvedDevices.data;
        vm.list_of_timers = resolvedTimers.data;
        activate();

        function activate() { }

        vm.addDevice = function () {
          var num_devices = vm.list_of_devices.length + 1
          var device = {name: "NoName Device " + num_devices,
                        timers:null
                       };
          devices.create(device);
          vm.list_of_devices = devices.devices;
        };

        vm.removeDevice = function (device) {
          var num_devices = vm.list_of_devices.length - 1

          devices.delete(device);
          vm.list_of_devices = devices.devices;
        };

        vm.saveDevice = function () {
          console.log('Save');
        };
    }
})();<|endoftext|>


<commit_before>var tesseract = require('node-tesseract');
var gm = require('gm').subClass({ imageMagick: true });

var obj = {
  convertImageToText : function(imagePath, cb){
    console.log('imagePath', imagePath)
    var current = imagePath.split('../');
    var imgPath = '../'+current[2];
    console.log('imgpath', imgPath)
    //var filteredImg =imgPath.slice(0,imgPath.length-3)+"tiff";
    // console.log('fil', filteredImg)
    var imgFil = gm(imgPath).type('grayscale').enhance().unsharp(6.8, 1.0, 2.69, 0).resize(1200,2000).write(imgPath, function (err) {
     if (err){
      console.log('image processing error', err)
      }
     });

    // sample anonoymous function(stats, text){};
    tesseract.process(imgPath, function(err, text) {
      if(err) {
        cb(err);
      } else {
        var re = /\n/g;
        text = text.replace(re, ' ');
        text = text.trim();
        console.log('//////////////////////');
         console.log('text: ', text);
        cb(null, text);
      }
    });
  },
};

module.exports = obj;
<commit_msg>[Feature] removed console logs<commit_after>var tesseract = require('node-tesseract');
var gm = require('gm').subClass({ imageMagick: true });

var obj = {
  convertImageToText : function(imagePath, cb){
    var current = imagePath.split('../');
    var imgPath = '../'+current[2];
    var imgFil = gm(imgPath).type('grayscale').enhance().unsharp(6.8, 1.0, 2.69, 0).resize(1200,2000).write(imgPath, function (err) {
     if (err){
      console.log('image processing error', err)
      }
     });

    // sample anonoymous function(stats, text){};
    tesseract.process(imgPath, function(err, text) {
      if(err) {
        cb(err);
      } else {
        var re = /\n/g;
        text = text.replace(re, ' ');
        text = text.trim();
        console.log('//////////////////////');
         console.log('text: ', text);
        cb(null, text);
      }
    });
  },
};

module.exports = obj;
<|endoftext|>