<commit_before>/* jshint node: true, expr: true */
/* global describe, before, it */

var should = require('chai').should();

var assert = require('assert');
var bCrypt = require("../bCrypt");
describe('Test Sync', function () {
  this.timeout('100s');
  this.slow('50s');

  var salt1, salt2,
      secret = "super secret",
      hash1, hash2, hash3, hash4, hash5, hash6, hash7, hash8, hash9, hash0, invalidHash,
      pw1, pw2, pw3, pw4, hash_pw1, hash_pw2, hash_pw3, hash_pw4;

  before(function () {
    console.log('generating hashes, this will take a while');
    salt1 = bCrypt.genSaltSync(8);
    should.exist(salt1, 'genSaltSync failed');
    salt2 = bCrypt.genSaltSync(10);
    should.exist(salt2, 'genSaltSync failed');

    hash1 = bCrypt.hashSync('super secret', salt1, null);
    hash2 = bCrypt.hashSync('super secret', salt1, null);
    hash3 = bCrypt.hashSync('supersecret', salt1, null);
    hash4 = bCrypt.hashSync('supersecret', salt1, null);
    hash5 = bCrypt.hashSync('super secret', salt2, null);
    hash6 = bCrypt.hashSync('super secret', salt2, null);
    hash7 = bCrypt.hashSync('supersecret', salt2, null);
    hash8 = bCrypt.hashSync('supersecret', salt2, null);
    hash9 = bCrypt.hashSync('super secret', null, null);
    hash0 = bCrypt.hashSync('super secret', null, null);

    invalidHash = 'some invalid hash that does not equal sixty bytes in length';

    pw1 = '\u6e2f';  // http://www.fileformat.info/info/unicode/char/6e2f/index.htm
    pw2 = '港'; // Character 0x6e2f same as pw1.
    pw3 = '\u6f2f';  // http://www.fileformat.info/info/unicode/char/6f2f/index.htm
    pw4 = '漯'; // Character 0x6f2f same as pw3.

    var salt = '$2a$05$0000000000000000000000';
    hash_pw1 = bCrypt.hashSync(pw1, salt, null);
    hash_pw2 = bCrypt.hashSync(pw2, salt, null);
    hash_pw3 = bCrypt.hashSync(pw3, salt, null);
    hash_pw4 = bCrypt.hashSync(pw4, salt, null);
  });

  it('should match first set of compares', function () {

    assert.ok(bCrypt.compareSync('super secret', hash1), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('super secret', hash2), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('super secret', hash3), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('super secret', hash4), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('super secret', hash5), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('super secret', hash6), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('super secret', hash7), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('super secret', hash8), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('super secret', hash9), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('super secret', hash0), 'compareSync should return true');
  });

  it('should match second set of compares', function () {
    assert.ok(!bCrypt.compareSync('supersecret', hash1), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('supersecret', hash2), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('supersecret', hash3), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('supersecret', hash4), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('supersecret', hash5), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('supersecret', hash6), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('supersecret', hash7), 'compareSync should return true');
    assert.ok(bCrypt.compareSync('supersecret', hash8), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('supersecret', hash9), 'compareSync should return true');
    assert.ok(!bCrypt.compareSync('supersecret', hash0), 'compareSync should return true');
  });

  it('hash_pw2 should compare correctly to pw1', function () {
    assert.ok(bCrypt.compareSync(pw1, hash_pw1), 'compareSync should return true');
  });
  it('hash_pw2 should compare correctly to pw2', function () {
    assert.ok(bCrypt.compareSync(pw2, hash_pw2), 'compareSync should return true');
  });
  it('hash_pw3 should compare correctly to pw3', function () {
    assert.ok(bCrypt.compareSync(pw3, hash_pw3), 'compareSync should return true');
  });
  it('hash_pw4 should compare correctly to pw4', function () {
    assert.ok(bCrypt.compareSync(pw4, hash_pw4), 'compareSync should return true');
  });

  it('hash_pw1 should be different than hash_pw3', function () {
    hash_pw1.should.not.eql(hash_pw3, 'hash_pw1 should be different from hash_pw3');
  });

  it('hash_pw2 should be different than hash_pw4', function () {
    hash_pw2.should.not.eql(hash_pw4, 'hash_pw2 should be different from hash_pw4');
  });
  it('hash_pw1 should be equal hash_pw2', function () {
    hash_pw1.should.eql(hash_pw2, 'hash_pw1 should equal hash_pw2');
  });
  it('hash_pw3 should be equal hash_pw4', function () {
    hash_pw3.should.eql(hash_pw3, 'hash_pw3 should equal hash_pw4');
  });
  it('invalid hash should return false and not throw', function() {
      bCrypt.compareSync('supersecret', invalidHash).should.be.false;
  });
});
<commit_msg>Fix style and assertions<commit_after>/* jshint node: true, expr: true */
/* global describe, before, it */

var should = require('chai').should(),
    bCrypt = require("../bCrypt");

describe('Test Sync', function() {
    this.timeout('100s');
    this.slow('50s');

    var salt1, salt2,
        secret = "super secret",
        hash1, hash2, hash3, hash4, hash5, hash6, hash7, hash8, hash9, hash0, invalidHash,
        pw1, pw2, pw3, pw4, hash_pw1, hash_pw2, hash_pw3, hash_pw4;

    before(function() {
        console.log('generating hashes, this will take a while');
        salt1 = bCrypt.genSaltSync(8);
        should.exist(salt1, 'genSaltSync failed');
        salt2 = bCrypt.genSaltSync(10);
        should.exist(salt2, 'genSaltSync failed');

        hash1 = bCrypt.hashSync('super secret', salt1, null);
        hash2 = bCrypt.hashSync('super secret', salt1, null);
        hash3 = bCrypt.hashSync('supersecret', salt1, null);
        hash4 = bCrypt.hashSync('supersecret', salt1, null);
        hash5 = bCrypt.hashSync('super secret', salt2, null);
        hash6 = bCrypt.hashSync('super secret', salt2, null);
        hash7 = bCrypt.hashSync('supersecret', salt2, null);
        hash8 = bCrypt.hashSync('supersecret', salt2, null);
        hash9 = bCrypt.hashSync('super secret', null, null);
        hash0 = bCrypt.hashSync('super secret', null, null);

        invalidHash = 'some invalid hash that does not equal sixty bytes in length';

        pw1 = '\u6e2f';  // http://www.fileformat.info/info/unicode/char/6e2f/index.htm
        pw2 = '港'; // Character 0x6e2f same as pw1.
        pw3 = '\u6f2f';  // http://www.fileformat.info/info/unicode/char/6f2f/index.htm
        pw4 = '漯'; // Character 0x6f2f same as pw3.

        var salt = '$2a$05$0000000000000000000000';
        hash_pw1 = bCrypt.hashSync(pw1, salt, null);
        hash_pw2 = bCrypt.hashSync(pw2, salt, null);
        hash_pw3 = bCrypt.hashSync(pw3, salt, null);
        hash_pw4 = bCrypt.hashSync(pw4, salt, null);
    });

    it('should match first set of compares', function() {
        bCrypt.compareSync('super secret', hash1).should.be.true;
        bCrypt.compareSync('super secret', hash2).should.be.true;
        bCrypt.compareSync('super secret', hash3).should.be.false;
        bCrypt.compareSync('super secret', hash4).should.be.false;
        bCrypt.compareSync('super secret', hash5).should.be.true;
        bCrypt.compareSync('super secret', hash6).should.be.true;
        bCrypt.compareSync('super secret', hash7).should.be.false;
        bCrypt.compareSync('super secret', hash8).should.be.false;
        bCrypt.compareSync('super secret', hash9).should.be.true;
        bCrypt.compareSync('super secret', hash0).should.be.true;
    });

    it('should match second set of compares', function() {
        bCrypt.compareSync('supersecret', hash1).should.be.false;
        bCrypt.compareSync('supersecret', hash2).should.be.false;
        bCrypt.compareSync('supersecret', hash3).should.be.true;
        bCrypt.compareSync('supersecret', hash4).should.be.true;
        bCrypt.compareSync('supersecret', hash5).should.be.false;
        bCrypt.compareSync('supersecret', hash6).should.be.false;
        bCrypt.compareSync('supersecret', hash7).should.be.true;
        bCrypt.compareSync('supersecret', hash8).should.be.true;
        bCrypt.compareSync('supersecret', hash9).should.be.false;
        bCrypt.compareSync('supersecret', hash0).should.be.false;
    });

    it('hash_pw1 should compare correctly to pw1', function() {
        bCrypt.compareSync(pw1, hash_pw1).should.be.true;
    });
    it('hash_pw2 should compare correctly to pw2', function() {
        bCrypt.compareSync(pw2, hash_pw2).should.be.true;
    });
    it('hash_pw3 should compare correctly to pw3', function() {
        bCrypt.compareSync(pw3, hash_pw3).should.be.true;
    });
    it('hash_pw4 should compare correctly to pw4', function() {
        bCrypt.compareSync(pw4, hash_pw4).should.be.true;
    });

    it('hash_pw1 should be different than hash_pw3', function() {
        hash_pw1.should.not.equal(hash_pw3, 'hash_pw1 should be different from hash_pw3');
    });
    it('hash_pw2 should be different than hash_pw4', function() {
        hash_pw2.should.not.equal(hash_pw4, 'hash_pw2 should be different from hash_pw4');
    });
    it('hash_pw1 should be equal hash_pw2', function() {
        hash_pw1.should.equal(hash_pw2, 'hash_pw1 should equal hash_pw2');
    });
    it('hash_pw3 should be equal hash_pw4', function() {
        hash_pw3.should.equal(hash_pw3, 'hash_pw3 should equal hash_pw4');
    });
    it('invalid hash should return false and not throw', function() {
        bCrypt.compareSync('supersecret', invalidHash).should.be.false;
    });
});
<|endoftext|>


<commit_before>/* @flow */

import React from 'react';
import Helmet from 'react-helmet';
import serialize from 'serialize-javascript';
import _ from 'lodash';

import type { Store } from '../types';

type Props = { store: Store, htmlContent?: string };

const Html = ({ store, htmlContent }: Props) => {
  const head = Helmet.renderStatic();
  const attrs = head.htmlAttributes.toComponent();
  const { lang, ...rest } = attrs || {};
  const assets = webpackIsomorphicTools.assets();

  return (
    <html {...rest} lang={lang || 'en'}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <link rel="apple-touch-icon" href="apple-touch-icon.png" />
        <link rel="shortcut icon" href="/favicon.ico" />

        {head.title.toComponent()}
        {head.base.toComponent()}
        {head.meta.toComponent()}
        {head.link.toComponent()}

        {/* Styles will be presented in production with webpack extract text plugin */}
        {_.keys(assets.styles).map(style =>
          <link
            key={_.uniqueId()}
            href={assets.styles[style]}
            media="screen, projection"
            rel="stylesheet"
            type="text/css"
          />,
        )}
        {/* Styles will be presented in development mode */}
        {/* I put all of the styles here to smoothen the flick */}
        {
          _.keys(assets.styles).length === 0 ?
            <style
              // eslint-disable-next-line react/no-danger
              dangerouslySetInnerHTML={{ __html:
                // $FlowFixMe: It's not an issue
                require('../theme/normalize.css')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/App/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/Home/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/UserInfo/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/NotFound/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../components/UserList/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../components/UserCard/styles.scss')._style,
              }}
            />
            : null
        }
      </head>
      <body>
        <div
          id="react-view"
          // Rendering the route, which passed from server-side
          // eslint-disable-next-line react/no-danger
          dangerouslySetInnerHTML={{ __html: htmlContent || '' }}
        />

        <script
          // Store the initial state into window
          // eslint-disable-next-line react/no-danger
          dangerouslySetInnerHTML={{
            __html: store && `window.__INITIAL_STATE__=${serialize(store.getState())};`,
          }}
        />
        {
          // Reverse the order of scripts for accessing vendor.js first
          _.keys(assets.javascript).reverse().map(script =>
            <script key={_.uniqueId()} src={assets.javascript[script]} />,
          )
        }
        {head.script.toComponent()}
      </body>
    </html>
  );
};

Html.defaultProps = { htmlContent: '' };

export default Html;
<commit_msg>update<commit_after>/* @flow */

import React from 'react';
import Helmet from 'react-helmet';
import serialize from 'serialize-javascript';
import _ from 'lodash';

import type { Store } from '../types';

type Props = { store: Store, htmlContent?: string };

const Html = ({ store, htmlContent }: Props) => {
  // Should be declared after "renderToStaticMarkup()" of "../server.js" or it won't work
  const head = Helmet.renderStatic();
  const attrs = head.htmlAttributes.toComponent();
  const { lang, ...rest } = attrs || {};
  const assets = webpackIsomorphicTools.assets();

  return (
    <html {...rest} lang={lang || 'en'}>
      <head>
        <meta charSet="utf-8" />
        <meta httpEquiv="x-ua-compatible" content="ie=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
        <link rel="apple-touch-icon" href="apple-touch-icon.png" />
        <link rel="shortcut icon" href="/favicon.ico" />

        {head.title.toComponent()}
        {head.base.toComponent()}
        {head.meta.toComponent()}
        {head.link.toComponent()}

        {/* Styles will be presented in production with webpack extract text plugin */}
        {_.keys(assets.styles).map(style =>
          <link
            key={_.uniqueId()}
            href={assets.styles[style]}
            media="screen, projection"
            rel="stylesheet"
            type="text/css"
          />,
        )}
        {/* Styles will be presented in development mode */}
        {/* I put all of the styles here to smoothen the flick */}
        {
          _.keys(assets.styles).length === 0 ?
            <style
              // eslint-disable-next-line react/no-danger
              dangerouslySetInnerHTML={{ __html:
                // $FlowFixMe: It's not an issue
                require('../theme/normalize.css')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/App/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/Home/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/UserInfo/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../containers/NotFound/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../components/UserList/styles.scss')._style +
                // $FlowFixMe: It's not an issue
                require('../components/UserCard/styles.scss')._style,
              }}
            />
            : null
        }
      </head>
      <body>
        <div
          id="react-view"
          // Rendering the route, which passed from server-side
          // eslint-disable-next-line react/no-danger
          dangerouslySetInnerHTML={{ __html: htmlContent || '' }}
        />

        <script
          // Store the initial state into window
          // eslint-disable-next-line react/no-danger
          dangerouslySetInnerHTML={{
            __html: store && `window.__INITIAL_STATE__=${serialize(store.getState())};`,
          }}
        />
        {
          // Reverse the order of scripts for accessing vendor.js first
          _.keys(assets.javascript).reverse().map(script =>
            <script key={_.uniqueId()} src={assets.javascript[script]} />,
          )
        }
        {head.script.toComponent()}
      </body>
    </html>
  );
};

Html.defaultProps = { htmlContent: '' };

export default Html;
<|endoftext|>


<commit_before>var assert = require('assert');
var Tape = require('../src/tape');

describe('Tape', function () {

    var test = 'AAAA   \t\t  \n\r\t_gkRg96sd4#4123';
    var tape;

    beforeEach(function() {
        tape = new Tape(test);
    });

    it('is initialized to the first character', function () {
        assert.equal(tape.peek(), test.charAt(0));
    });

    it('moves to the next character', function () {
        assert.equal(tape.next(), test.charAt(0));
        assert.equal(tape.peek(), test.charAt(1));
        assert.equal(tape.pos, 1);
    });

    it('also moves back to the previous character', function () {
        tape.next();
        assert.equal(tape.prev(), test.charAt(1));
        assert.equal(tape.peek(), test.charAt(0));
        assert.equal(tape.pos, 0);
    });

    it('skips white-space', function () {
        tape.pos = 4;
        tape.skipWS();
        assert.equal(tape.pos, 14);
    });

    it('skips non-white-space', function () {
        tape.skipNonWS();
        assert.equal(tape.pos, 4);
    });

    it('skips to one of the characters in string', function () {
        tape.pos = 4;
        tape.skipTo('#abcABC_0123456789');
        assert.equal(tape.pos, 14);
    });

    it('skips to one of the characters matched by regEx', function () {
        tape.skipTo(/[0-9]/);
        assert.equal(tape.pos, 19);
    });

    it('skips to character matched by filter function', function () {
        tape.skipTo(function (ch) {
            return ch === '_';
        });
        assert.equal(tape.pos, 14);
    });

    it('keeps track of single mark', function () {
        tape.pos = 19;
        tape.setMark();
        tape.next();
        tape.next();
        var marked = tape.getMarked();
        assert.equal(marked, '96');
    });

    it('remembers multiple marks', function () {
        tape.pos = 3;
        tape.setMark();
        tape.pushMark();
        for (i = 0 ; i < 11 ; i++) {
            tape.next();
        }
        tape.setMark();
        for (i = 0 ; i < 3 ; i++) {
            tape.next();
        }
        var marked = tape.getMarked();
        assert.equal(marked, test.substring(14, 17));
        tape.popMark();
        marked = tape.getMarked();
        assert.equal(marked, test.substring(3, 17));
    });

    it('also supports arrays', function (){
        var arr = [{
            name: 'Cave',
            id: 1
        }, {
            name: 'Caroline',
            id: 2
        }, {
            name: 'Chell',
            id: 1498
        }, {
            name: 'Ratman',
            id: -1
        }, {
            name: 'GLaDOS',
            id: 2
        }];

        tape = new Tape(arr);

        tape.skipTo(function (char) {
            return char.id > 10;
        });

        tape.setMark();

        assert.equal(tape.pos, 2);
        tape.next();
        tape.next();
        assert.equal(tape.pos, 4);

        var marked = tape.getMarked();

        assert.equal(marked.length, 2);
        assert.equal(marked[0].name, 'Chell');
        assert.equal(marked[1].name, 'Ratman');
        assert.equal(tape.hasMore(), true);
        assert.equal(tape.pos, 4);
    });

});
<commit_msg>Added yet more tests to Tape.<commit_after>var assert = require('assert');
var Tape = require('../src/tape');

describe('Tape', function () {

    var test = 'AAAA   \t\t  \n\r\t_gkRg96sd4#4123';
    var tape;

    beforeEach(function() {
        tape = new Tape(test);
    });

    it('is initialized to the first character', function () {
        assert.equal(tape.peek(), test.charAt(0));
    });

    it('moves to the next character', function () {
        assert.equal(tape.next(), test.charAt(0));
        assert.equal(tape.peek(), test.charAt(1));
        assert.equal(tape.pos, 1);
    });

    it('also moves back to the previous character', function () {
        tape.next();
        assert.equal(tape.prev(), test.charAt(1));
        assert.equal(tape.peek(), test.charAt(0));
        assert.equal(tape.pos, 0);
    });

    it('skips white-space', function () {
        tape.pos = 4;
        tape.skipWS();
        assert.equal(tape.pos, 14);
    });

    it('skips non-white-space', function () {
        tape.skipNonWS();
        assert.equal(tape.pos, 4);
    });

    it('skips to one of the characters in string', function () {
        tape.pos = 4;
        tape.skipTo('#abcABC_0123456789');
        assert.equal(tape.pos, 14);
    });

    it('skips to one of the characters matched by regEx', function () {
        tape.skipTo(/[0-9]/);
        assert.equal(tape.pos, 19);
    });

    it('skips to character matched by filter function', function () {
        tape.skipTo(function (ch) {
            return ch === '_';
        });
        assert.equal(tape.pos, 14);
    });

    it('keeps track of single mark', function () {
        tape.pos = 19;
        tape.setMark();
        tape.next();
        tape.next();
        var marked = tape.getMarked();
        assert.equal(marked, '96');
    });

    it('remembers multiple marks', function () {
        tape.pos = 3;
        tape.setMark();
        tape.pushMark();
        for (i = 0 ; i < 11 ; i++) {
            tape.next();
        }
        tape.setMark();
        for (i = 0 ; i < 3 ; i++) {
            tape.next();
        }
        var marked = tape.getMarked();
        assert.equal(marked, test.substring(14, 17));
        tape.popMark();
        marked = tape.getMarked();
        assert.equal(marked, test.substring(3, 17));
    });

    it('also supports arrays of objects', function (){
        var arr = [{
            name: 'Cave',
            id: 1
        }, {
            name: 'Caroline',
            id: 2
        }, {
            name: 'Chell',
            id: 1498
        }, {
            name: 'Ratman',
            id: -1
        }, {
            name: 'GLaDOS',
            id: 2
        }];

        tape = new Tape(arr);

        tape.skipTo(function (char) {
            return char.id > 10;
        });

        tape.setMark();

        assert.equal(tape.pos, 2);
        tape.next();
        tape.next();
        assert.equal(tape.pos, 4);

        var marked = tape.getMarked();

        assert.equal(marked.length, 2);
        assert.equal(marked[0].name, 'Chell');
        assert.equal(marked[1].name, 'Ratman');
        assert.equal(tape.hasMore(), true);
        assert.equal(tape.pos, 4);
    });

    it('casts objects to string when doing string operations', function () {
        var arr = [
            { toString: function () { return '';} },
            { toString: function () { return '   ';} },
            { toString: function () { return '\t';} },
            { toString: function () { return '\r\n';} },
            { toString: function () { return 'aa';} },
            { toString: function () { return 'aaa';} },
            { toString: function () { return ' ';} },
            { toString: function () { return 'bbb';} },
            { toString: function () { return 'bb';} },
            { toString: function () { return 'cccc';} }
        ];

        tape = new Tape(arr);

        tape.skipWS();
        tape.pos.should.be.exactly(4);
        tape.skipNonWS();
        tape.pos.should.be.exactly(6);
        tape.next();
        tape.skipTo(/c+/);
        tape.pos.should.be.exactly(9);
    });

});
<|endoftext|>


<commit_before>
/**
 * Module dependencies
 */

var sinon        = require('sinon')
  , _            = require('underscore')
  , path         = require('path')
  , expect       = require('chai').expect
  , proxyquire   = require('proxyquire')
  , jsonFixtures = require('./fixtures/json')
  , fixtures     = require('./fixtures/update');

var File = require('../lib/file').File;

module.exports = function() {
  describe('File', function() {
    describe('readTranslations', function() {
      it('should be able to return a translation object', function() {
        var localesFolder = cf.localesFolder;
        var locales = ['en-US'];
        var globStub = {
          sync : function(path, opts) {
            if(/\.locale/.test(path)
            && opts.cwd === localesFolder) {
              return locales;
            }
          }
        };
        var fsStub = {
          readFileSync : sinon.stub().returns(jsonFixtures.basicTranslationItemString)
        };
        var pathStub = {
          join : function() {
            return 'en-US.locale';
          }
        };
        var File = proxyquire('../lib/file', { glob : globStub, fs : fsStub, path : pathStub }).File
        var file = new File();
        file.localesFolder = cf.localesFolder;
        file.locales = locales;
        var translations = file.readTranslations();
        expect(translations).to.have.property(locales[0]);
      });
    });
  });
};

<commit_msg>Added folder creation test for File#writeTranslations<commit_after>
/**
 * Module dependencies
 */

var sinon        = require('sinon')
  , _            = require('underscore')
  , path         = require('path')
  , expect       = require('chai').expect
  , proxyquire   = require('proxyquire')
  , jsonFixtures = require('./fixtures/json')
  , fixtures     = require('./fixtures/update');

var File = require('../lib/file').File;

module.exports = function() {
  describe('File', function() {
    describe('#readTranslations', function() {
      it('should be able to return a translation object', function() {
        var localesFolder = cf.localesFolder;
        var locales = ['en-US'];
        var globStub = {
          sync : function(path, opts) {
            if(/\.locale/.test(path)
            && opts.cwd === localesFolder) {
              return locales;
            }
          }
        };
        var fsStub = {
          readFileSync : sinon.stub().returns(jsonFixtures.basicTranslationItemString)
        };
        var pathStub = {
          join : function() {
            return 'en-US.locale';
          }
        };
        var File = proxyquire('../lib/file', { glob : globStub, fs : fsStub, path : pathStub }).File
        var file = new File();
        file.localesFolder = cf.localesFolder;
        file.locales = locales;
        var translations = file.readTranslations();
        expect(translations).to.have.property(locales[0]);
      });
    });

    describe('#writeTranslations', function() {
      it('should not make a folder for locales storage if it does exists', function() {
        var localesFolder = 'test-folder';
        var fsStub = {
          existsSync : sinon.stub().withArgs(localesFolder).returns(true),
          mkdirSync : sinon.spy()
        };
        var File = proxyquire('../lib/file', { fs : fsStub }).File;
        var file = new File();
        file.localesFolder = localesFolder;
        file.writeTranslations();
        expect(fsStub.mkdirSync.calledOnce).to.be.false;
      });

      it('should make a folder for locales storage if it does not exists', function() {
        var localesFolder = 'test-folder';
        var fsStub = {
          existsSync : sinon.stub().withArgs(localesFolder).returns(false),
          mkdirSync : sinon.spy()
        };
        var File = proxyquire('../lib/file', { fs : fsStub }).File;
        var file = new File();
        file.localesFolder = localesFolder;
        file.writeTranslations();
        expect(fsStub.mkdirSync.calledOnce).to.be.true;
        expect(fsStub.mkdirSync.args[0][0]).to.be.equal(localesFolder);
      });

    });
  });
};

<|endoftext|>


<commit_before>var allTestFiles = [];
for (var file in window.__karma__.files) {
  if (window.__karma__.files.hasOwnProperty(file)) {
    if (/Spec\.js$/.test(file)) {
       allTestFiles.push(file);
    }
  }
}

require.config({
  // Karma serves files under /base, which is the basePath from your config file
  baseUrl: '/base/dist/javascript',

  // dynamically load all test files
  deps: allTestFiles,

  // we have to kickoff jasmine, as it is asynchronous
  callback: window.__karma__.start,

  paths: {
      'angular':            'vendor/angular',
      'angular-bing-maps':  'vendor/angular-bing-maps',
      'angular-chart':      'vendor/angular-chart',
      'angular-route':      'vendor/angular-route',
      'angular-sanitize':   'vendor/angular-sanitize',
      'angularjs-viewhead': 'vendor/angularjs-viewhead',
      'autosize':           'vendor/autosize.min',
      'canvasRenderer':     'vendor/CanvasRenderer',
      'chart':              'vendor/Chart',
      'jquery':             'vendor/jquery',
      'i18next':            'vendor/i18next',
      'mapcontrol':         '//ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0',
      'ng-i18next':         'vendor/ng-i18next',
      'projector':          'vendor/Projector',
      'three':              'vendor/three.min',
      'home':               'controller/home'
  },
  shim: {
      'angular': {
          exports: 'angular'
      },
      'angular-bing-maps':  ['angular', 'mapcontrol'],
      'angular-chart':      ['chart', 'angular'],
      'angular-route':      ['angular'],
      'angular-sanitize':   ['angular'],
      'angularjs-viewhead': ['angular'],
      'autosize':           ['jquery'],
      'canvasRenderer':     ['three'],
      'i18next':            ['jquery'],
      'app':                ['angular-bing-maps', 'angular-chart', 'angular-route', 'angular-sanitize', 'angularjs-viewhead', 'ng-i18next'],
      'ng-i18next':         ['angular', 'i18next'],
      'projector':          ['three']
  }
});
<commit_msg>Remove "home" path for require.js.<commit_after>var allTestFiles = [];
for (var file in window.__karma__.files) {
  if (window.__karma__.files.hasOwnProperty(file)) {
    if (/Spec\.js$/.test(file)) {
       allTestFiles.push(file);
    }
  }
}

require.config({
  // Karma serves files under /base, which is the basePath from your config file
  baseUrl: '/base/dist/javascript',

  // dynamically load all test files
  deps: allTestFiles,

  // we have to kickoff jasmine, as it is asynchronous
  callback: window.__karma__.start,

  paths: {
      'angular':            'vendor/angular',
      'angular-bing-maps':  'vendor/angular-bing-maps',
      'angular-chart':      'vendor/angular-chart',
      'angular-route':      'vendor/angular-route',
      'angular-sanitize':   'vendor/angular-sanitize',
      'angularjs-viewhead': 'vendor/angularjs-viewhead',
      'autosize':           'vendor/autosize.min',
      'canvasRenderer':     'vendor/CanvasRenderer',
      'chart':              'vendor/Chart',
      'jquery':             'vendor/jquery',
      'i18next':            'vendor/i18next',
      'mapcontrol':         '//ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0',
      'ng-i18next':         'vendor/ng-i18next',
      'projector':          'vendor/Projector',
      'three':              'vendor/three.min'
  },
  shim: {
      'angular': {
          exports: 'angular'
      },
      'angular-bing-maps':  ['angular', 'mapcontrol'],
      'angular-chart':      ['chart', 'angular'],
      'angular-route':      ['angular'],
      'angular-sanitize':   ['angular'],
      'angularjs-viewhead': ['angular'],
      'autosize':           ['jquery'],
      'canvasRenderer':     ['three'],
      'i18next':            ['jquery'],
      'app':                ['angular-bing-maps', 'angular-chart', 'angular-route', 'angular-sanitize', 'angularjs-viewhead', 'ng-i18next'],
      'ng-i18next':         ['angular', 'i18next'],
      'projector':          ['three']
  }
});
<|endoftext|>


<commit_before>define(['Chance', 'mocha', 'chai', 'underscore'], function (Chance, mocha, chai, _) {
    var expect = chai.expect;

    describe("Dice", function () {
        var die, chance = new Chance();

        it("returns a properly bounded d4", function () {
            _(1000).times(function () {
                die = chance.d4();
                expect(die).to.be.within(1, 4);
            });
        });

        it("returns a properly bounded d6", function () {
            _(1000).times(function () {
                die = chance.d6();
                expect(die).to.be.within(1, 6);
            });
        });

        it("returns a properly bounded d8", function () {
            _(1000).times(function () {
                die = chance.d8();
                expect(die).to.be.within(1, 8);
            });
        });

        it("returns a properly bounded d10", function () {
            _(1000).times(function () {
                die = chance.d10();
                expect(die).to.be.within(1, 10);
            });
        });

        it("returns a properly bounded d12", function () {
            _(1000).times(function () {
                die = chance.d12();
                expect(die).to.be.within(1, 12);
            });
        });

        it("returns a properly bounded d20", function () {
            _(1000).times(function () {
                die = chance.d20();
                expect(die).to.be.within(1, 20);
            });
        });

        it("returns a properly bounded d100", function () {
            _(1000).times(function () {
                die = chance.d100();
                expect(die).to.be.within(1, 100);
            });
        });
    });

    describe("Guid", function () {
        var guid, chance = new Chance();

        it("returns a proper guid", function () {
            _(1000).times(function () {
                guid = chance.guid();
                expect(guid).to.match(/([0-9a-fA-F]){8}(-([0-9a-fA-F]){4}){3}-([0-9a-fA-F]){12}/);
            });
        });
    });
});
<commit_msg>Add test for d30(), rpg().<commit_after>define(['Chance', 'mocha', 'chai', 'underscore'], function (Chance, mocha, chai, _) {
    var expect = chai.expect;

    describe("Dice", function () {
        var die, dice, chance = new Chance();

        it("returns a properly bounded d4", function () {
            _(1000).times(function () {
                die = chance.d4();
                expect(die).to.be.within(1, 4);
            });
        });

        it("returns a properly bounded d6", function () {
            _(1000).times(function () {
                die = chance.d6();
                expect(die).to.be.within(1, 6);
            });
        });

        it("returns a properly bounded d8", function () {
            _(1000).times(function () {
                die = chance.d8();
                expect(die).to.be.within(1, 8);
            });
        });

        it("returns a properly bounded d10", function () {
            _(1000).times(function () {
                die = chance.d10();
                expect(die).to.be.within(1, 10);
            });
        });

        it("returns a properly bounded d12", function () {
            _(1000).times(function () {
                die = chance.d12();
                expect(die).to.be.within(1, 12);
            });
        });

        it("returns a properly bounded d20", function () {
            _(1000).times(function () {
                die = chance.d20();
                expect(die).to.be.within(1, 20);
            });
        });

        it("returns a properly bounded d30", function () {
            _(1000).times(function () {
                die = chance.d30();
                expect(die).to.be.within(1, 30);
            });
        });

        it("returns a properly bounded d100", function () {
            _(1000).times(function () {
                die = chance.d100();
                expect(die).to.be.within(1, 100);
            });
        });

        it("rpg() appears to work as expected", function () {
            _(1000).times(function () {
                expect(function () { chance.rpg(); }).to.throw(Error);
                dice = chance.rpg('5d20');
                expect(dice).to.be.an.array;
                expect(dice).to.have.length(5);
                _.map(dice, function (die) {
                    expect(die).to.be.within(1, 20);
                });
            });
        });
    });

    describe("Guid", function () {
        var guid, chance = new Chance();

        it("returns a proper guid", function () {
            _(1000).times(function () {
                guid = chance.guid();
                expect(guid).to.match(/([0-9a-fA-F]){8}(-([0-9a-fA-F]){4}){3}-([0-9a-fA-F]){12}/);
            });
        });
    });
});
<|endoftext|>


<commit_before>// @flow
import { css } from "styled-components";
import { flexCSS } from "../constants";

const sizes = {
  sm: 500,
  md: 768,
  lg: 1100
};

export const align = (rule: string, flow: string = "horizontal") =>
  flow === "vertical" ? verticalAlign(rule) : horizontalAlign(rule);

// const flexRule = cssAlignRules[flow];
// const alignRule = alignRules[rule];
// return `${flexRule}: ${alignRule};`;

export const verticalAlign = (rule: string, direction: string) =>
  `${flexCSS.vAlign.directions[direction]}: ${flexCSS.vAlign.values[rule]};`;

export const horizontalAlign = (rule: string, direction: string) =>
  `${flexCSS.hAlign.directions[direction]}: ${flexCSS.hAlign.values[rule]};`;
<commit_msg>fix align function<commit_after>// @flow
import { css } from "styled-components";
import { flexCSS } from "../constants";

const sizes = {
  sm: 500,
  md: 768,
  lg: 1100
};

export const align = (rule: string, flow: string = "horizontal") =>
  flow === "vertical" ? verticalAlign(rule, flow) : horizontalAlign(rule, flow);

// const flexRule = cssAlignRules[flow];
// const alignRule = alignRules[rule];
// return `${flexRule}: ${alignRule};`;

export const verticalAlign = (rule: string, direction: string) =>
  `${flexCSS.vAlign.directions[direction]}: ${flexCSS.vAlign.values[rule]};`;

export const horizontalAlign = (rule: string, direction: string) =>
  `${flexCSS.hAlign.directions[direction]}: ${flexCSS.hAlign.values[rule]};`;
<|endoftext|>


<commit_before>/* @flow strict-local */
import { Alert } from 'react-native';
import Toast from '@remobile/react-native-toast';

export const showToast = (message: string) => {
  Toast.show(message, Toast.SHORT);
};

export const showErrorAlert = (message: string, title: string) =>
  Alert.alert(title, message, [{ text: 'OK', onPress: () => {} }], { cancelable: true });
<commit_msg>toast: Drop ignored second argument to `Toast.show`.<commit_after>/* @flow strict-local */
import { Alert } from 'react-native';
import Toast from '@remobile/react-native-toast';

export const showToast = (message: string) => {
  Toast.show(message);
};

export const showErrorAlert = (message: string, title: string) =>
  Alert.alert(title, message, [{ text: 'OK', onPress: () => {} }], { cancelable: true });
<|endoftext|>


<commit_before>import moment from 'moment-timezone';
import { head, last } from './func';

// loads moment-timezone's timezone data, which comes from the
// IANA Time Zone Database at https://www.iana.org/time-zones
moment.tz.load({version: 'latest', zones: [], links: []});

/**
 * Create a time data object using moment.
 * If a time is provided, just format it; if not, use the current time.
 *
 * @function getValidTimeData
 * @param  {string} time          a time; defaults to now
 * @param  {string} meridiem      AM or PM; defaults to AM via moment
 * @param  {Number} timeMode      12 or 24-hour mode
 * @param  {string} tz            a timezone name; guesses user timezone by default
 * @return {Object}               a key-value representation of time data
 */
const getValidTimeData = (time, meridiem, timeMode, tz = guessUserTz()) => {
  const validMeridiem = getValidMeridiem(meridiem);

  // when we only have a valid meridiem, that implies a 12h mode
  const mode = (validMeridiem && !timeMode) ? 12 : timeMode || 24;

  const validMode = getValidateTimeMode(mode);
  const validTime = getValidTimeString(time, validMeridiem);

  const time24 = ((validTime)
    ? moment(`1970-01-01 ${validTime}`).tz(tz.zoneName).format('HH:mmA')
    : moment().tz(tz.zoneName).format('HH:mmA')).split(/:/);

  const time12 = ((validTime)
    ? moment(`1970-01-01 ${validTime}`).tz(tz.zoneName).format('hh:mmA')
    : moment().tz(tz.zoneName).format('hh:mmA')).split(/:/);

  const timeData = {
    hour12: head(time12).replace(/^0/, ''),
    hour24: head(time24),
    minute: last(time24).slice(0, 2),
    meridiem: last(time12).slice(2),
    mode: validMode,
    timezone: tz
  };

  return timeData;
};

/**
 * Format the current time as a string
 * @function getCurrentTime
 * @return {[type]} [description]
 */
const getCurrentTime = () => {
  const time = getValidTimeData();
  return `${time.hour24}:${time.minute}`;
};

/**
 * Get an integer representation of a time.
 * @function getValidateIntTime
 * @param  {string} time
 * @return {Number}
 */
const getValidateIntTime = (time) => {
  if (isNaN(parseInt(time))) { return 0; }
  return parseInt(time);
};

/**
 * Validate, set a default for, and stringify time data.
 * @function getValidateTime
 * @type {string}
 * @return {string}
 */
const getValidateTime = (time) => {
  if (typeof time === 'undefined') { time = '00'; }
  if (isNaN(parseInt(time))) { time = '00'; }
  if (parseInt(time) < 10) { time = `0${parseInt(time)}`; }
  return `${time}`;
};

/**
 * Given a time and meridiem, produce a time string to pass to moment
 * @function getValidTimeString
 * @param  {[type]} time     [description]
 * @param  {[type]} meridiem [description]
 * @return {[type]}          [description]
 */
const getValidTimeString = (time, meridiem) => {
  if (typeof time === 'string') {
    let validTime = (time && time.includes(':'))
      ? time.split(/:/).map((t) => getValidateTime(t)).join(':')
      : time;

    validTime = (validTime && meridiem && parseInt(head(validTime.split(/:/))) < 12)
      ? `${validTime} ${meridiem}`
      : validTime;

    return validTime;
  }

  return time;
};

/**
 * Get time data to use for initializing a TimePicker.
 * @function initialTime
 * @param  {string} defaultTime
 * @param  {Number} mode=24
 * @return {[string, string, string]}
 */
const initialTime = (defaultTime, mode = 24) => {
  let [hour, minute, meridiem] = getValidTimeData(defaultTime, mode);

  hour = getValidateIntTime(hour);
  minute = getValidateIntTime(minute);

  if (hour > 24) { hour = 24 - hour; }
  if (minute >= 60) { minute = 60 - minute; }

  hour = getValidateTime(hour);
  minute = getValidateTime(minute);

  return [hour, minute, meridiem];
};

const getValidMeridiem = (meridiem) => {
  if (typeof meridiem === 'string') {
    return (meridiem && meridiem.match(/[am|pm]/i)) ? meridiem : null;
  }

  return meridiem;
};

/**
 * Validate and set a sensible default for time modes.
 * @function getValidateTimeMode
 * @param  {string|Number} timeMode
 * @return {Number}
 */
const getValidateTimeMode = (timeMode) => {
  let mode = parseInt(timeMode);

  if (isNaN(mode)) { return 24; }
  if (mode !== 24 && mode !== 12) { return 24; }

  return mode;
};

const tzNames = (() => {
  //  We want to subset the existing timezone data as much as possible, both for efficiency
  //  and to avoid confusing the user. Here, we focus on removing reduntant timezone names
  //  and timezone names for timezones we don't necessarily care about, like Antarctica, and
  //  special timezone names that exist for convenience.
  const scrubbedPrefixes = ['Antarctica', 'Arctic', 'Chile', 'Etc'];
  const scrubbedSuffixes = ['ACT', 'East', 'Knox_IN', 'LHI', 'North', 'NSW', 'South', 'West'];

  const tzNames = moment.tz.names()
      .filter(name => name.indexOf('/') !== -1)
      .filter(name => !scrubbedPrefixes.includes(name.split('/')[0]))
      .filter(name => !scrubbedSuffixes.includes(name.split('/').slice(-1)[0]));

  return tzNames;
})();

// We need a human-friendly city name for each timezone identifier
// counting Canada/*, Mexico/*, and US/* allows users to search for
// things like 'Eastern' or 'Mountain' and get matches back
const tzCities = tzNames
    .map(name => ['Canada', 'Mexico', 'US'].includes(name.split('/')[0])
      ? name : name.split('/').slice(-1)[0])
    .map(name => name.replace(/_/g, ' '));

// Provide a mapping between a human-friendly city name and its corresponding
// timezone identifier and timezone abbreviation as a named export.
// We can fuzzy match on any of these.
const tzMaps = tzCities.map(city => {
  let tzMap = {};
  const tzName = tzNames[tzCities.indexOf(city)];

  tzMap['city'] = city;
  tzMap['zoneName'] = tzName;
  tzMap['zoneAbbr'] = moment().tz(tzName).zoneAbbr();

  return tzMap;
});

const getTzForCity = (city) => tzMaps
    .filter(tzMap => tzMap['city'] === city)
    .reduce(tzMap => tzMap);

const getTzForName = (name) => tzMaps
    .filter(tzMap => tzMap['zoneName'] === name)
    .reduce(tzMap => tzMap);

const guessUserTz = () => {
  // User-Agent sniffing is not always reliable, but is the recommended technique
  // for determining whether or not we're on a mobile device according to MDN
  // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#Mobile_Tablet_or_Desktop
  const isMobile = global.navigator !== undefined
    ? global.navigator.userAgent.match(/Mobi/)
    : false;

  const supportsIntl = global.Intl !== undefined;

  let userTz;

  if (isMobile && supportsIntl) {
    // moment-timezone gives preference to the Intl API regardless of device type,
    // so unset global.Intl to trick moment-timezone into using its fallback
    // see https://github.com/moment/moment-timezone/issues/441
    // TODO: Clean this up when that issue is resolved
    const globalIntl = global.Intl;
    global.Intl = undefined;
    userTz = moment.tz.guess();
    global.Intl = globalIntl;
  } else {
    userTz = moment.tz.guess();
  }

  return getTzForName(userTz);
};

export default {
  current: getCurrentTime,
  time: getValidTimeData,
  validate: getValidateTime,
  validateInt: getValidateIntTime,
  validateTimeMode: getValidateTimeMode,
  initial: initialTime,
  tzForCity: getTzForCity,
  guessUserTz,
  tzMaps
};
<commit_msg>cleanup docstrings<commit_after>import moment from 'moment-timezone';
import { head, last } from './func';

// loads moment-timezone's timezone data, which comes from the
// IANA Time Zone Database at https://www.iana.org/time-zones
moment.tz.load({version: 'latest', zones: [], links: []});

/**
 * Create a time data object using moment.
 * If a time is provided, just format it; if not, use the current time.
 *
 * @function getValidTimeData
 * @param  {string} time          a time; defaults to now
 * @param  {string} meridiem      AM or PM; defaults to AM via moment
 * @param  {Number} timeMode      12 or 24-hour mode
 * @param  {string} tz            a timezone name; guesses user timezone by default
 * @return {Object}               a key-value representation of time data
 */
const getValidTimeData = (time, meridiem, timeMode, tz = guessUserTz()) => {
  const validMeridiem = getValidMeridiem(meridiem);

  // when we only have a valid meridiem, that implies a 12h mode
  const mode = (validMeridiem && !timeMode) ? 12 : timeMode || 24;

  const validMode = getValidateTimeMode(mode);
  const validTime = getValidTimeString(time, validMeridiem);

  const time24 = ((validTime)
    ? moment(`1970-01-01 ${validTime}`).tz(tz.zoneName).format('HH:mmA')
    : moment().tz(tz.zoneName).format('HH:mmA')).split(/:/);

  const time12 = ((validTime)
    ? moment(`1970-01-01 ${validTime}`).tz(tz.zoneName).format('hh:mmA')
    : moment().tz(tz.zoneName).format('hh:mmA')).split(/:/);

  const timeData = {
    hour12: head(time12).replace(/^0/, ''),
    hour24: head(time24),
    minute: last(time24).slice(0, 2),
    meridiem: last(time12).slice(2),
    mode: validMode,
    timezone: tz
  };

  return timeData;
};

/**
 * Format the current time as a string
 * @function getCurrentTime
 * @return {string}
 */
const getCurrentTime = () => {
  const time = getValidTimeData();
  return `${time.hour24}:${time.minute}`;
};

/**
 * Get an integer representation of a time.
 * @function getValidateIntTime
 * @param  {string} time
 * @return {Number}
 */
const getValidateIntTime = (time) => {
  if (isNaN(parseInt(time))) { return 0; }
  return parseInt(time);
};

/**
 * Validate, set a default for, and stringify time data.
 * @function getValidateTime
 * @param {string}
 * @return {string}
 */
const getValidateTime = (time) => {
  if (typeof time === 'undefined') { time = '00'; }
  if (isNaN(parseInt(time))) { time = '00'; }
  if (parseInt(time) < 10) { time = `0${parseInt(time)}`; }
  return `${time}`;
};

/**
 * Given a time and meridiem, produce a time string to pass to moment
 * @function getValidTimeString
 * @param  {string} time
 * @param  {string} meridiem
 * @return {string}
 */
const getValidTimeString = (time, meridiem) => {
  if (typeof time === 'string') {
    let validTime = (time && time.includes(':'))
      ? time.split(/:/).map((t) => getValidateTime(t)).join(':')
      : time;

    validTime = (validTime && meridiem && parseInt(head(validTime.split(/:/))) < 12)
      ? `${validTime} ${meridiem}`
      : validTime;

    return validTime;
  }

  return time;
};

/**
 * Get time data to use for initializing a TimePicker.
 * @function initialTime
 * @param  {string} defaultTime
 * @param  {Number} mode=24
 * @return {[string, string, string]}
 */
const initialTime = (defaultTime, mode = 24) => {
  let [hour, minute, meridiem] = getValidTimeData(defaultTime, mode);

  hour = getValidateIntTime(hour);
  minute = getValidateIntTime(minute);

  if (hour > 24) { hour = 24 - hour; }
  if (minute >= 60) { minute = 60 - minute; }

  hour = getValidateTime(hour);
  minute = getValidateTime(minute);

  return [hour, minute, meridiem];
};

const getValidMeridiem = (meridiem) => {
  if (typeof meridiem === 'string') {
    return (meridiem && meridiem.match(/[am|pm]/i)) ? meridiem : null;
  }

  return meridiem;
};

/**
 * Validate and set a sensible default for time modes.
 * @function getValidateTimeMode
 * @param  {string|Number} timeMode
 * @return {Number}
 */
const getValidateTimeMode = (timeMode) => {
  let mode = parseInt(timeMode);

  if (isNaN(mode)) { return 24; }
  if (mode !== 24 && mode !== 12) { return 24; }

  return mode;
};

const tzNames = (() => {
  //  We want to subset the existing timezone data as much as possible, both for efficiency
  //  and to avoid confusing the user. Here, we focus on removing reduntant timezone names
  //  and timezone names for timezones we don't necessarily care about, like Antarctica, and
  //  special timezone names that exist for convenience.
  const scrubbedPrefixes = ['Antarctica', 'Arctic', 'Chile', 'Etc'];
  const scrubbedSuffixes = ['ACT', 'East', 'Knox_IN', 'LHI', 'North', 'NSW', 'South', 'West'];

  const tzNames = moment.tz.names()
      .filter(name => name.indexOf('/') !== -1)
      .filter(name => !scrubbedPrefixes.includes(name.split('/')[0]))
      .filter(name => !scrubbedSuffixes.includes(name.split('/').slice(-1)[0]));

  return tzNames;
})();

// We need a human-friendly city name for each timezone identifier
// counting Canada/*, Mexico/*, and US/* allows users to search for
// things like 'Eastern' or 'Mountain' and get matches back
const tzCities = tzNames
    .map(name => ['Canada', 'Mexico', 'US'].includes(name.split('/')[0])
      ? name : name.split('/').slice(-1)[0])
    .map(name => name.replace(/_/g, ' '));

// Provide a mapping between a human-friendly city name and its corresponding
// timezone identifier and timezone abbreviation as a named export.
// We can fuzzy match on any of these.
const tzMaps = tzCities.map(city => {
  let tzMap = {};
  const tzName = tzNames[tzCities.indexOf(city)];

  tzMap['city'] = city;
  tzMap['zoneName'] = tzName;
  tzMap['zoneAbbr'] = moment().tz(tzName).zoneAbbr();

  return tzMap;
});

const getTzForCity = (city) => tzMaps
    .filter(tzMap => tzMap['city'] === city)
    .reduce(tzMap => tzMap);

const getTzForName = (name) => tzMaps
    .filter(tzMap => tzMap['zoneName'] === name)
    .reduce(tzMap => tzMap);

const guessUserTz = () => {
  // User-Agent sniffing is not always reliable, but is the recommended technique
  // for determining whether or not we're on a mobile device according to MDN
  // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent#Mobile_Tablet_or_Desktop
  const isMobile = global.navigator !== undefined
    ? global.navigator.userAgent.match(/Mobi/)
    : false;

  const supportsIntl = global.Intl !== undefined;

  let userTz;

  if (isMobile && supportsIntl) {
    // moment-timezone gives preference to the Intl API regardless of device type,
    // so unset global.Intl to trick moment-timezone into using its fallback
    // see https://github.com/moment/moment-timezone/issues/441
    // TODO: Clean this up when that issue is resolved
    const globalIntl = global.Intl;
    global.Intl = undefined;
    userTz = moment.tz.guess();
    global.Intl = globalIntl;
  } else {
    userTz = moment.tz.guess();
  }

  return getTzForName(userTz);
};

export default {
  current: getCurrentTime,
  time: getValidTimeData,
  validate: getValidateTime,
  validateInt: getValidateIntTime,
  validateTimeMode: getValidateTimeMode,
  initial: initialTime,
  tzForCity: getTzForCity,
  guessUserTz,
  tzMaps
};
<|endoftext|>


<commit_before>import {extend} from "lodash";
import SeqCollection from "../model/SeqCollection";

const TreeHelper =  function(msa) {
  this.msa = msa;
  return this;
};

var tf =

    {loadTree: function(cb) {
      return this.msa.g.package.loadPackages(["msa-tnt", "biojs-io-newick"], cb);
    },

    showTree: function(newickStr) {
      var newick = window.require("biojs-io-newick");
      var mt = window.require("msa-tnt");

      if (typeof newickStr === "string") {
        var newickObj = newick.parse_newick(newickStr);
      } else {
        newickObj = newickStr;
      }

      var sel = new mt.selections();
      var treeDiv;

      if(this.msa.el.getElementsByClassName('tnt_groupDiv').length === 0){
        treeDiv = document.createElement("div");
        this.msa.el.appendChild(treeDiv);
      } else {
        console.log('A tree already exists. It will be overridden.');
        treeDiv = this.msa.el.getElementsByClassName('tnt_groupDiv')[0].parentNode;
        treeDiv.innerHTML = '';
      }

      const seqs = this.msa.seqs.toJSON();
      //adapt tree ids to sequence ids
      function iterateTree(nwck){
        if(nwck.children != null){
          nwck.children.forEach(x => iterateTree(x));
        } else {
          //found a leave
          let seq = seqs.filter(s => s.name === nwck.name)[0];

          if(seq != null){
            if(typeof seq.id === 'number'){
              //no tree has been uploaded so far, seqs have standard IDs
              seq.ids = [`s${seq.id + 1}`];
              nwck.name = `s${seq.id + 1}`;
            } else {
              //seqs have custom ids - don't mess with these
              console.log('hi');
              nwck.name = seq.id;
            }
          }
        }
      }
      iterateTree(newickObj);

      var nodes = mt.app({
        seqs: seqs,
        tree: newickObj
      });

      var t = new mt.adapters.tree({
        model: nodes,
        el: treeDiv,
        sel: sel
      });

      //treeDiv.style.width = "500px"

      // construct msa in a virtual dom
      var m = new mt.adapters.msa({
          model: nodes,
          sel: sel,
          msa: this.msa
      });

      // remove top collection
      nodes.models.forEach((e) => {
        delete e.collection;
        return Object.setPrototypeOf(e, require("backbone-thin").Model.prototype);
      });

      this.msa.seqs.reset(nodes.models);
      //@msa.draw()
      //@msa.render()
      return console.log(this.msa.seqs);
    },

    // workaround against browserify's static analysis
    require(pkg) {
      return require(pkg);
    }
    };

extend(TreeHelper.prototype , tf);
export default TreeHelper;
<commit_msg>removed useless console.log<commit_after>import {extend} from "lodash";
import SeqCollection from "../model/SeqCollection";

const TreeHelper =  function(msa) {
  this.msa = msa;
  return this;
};

var tf =

    {loadTree: function(cb) {
      return this.msa.g.package.loadPackages(["msa-tnt", "biojs-io-newick"], cb);
    },

    showTree: function(newickStr) {
      var newick = window.require("biojs-io-newick");
      var mt = window.require("msa-tnt");

      if (typeof newickStr === "string") {
        var newickObj = newick.parse_newick(newickStr);
      } else {
        newickObj = newickStr;
      }

      var sel = new mt.selections();
      var treeDiv;

      if(this.msa.el.getElementsByClassName('tnt_groupDiv').length === 0){
        treeDiv = document.createElement("div");
        this.msa.el.appendChild(treeDiv);
      } else {
        console.log('A tree already exists. It will be overridden.');
        treeDiv = this.msa.el.getElementsByClassName('tnt_groupDiv')[0].parentNode;
        treeDiv.innerHTML = '';
      }

      const seqs = this.msa.seqs.toJSON();
      //adapt tree ids to sequence ids
      function iterateTree(nwck){
        if(nwck.children != null){
          nwck.children.forEach(x => iterateTree(x));
        } else {
          //found a leave
          let seq = seqs.filter(s => s.name === nwck.name)[0];

          if(seq != null){
            if(typeof seq.id === 'number'){
              //no tree has been uploaded so far, seqs have standard IDs
              seq.ids = [`s${seq.id + 1}`];
              nwck.name = `s${seq.id + 1}`;
            } else {
              //seqs have custom ids - don't mess with these
              nwck.name = seq.id;
            }
          }
        }
      }
      iterateTree(newickObj);

      var nodes = mt.app({
        seqs: seqs,
        tree: newickObj
      });

      var t = new mt.adapters.tree({
        model: nodes,
        el: treeDiv,
        sel: sel
      });

      //treeDiv.style.width = "500px"

      // construct msa in a virtual dom
      var m = new mt.adapters.msa({
          model: nodes,
          sel: sel,
          msa: this.msa
      });

      // remove top collection
      nodes.models.forEach((e) => {
        delete e.collection;
        return Object.setPrototypeOf(e, require("backbone-thin").Model.prototype);
      });

      this.msa.seqs.reset(nodes.models);
      //@msa.draw()
      //@msa.render()
      return console.log(this.msa.seqs);
    },

    // workaround against browserify's static analysis
    require(pkg) {
      return require(pkg);
    }
    };

extend(TreeHelper.prototype , tf);
export default TreeHelper;
<|endoftext|>


<commit_before>import jump from 'jump.js'

export default class Util {
    /**
     * Expand color to full RGBA string
     *
     * @param raw rgba value
     * @return RGBA string
     */
    static expandColor (num) {
        num >>>= 0;

        let b = num & 0xFF,
            g = (num & 0xFF00) >>> 8,
            r = (num & 0xFF0000) >>> 16,
            a = ((num & 0xFF000000) >>> 24) / 255;
        return "rgba(" + [r, g, b, a].join(",") + ")";
    }

    static entityEncode (string) {

        while (string.indexOf("<") !== -1) {
            string = string.replace("<", "<");
        }

        while (string.indexOf(">") !== -1) {
            string = string.replace(">", ">");
        }

        string = string.replace(/\n/g, '
');

        return string;
    }

    static generateSnippet (object) {

        if(typeof object.snippet != "undefined")
            return object.snippet;

        let snippet = object.snippet || object.data;
        let type = object.message_type || object.type;
        let mime_type = object.mime_type || "text/plain";

        snippet = mime_type == "text/plain" ? (snippet) : "Photo";

        if (type != 0 && type != 6 && type != 3 && type != 5)
            snippet = "You: " + snippet;

        return snippet;

    }

    static generateContact (id, title, mute, private_notifications, color, accent, ligher, darker) {
        return {
            id: id,
            title: title,
            mute: mute,
            private_notifications: private_notifications,
            colors: {
                default: color,
                accent: accent,
                ligher: ligher,
                darker: darker
            }
        }
    }

    /**
        * Scroll to bottom
        * Scrolls thread to bottom of page; uses tween easing
        *
        * @param speed - default to zero (no animation)
        */
    static scrollToBottom(speed=0) {

        const docu = document.getElementsByTagName("html")[0].clientHeight
        const body = document.getElementsByTagName("body")[0].clientHeight

        const bottom = Math.max(docu, body); // Calculate bottom

        // Jump to bottom
        jump(bottom, {
            duration: speed,
            easing: (t, b, c, d) => { // easeInOutCubic - @jaxgeller
                if ((t/=d/2) < 1) return c/2*t*t*t + b;
                return c/2*((t-=2)*t*t + 2) + b;
            }
        });
    }

    /**
     * Is Scrolled to Bottom
     * Determines if scrolled to bottom of page
     *
     * @param el, optional object
     * @return boolean
     */
    static isScrolledToBottom(el = null) {
        if (el == null)
            el = document.querySelector("html");

       return el.scrollTop ===
            (el.scrollHeight - el.offsetHeight)
    }

    static snackbar(message) {
        let data = {}

        if(typeof message == "string")
            data = { message: message };
        else
            data = message

        const snackbar = document.querySelector('.mdl-js-snackbar');

        if (typeof snackbar.attributes['hidden'] != "undefined") // ad block work around
            snackbar.attributes.removeNamedItem("hidden");

        snackbar.MaterialSnackbar.showSnackbar(data);
    }

    static materialColorChange($el, color) {

        const container = document.createElement("span"); // Overflow container
        const animator = document.createElement("span"); // Animation space

        container.appendChild(animator); // Prepare and insert in dom
        container.className = "animator";
        $el.insertBefore(container, $el.firstChild);

        animator.style.background = color; // Add background color
        $el.className += " transition"; // Add transition class for animation

        setTimeout(() => {
            $el.style.background = color;
            $el.style.backgroundColor = color;

            $el.className = $el.className.replace(" transition", "");

            $el.removeChild(container);
        }, 1250);
    }
}

/**
* Contains element
* @param element value
*/
Array.prototype.contains = function(element) {
    return this.indexOf(element) > -1 ? true : false
}

/**
* Contains element, by key
* @param key
* @param element value
*/
Array.prototype.containsObjKey = function(key, element) {

    for (let i = 0; i < this.length; i++)
        if (this[i][key] === element)
            return true

    return false
}

<commit_msg>Fix scroll to bottom jitter<commit_after>import jump from 'jump.js'

export default class Util {
    /**
     * Expand color to full RGBA string
     *
     * @param raw rgba value
     * @return RGBA string
     */
    static expandColor (num) {
        num >>>= 0;

        let b = num & 0xFF,
            g = (num & 0xFF00) >>> 8,
            r = (num & 0xFF0000) >>> 16,
            a = ((num & 0xFF000000) >>> 24) / 255;
        return "rgba(" + [r, g, b, a].join(",") + ")";
    }

    static entityEncode (string) {

        while (string.indexOf("<") !== -1) {
            string = string.replace("<", "<");
        }

        while (string.indexOf(">") !== -1) {
            string = string.replace(">", ">");
        }

        string = string.replace(/\n/g, '
');

        return string;
    }

    static generateSnippet (object) {

        if(typeof object.snippet != "undefined")
            return object.snippet;

        let snippet = object.snippet || object.data;
        let type = object.message_type || object.type;
        let mime_type = object.mime_type || "text/plain";

        snippet = mime_type == "text/plain" ? (snippet) : "Photo";

        if (type != 0 && type != 6 && type != 3 && type != 5)
            snippet = "You: " + snippet;

        return snippet;

    }

    static generateContact (id, title, mute, private_notifications, color, accent, ligher, darker) {
        return {
            id: id,
            title: title,
            mute: mute,
            private_notifications: private_notifications,
            colors: {
                default: color,
                accent: accent,
                ligher: ligher,
                darker: darker
            }
        }
    }

    /**
        * Scroll to bottom
        * Scrolls thread to bottom of page; uses tween easing
        *
        * @param speed - default to zero (no animation)
        */
    static scrollToBottom(speed=0) {

        if (Util.isScrolledToBottom()) // Ignore if at bottom
            return false;

        const docu = document.getElementsByTagName("html")[0].clientHeight
        const body = document.getElementsByTagName("body")[0].clientHeight

        const bottom = Math.max(docu, body); // Calculate bottom

        // Jump to bottom
        jump(bottom, {
            duration: speed,
            easing: (t, b, c, d) => { // easeInOutCubic - @jaxgeller
                if ((t/=d/2) < 1) return c/2*t*t*t + b;
                return c/2*((t-=2)*t*t + 2) + b;
            }
        });
    }

    /**
     * Is Scrolled to Bottom
     * Determines if scrolled to bottom of page
     *
     * @param el, optional object
     * @return boolean
     */
    static isScrolledToBottom(el = null) {
        if (el == null)
            el = document.querySelector("html");

       return el.scrollTop ===
            (el.scrollHeight - el.offsetHeight)
    }

    static snackbar(message) {
        let data = {}

        if(typeof message == "string")
            data = { message: message };
        else
            data = message

        const snackbar = document.querySelector('.mdl-js-snackbar');

        if (typeof snackbar.attributes['hidden'] != "undefined") // ad block work around
            snackbar.attributes.removeNamedItem("hidden");

        snackbar.MaterialSnackbar.showSnackbar(data);
    }

    static materialColorChange($el, color) {

        const container = document.createElement("span"); // Overflow container
        const animator = document.createElement("span"); // Animation space

        container.appendChild(animator); // Prepare and insert in dom
        container.className = "animator";
        $el.insertBefore(container, $el.firstChild);

        animator.style.background = color; // Add background color
        $el.className += " transition"; // Add transition class for animation

        setTimeout(() => {
            $el.style.background = color;
            $el.style.backgroundColor = color;

            $el.className = $el.className.replace(" transition", "");

            $el.removeChild(container);
        }, 1250);
    }
}

/**
* Contains element
* @param element value
*/
Array.prototype.contains = function(element) {
    return this.indexOf(element) > -1 ? true : false
}

/**
* Contains element, by key
* @param key
* @param element value
*/
Array.prototype.containsObjKey = function(key, element) {

    for (let i = 0; i < this.length; i++)
        if (this[i][key] === element)
            return true

    return false
}

<|endoftext|>


<commit_before>// Copyright 2018, University of Colorado Boulder

/**
 * ?fuzzBoard keyboard fuzzer
 * TODO: keep track of keystate so that we don't trigger a keydown of keyA before the previous keyA keyup event has been called.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  const KeyStateTracker = require( 'SCENERY/accessibility/KeyStateTracker' );
  const KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  const Random = require( 'DOT/Random' );
  const scenery = require( 'SCENERY/scenery' );
  const timer = require( 'PHET_CORE/timer' );

  // uppercase matters
  const keyboardTestingSchema = {
    INPUT: [ ...KeyboardUtil.ARROW_KEYS, KeyboardUtil.KEY_PAGE_UP, KeyboardUtil.KEY_PAGE_DOWN,
      KeyboardUtil.KEY_HOME, KeyboardUtil.KEY_END, KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ],
    DIV: [ ...KeyboardUtil.ARROW_KEYS ],
    P: [ KeyboardUtil.KEY_ESCAPE ],
    BUTTON: [ KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ]
  };

  const NEXT_ELEMENT_THRESHOLD = .10;

  const DO_KNOWN_KEYS_THRESHOLD = .60; // for keydown/up
  const CLICK_EVENT = .10; // TODO because of implementation this is actually 20%. but reads like "half of the time after 60% of the time"

  const KEY_DOWN = 'keydown';
  const KEY_UP = 'keyup';

  var min = 9;
  var max = 223;

  /**
   *
   * @param {Object} [options]
   * @constructor
   */
  class KeyboardFuzzer {
    constructor( display, seed ) {

      // @private
      this.display = display;
      this.random = new Random( { seed: seed } );
      this.keyStateTracker = new KeyStateTracker();
      this.keysPressedEachFrame = 10;

      // @private {HTMLElement}
      this.currentElement = null;
    }

    /**
     * Randomly decide if we should focus the next element, or stay focused on the current element
     */
    chooseNextElement() {
      if ( this.currentElement === null ) {
        this.currentElement = document.activeElement;
      }
      else if ( this.random.nextDouble() < NEXT_ELEMENT_THRESHOLD ) {
        var nextFocusable = AccessibilityUtil.getRandomFocusable();
        nextFocusable.focus();
        this.currentElement = nextFocusable;
      }

    }

    /**
     * @param {HTMLElement} element
     */
    triggerClickEvent( element ) {
      element.click();
    }

    /**
     * Trigger a keydown/keyup pair. The keyup is triggered with a timeout
     * @param {HTMLElement} element
     * @param {number} keyCode
     */
    triggerKeyDownUpEvents( element, keyCode ) {

      if ( !this.keyStateTracker.isKeyDown( keyCode ) ) {

        // TODO: screen readers normally take our keydown events, but may not here, is the descrpency ok?
        this.triggerDOMEvent( KEY_DOWN, element, keyCode );

        timer.setTimeout( () => {
          this.triggerDOMEvent( KEY_UP, element, keyCode );

        }, 1 ); // TODO: make this time variable?
      }
    }

    /**
     * Trigger a keydown/keyup pair with a random keyCode
     * @param {HTMLElement} element
     */
    triggerRandomKeyDownUpEvents( element ) {

      var randomKeyCode = Math.floor( this.random.nextDouble() * ( max - min ) + min );

      this.triggerKeyDownUpEvents( element, randomKeyCode );

    }

    /**
     * A random event creater that sends keyboard events. Based on the idea of fuzzMouse, but to test/spam accessibility
     * related keyboard navigation and alternate input implementation.
     *
     * TODO: NOTE: Right now this is a very experimental implementation. Tread wearily
     * TODO: @param keyboardPressesPerFocusedItem {number} - basically would be the same as fuzzRate, but handling
     * TODO:     the keydown events for a focused item
     */
    fuzzBoardEvents() {

      this.chooseNextElement();

      var elementWithFocus = document.activeElement;

      for ( let i = 0; i < this.keysPressedEachFrame; i++ ) {

        if ( keyboardTestingSchema[ elementWithFocus.tagName ] ) {

          if ( this.random.nextDouble() < DO_KNOWN_KEYS_THRESHOLD ) {
            const keyCodes = keyboardTestingSchema[ elementWithFocus.tagName ];
            const keyCode = this.random.sample( keyCodes );
            this.triggerKeyDownUpEvents( elementWithFocus, keyCode );
          }
          else if ( this.random.nextDouble() < CLICK_EVENT ) {
            this.triggerClickEvent( elementWithFocus );
          }
          else {
            this.triggerRandomKeyDownUpEvents( elementWithFocus );
          }
        }
        // TODO: What about other types of events, not just keydown/keyup??!?!
        // TODO: what about application role elements
      }
    }

    /**
     * Taken from example in http://output.jsbin.com/awenaq/3,
     * @param {string} event
     * @param {HTMLElement} element
     * @param {number} [keycode]
     */
    triggerDOMEvent( event, element, keyCode ) {
      var eventObj = document.createEventObject ?
                     document.createEventObject() : document.createEvent( 'Events' );

      if ( eventObj.initEvent ) {
        eventObj.initEvent( event, true, true );
      }

      eventObj.keyCode = keyCode;
      // eventObj.shiftKey = true; // TODO: we can add modifier keys in here with options?
      eventObj.which = keyCode;

      if ( this.keyStateTracker.shiftKeyDown ) {
        eventObj.shiftKey = true;
      }

      if ( event === KEY_DOWN ) {
        this.keyStateTracker.keydownUpdate( eventObj );
      }
      else if ( event === KEY_UP ) {
        this.keyStateTracker.keyupUpdate( eventObj );
      }

      element.dispatchEvent ? element.dispatchEvent( eventObj ) : element.fireEvent( 'on' + event, eventObj );
    }
  }

  return scenery.register( 'KeyboardFuzzer', KeyboardFuzzer );
} );<commit_msg>add variable length of time a key is down, https://github.com/phetsims/scenery/issues/871<commit_after>// Copyright 2018, University of Colorado Boulder

/**
 * ?fuzzBoard keyboard fuzzer
 * TODO: keep track of keystate so that we don't trigger a keydown of keyA before the previous keyA keyup event has been called.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( require => {
  'use strict';

  // modules
  const AccessibilityUtil = require( 'SCENERY/accessibility/AccessibilityUtil' );
  const KeyStateTracker = require( 'SCENERY/accessibility/KeyStateTracker' );
  const KeyboardUtil = require( 'SCENERY/accessibility/KeyboardUtil' );
  const Random = require( 'DOT/Random' );
  const scenery = require( 'SCENERY/scenery' );
  const timer = require( 'PHET_CORE/timer' );

  // uppercase matters
  const keyboardTestingSchema = {
    INPUT: [ ...KeyboardUtil.ARROW_KEYS, KeyboardUtil.KEY_PAGE_UP, KeyboardUtil.KEY_PAGE_DOWN,
      KeyboardUtil.KEY_HOME, KeyboardUtil.KEY_END, KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ],
    DIV: [ ...KeyboardUtil.ARROW_KEYS ],
    P: [ KeyboardUtil.KEY_ESCAPE ],
    BUTTON: [ KeyboardUtil.KEY_ENTER, KeyboardUtil.KEY_SPACE ]
  };

  const MAX_MS_KEY_HOLD_DOWN = 200;
  const NEXT_ELEMENT_THRESHOLD = .1;

  const DO_KNOWN_KEYS_THRESHOLD = .60; // for keydown/up
  const CLICK_EVENT = .10; // TODO because of implementation this is actually 20%. but reads like "half of the time after 60% of the time"

  const KEY_DOWN = 'keydown';
  const KEY_UP = 'keyup';

  var min = 9;
  var max = 223;

  /**
   *
   * @param {Object} [options]
   * @constructor
   */
  class KeyboardFuzzer {
    constructor( display, seed ) {

      // @private
      this.display = display;
      this.random = new Random( { seed: seed } );
      this.keyStateTracker = new KeyStateTracker();
      this.keysPressedEachFrame = 100;
      this.keyupListeners = [];

      // @private {HTMLElement}
      this.currentElement = null;
    }

    /**
     * @private
     * Randomly decide if we should focus the next element, or stay focused on the current element
     */
    chooseNextElement() {
      if ( this.currentElement === null ) {
        this.currentElement = document.activeElement;
      }
      else if ( this.random.nextDouble() < NEXT_ELEMENT_THRESHOLD ) {

        // before we change focus to the next item, press "keyup" on all elements we had.
        this.clearListeners();
        var nextFocusable = AccessibilityUtil.getRandomFocusable();
        nextFocusable.focus();
        this.currentElement = nextFocusable;
      }
    }

    /**
     * @private
     */
    clearListeners() {
      this.keyupListeners.forEach( function( listener ) {
        timer.clearTimeout( listener );
        listener();
      } );
    }

    /**
     * @private
     * @param {HTMLElement} element
     */
    triggerClickEvent( element ) {
      element.click();
    }

    /**
     * Trigger a keydown/keyup pair. The keyup is triggered with a timeout
     * @param {HTMLElement} element
     * @param {number} keyCode
     */
    triggerKeyDownUpEvents( element, keyCode ) {

      if ( !this.keyStateTracker.isKeyDown( keyCode ) ) {

        // TODO: screen readers normally take our keydown events, but may not here, is the descrpency ok?
        this.triggerDOMEvent( KEY_DOWN, element, keyCode );

        this.keyupListeners.push( timer.setTimeout( () => {
          this.triggerDOMEvent( KEY_UP, element, keyCode );

        }, this.random.nextInt( MAX_MS_KEY_HOLD_DOWN ) ) );
      }
    }

    /**
     * Trigger a keydown/keyup pair with a random keyCode
     * @private
     * @param {HTMLElement} element
     */
    triggerRandomKeyDownUpEvents( element ) {

      var randomKeyCode = Math.floor( this.random.nextDouble() * ( max - min ) + min );

      this.triggerKeyDownUpEvents( element, randomKeyCode );

    }

    /**
     * A random event creater that sends keyboard events. Based on the idea of fuzzMouse, but to test/spam accessibility
     * related keyboard navigation and alternate input implementation.
     *
     * @public
     * TODO: NOTE: Right now this is a very experimental implementation. Tread wearily
     * TODO: @param keyboardPressesPerFocusedItem {number} - basically would be the same as fuzzRate, but handling
     * TODO:     the keydown events for a focused item
     */
    fuzzBoardEvents() {

      this.chooseNextElement();

      var elementWithFocus = document.activeElement;

      for ( let i = 0; i < this.keysPressedEachFrame; i++ ) {

        if ( keyboardTestingSchema[ elementWithFocus.tagName ] ) {

          if ( this.random.nextDouble() < DO_KNOWN_KEYS_THRESHOLD ) {
            const keyCodes = keyboardTestingSchema[ elementWithFocus.tagName ];
            const keyCode = this.random.sample( keyCodes );
            this.triggerKeyDownUpEvents( elementWithFocus, keyCode );
          }
          else if ( this.random.nextDouble() < CLICK_EVENT ) {
            this.triggerClickEvent( elementWithFocus );
          }
          else {
            this.triggerRandomKeyDownUpEvents( elementWithFocus );
          }
        }
        // TODO: What about other types of events, not just keydown/keyup??!?!
        // TODO: what about application role elements
      }
    }

    /**
     * Taken from example in http://output.jsbin.com/awenaq/3,
     * @param {string} event
     * @param {HTMLElement} element
     * @param {number} [keycode]
     * @private
     */
    triggerDOMEvent( event, element, keyCode ) {
      var eventObj = document.createEventObject ?
                     document.createEventObject() : document.createEvent( 'Events' );

      if ( eventObj.initEvent ) {
        eventObj.initEvent( event, true, true );
      }

      eventObj.keyCode = keyCode;
      // eventObj.shiftKey = true; // TODO: we can add modifier keys in here with options?
      eventObj.which = keyCode;

      if ( this.keyStateTracker.shiftKeyDown ) {
        eventObj.shiftKey = true;
      }

      if ( event === KEY_DOWN ) {
        this.keyStateTracker.keydownUpdate( eventObj );
      }
      else if ( event === KEY_UP ) {
        this.keyStateTracker.keyupUpdate( eventObj );
      }

      element.dispatchEvent ? element.dispatchEvent( eventObj ) : element.fireEvent( 'on' + event, eventObj );
    }
  }

  return scenery.register( 'KeyboardFuzzer', KeyboardFuzzer );
} );<|endoftext|>


<commit_before>// Copyright 2017, University of Colorado Boulder

/**
 * Manages a queue of Utterances that are read in order by a screen reader.  This queue typically reads
 * things in a first-in-first-out manner, but it is possible to send an alert directly to the front of
 * the queue.  Items in the queue are sent to the screen reader front to back with a certain delay.
 *
 * Screen readers are inconsistent in the way that they order alerts, some use last-in-first-out order,
 * others use first-in-first-out order, others just read the last alert that was provided. This queue
 * manages order and improves consistency.
 *
 * @author Jesse Greenberg
 */
define( function( require ) {
  'use strict';

  // modules
  var AriaHerald = require( 'SCENERY_PHET/accessibility/AriaHerald' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Timer = require( 'PHET_CORE/Timer' );
  var Utterance = require( 'SCENERY_PHET/accessibility/Utterance' );
  var UtteranceQueueIO = require( 'SCENERY_PHET/accessibility/UtteranceQueueIO' );

  // phet-io modules
  var phetioEvents = require( 'ifphetio!PHET_IO/phetioEvents' );

  // {Tandem} - phet-io support
  var tandem = Tandem.rootTandem.createTandem( 'utteranceQueue' );

  // {Utterance} - array of utterances, spoken in first to last order
  var queue = [];

  // the interval for sending alerts to the screen reader, in milliseconds
  var interval = 500;

  // whether or not Utterances moving through the queue are read by a screen reader
  var muted = false;

  // whether the UtterancesQueue is alerting, and if you can add/remove utterances
  var enabled = true;

  var UtteranceQueue = {

    /**
     * Add an utterance ot the end of the queue.  If the utterance has a type of alert which
     * is already in the queue, the older alert will be immediately removed.
     *
     * @public
     * @param {Utterance|string} utterance
     */
    addToBack: function( utterance ) {
      assert && assert( utterance instanceof Utterance || typeof utterance === 'string',
        'utterance queue only supports string or type Utterance.' );

      // No-op function if the UtteranceQueue is disabled
      if ( !enabled ) {
        return;
      }

      if ( typeof utterance === 'string' ) {
        utterance = new Utterance( utterance );
      }

      // if there are any other items in the queue of the same type, remove them immediately
      // because the new utterance is meant to replace them
      if ( utterance.typeId ) {
        for ( var i = queue.length - 1; i >= 0; i-- ) {
          var otherUtterance = queue[ i ];
          if ( otherUtterance.typeId === utterance.typeId ) {
            queue.splice( i, 1 );
          }
        }
      }

      queue.push( utterance );
    },

    /**
     * Add an utterance to the front of the queue to be read immediately.
     * @param {Utterance|string} utterance
     */
    addToFront: function( utterance ) {
      assert && assert( utterance instanceof Utterance || typeof utterance === 'string',
        'utterance queue only supports string or type Utterance.' );

      // No-op function if the UtteranceQueue is disabled
      if ( !enabled ) {
        return;
      }

      if ( typeof utterance === 'string' ) {
        utterance = new Utterance( utterance );
      }
      queue.unshift( utterance );
    },

    /**
     * Move to the next item in the queue. Checks the Utterance predicate first, if predicate
     * returns false, no alert will be read. Called privately by Timer.setInterval
     *
     * @private
     */
    next: function() {

      // get and remove the next item from the queue
      var nextUtterance = queue.shift();

      // only speak the utterance if the Utterance predicate returns true
      if ( nextUtterance && !muted && nextUtterance.predicate() ) {

        // phet-io event to the data stream
        var id = phetioEvents.start( 'model', tandem.id, UtteranceQueueIO, 'announced', { utterance: nextUtterance.text } );

        // Pass the utterance text on to be set in the PDOM.
        AriaHerald.announcePolite( nextUtterance.text );

        phetioEvents.end( id );
      }
    },

    /**
     * Clear the UtteranceQueue of all Utterances, any Utterances remaining in the queue will
     * not be announced by the screen reader.
     *
     * @public
     */
    clear: function() {
      queue = [];
    },

    /**
     * Set whether or not the utterance queue is muted.  When muted, Utterances will still
     * move through the queue, but nothing will be sent to assistive technology.
     *
     * @param {boolean} isMuted
     */
    setMuted: function( isMuted ) {
      muted = isMuted;
    },
    set muted( isMuted ) { this.setMuted( isMuted ); },

    /**
     * Get whether or not the UtteranceQueue is muted.  When muted, Utterances will still
     * move through the queue, but nothing will be read by asistive technology.
     * @public
     */
    getMuted: function() {
      return muted;
    },
    get muted() { return this.getMuted(); },

    /**
     * Set whether or not the utterance queue is enabled.  When enabled, Utterances cannot be added to
     * the queue, and the Queue cannot be cleared. Also nothing will be sent to assistive technology.
     *
     * @param {boolean} isEnabled
     */
    setEnabled: function( isEnabled ) {
      enabled = isEnabled;
    },
    set enabled( isEnabled ) { this.setEnabled( isEnabled ); },

    /**
     * Get whether or not the utterance queue is enabled.  When enabled, Utterances cannot be added to
     * the queue, and the Queue cannot be cleared. Also nothing will be sent to assistive technology.
     * @public
     */
    getEnabled: function() {
      return enabled;
    },
    get enabled() { return this.getEnabled(); },

    /**
     * Get the interval that alerts are sent to the screen reader.
     *
     * @public
     * @return {number}
     */
    getInterval: function() {
      return interval;
    },
    get interval() { return this.getInterval(); },

    /**
     * Set the alert interval in milliseconds
     * @public
     * @param {number} alertInterval
     */
    setInterval: function( alertInterval ) {
      interval = alertInterval;
    },
    set interval( alertInterval ) { this.setInterval( alertInterval ); }
  };

  sceneryPhet.register( 'UtteranceQueue', UtteranceQueue );

  // step the alert queue
  Timer.setInterval( function() {

      // No-op function if the UtteranceQueue is disabled
      if ( !enabled ) {
        return;
      }

      UtteranceQueue.next();
    }, UtteranceQueue.interval
  );

  tandem.addInstance( UtteranceQueue, { phetioType: UtteranceQueueIO } );

  return UtteranceQueue;
} );
<commit_msg>disable UtteranceQueue when a11y not enabled, https://github.com/phetsims/joist/issues/454 https://github.com/phetsims/scenery/issues/710<commit_after>// Copyright 2017, University of Colorado Boulder

/**
 * Manages a queue of Utterances that are read in order by a screen reader.  This queue typically reads
 * things in a first-in-first-out manner, but it is possible to send an alert directly to the front of
 * the queue.  Items in the queue are sent to the screen reader front to back with a certain delay interval.
 *
 * Screen readers are inconsistent in the way that they order alerts, some use last-in-first-out order,
 * others use first-in-first-out order, others just read the last alert that was provided. This queue
 * manages order and improves consistency.
 *
 * NOTE: UtteranceQueue is just an Object, not a type, but it needs to be initialized before use as a singleton.
 * As of this writing (Nov 2017) this initialization occurs in Sim.js. Therefore if something adds an alert to the
 * queue before Sim.js has initialized the queue, the result will be a silent no-op.
 *
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @author Michael Kauzmann (PhET Interactive Simulations)
 */
define( function( require ) {
  'use strict';

  // modules
  var AriaHerald = require( 'SCENERY_PHET/accessibility/AriaHerald' );
  var sceneryPhet = require( 'SCENERY_PHET/sceneryPhet' );
  var Tandem = require( 'TANDEM/Tandem' );
  var Timer = require( 'PHET_CORE/Timer' );
  var Utterance = require( 'SCENERY_PHET/accessibility/Utterance' );
  var UtteranceQueueIO = require( 'SCENERY_PHET/accessibility/UtteranceQueueIO' );

  // phet-io modules
  var phetioEvents = require( 'ifphetio!PHET_IO/phetioEvents' );

  // {Tandem} - phet-io support
  var tandem = Tandem.rootTandem.createTandem( 'utteranceQueue' );

  // {Utterance} - array of utterances, spoken in first to last order
  var queue = [];

  // the interval for sending alerts to the screen reader, in milliseconds
  var interval = 500;

  // whether or not Utterances moving through the queue are read by a screen reader
  var muted = false;

  // whether the UtterancesQueue is alerting, and if you can add/remove utterances
  var enabled = true;

  // initialization is like UtteranceQueue's constructor. No-ops all around if not initialized (cheers).
  var initialized = false;

  var UtteranceQueue = {

    /**
     * Add an utterance ot the end of the queue.  If the utterance has a type of alert which
     * is already in the queue, the older alert will be immediately removed.
     *
     * @public
     * @param {Utterance|string} utterance
     */
    addToBack: function( utterance ) {
      assert && assert( utterance instanceof Utterance || typeof utterance === 'string',
        'utterance queue only supports string or type Utterance.' );

      // No-op function if the UtteranceQueue is disabled
      if ( !enabled || !initialized ) {
        return;
      }

      if ( typeof utterance === 'string' ) {
        utterance = new Utterance( utterance );
      }

      // if there are any other items in the queue of the same type, remove them immediately
      // because the new utterance is meant to replace them
      if ( utterance.typeId ) {
        for ( var i = queue.length - 1; i >= 0; i-- ) {
          var otherUtterance = queue[ i ];
          if ( otherUtterance.typeId === utterance.typeId ) {
            queue.splice( i, 1 );
          }
        }
      }

      queue.push( utterance );
    },

    /**
     * Add an utterance to the front of the queue to be read immediately.
     * @param {Utterance|string} utterance
     */
    addToFront: function( utterance ) {
      assert && assert( utterance instanceof Utterance || typeof utterance === 'string',
        'utterance queue only supports string or type Utterance.' );

      // No-op function if the UtteranceQueue is disabled
      if ( !enabled || !initialized ) {
        return;
      }

      if ( typeof utterance === 'string' ) {
        utterance = new Utterance( utterance );
      }
      queue.unshift( utterance );
    },

    /**
     * Move to the next item in the queue. Checks the Utterance predicate first, if predicate
     * returns false, no alert will be read. Called privately by Timer.setInterval
     *
     * @private
     */
    next: function() {

      // get and remove the next item from the queue
      var nextUtterance = queue.shift();

      // only speak the utterance if the Utterance predicate returns true
      if ( nextUtterance && !muted && nextUtterance.predicate() ) {

        // phet-io event to the data stream
        var id = phetioEvents.start( 'model', tandem.id, UtteranceQueueIO, 'announced', { utterance: nextUtterance.text } );

        // Pass the utterance text on to be set in the PDOM.
        AriaHerald.announcePolite( nextUtterance.text );

        phetioEvents.end( id );
      }
    },

    /**
     * Clear the UtteranceQueue of all Utterances, any Utterances remaining in the queue will
     * not be announced by the screen reader.
     *
     * @public
     */
    clear: function() {
      queue = [];
    },

    /**
     * Set whether or not the utterance queue is muted.  When muted, Utterances will still
     * move through the queue, but nothing will be sent to assistive technology.
     *
     * @param {boolean} isMuted
     */
    setMuted: function( isMuted ) {
      muted = isMuted;
    },
    set muted( isMuted ) { this.setMuted( isMuted ); },

    /**
     * Get whether or not the UtteranceQueue is muted.  When muted, Utterances will still
     * move through the queue, but nothing will be read by asistive technology.
     * @public
     */
    getMuted: function() {
      return muted;
    },
    get muted() { return this.getMuted(); },

    /**
     * Set whether or not the utterance queue is enabled.  When enabled, Utterances cannot be added to
     * the queue, and the Queue cannot be cleared. Also nothing will be sent to assistive technology.
     *
     * @param {boolean} isEnabled
     */
    setEnabled: function( isEnabled ) {
      enabled = isEnabled;
    },
    set enabled( isEnabled ) { this.setEnabled( isEnabled ); },

    /**
     * Get whether or not the utterance queue is enabled.  When enabled, Utterances cannot be added to
     * the queue, and the Queue cannot be cleared. Also nothing will be sent to assistive technology.
     * @public
     */
    getEnabled: function() {
      return enabled;
    },
    get enabled() { return this.getEnabled(); },

    /**
     * Get the interval that alerts are sent to the screen reader.
     *
     * @public
     * @return {number}
     */
    getInterval: function() {
      return interval;
    },
    get interval() { return this.getInterval(); },

    /**
     * Set the alert interval in milliseconds
     * @public
     * @param {number} alertInterval
     */
    setInterval: function( alertInterval ) {
      interval = alertInterval;
    },
    set interval( alertInterval ) { this.setInterval( alertInterval ); },

    /**
     * Basically a constructor for the queue. Setup necessary processes for runing the queue and register
     * the phet-io tandem. If UtteranceQueue is not initialized, all functions will be no-ops. See type
     * documentation above for NOTE.
     * @public
     */
    initialize: function() {
      initialized = true;

      var self = this;

      // step the alert queue
      Timer.setInterval( function() {

          // No-op function if the UtteranceQueue is disabled
          if ( !enabled ) {
            return;
          }

          self.next();
        }, this.interval
      );

      tandem.addInstance( this, { phetioType: UtteranceQueueIO } );
    }
  };

  sceneryPhet.register( 'UtteranceQueue', UtteranceQueue );

  return UtteranceQueue;
} );
<|endoftext|>