<commit_before>import validationRules from 'form/validationRules';

describe('Validation-Rules', () => {
  // values no use most of the time as arg[0] of validation functions
  const values = {};
  it('required', () => {
    const { required } = validationRules;
    expect(required(values)).toBe(false);
    expect(required(values, '')).toBe(false);
    expect(required(values, ' ')).toBe(true);
  });

  it('isExisty', () => {
    const { isExisty } = validationRules;
    expect(isExisty(values)).toBe(false);
    expect(isExisty(values, null)).toBe(false);
    expect(isExisty(values, '')).toBe(true);
  });

  it('matchRegex', () => {
    const { matchRegex } = validationRules;
    expect(matchRegex(values)).toBe(true);
    expect(matchRegex(values, null)).toBe(true);
    expect(matchRegex(values, '')).toBe(true);
    expect(matchRegex(values, 'bac', /c/)).toBe(true);
    expect(matchRegex(values, 'bac', /^c/)).toBe(false);
    expect(matchRegex(values, 'abc', /^a/)).toBe(true);
  });

  it('isUndefined', () => {
    const { isUndefined } = validationRules;
    expect(isUndefined(values)).toBe(true);
    let t;
    expect(isUndefined(values, t)).toBe(true);
    expect(isUndefined(values, '')).toBe(false);
  });

  it('isEmptyString', () => {
    const { isEmptyString } = validationRules;
    // return value === ''
    expect(isEmptyString(values, '11')).toBe(false);
    expect(isEmptyString(values, 1)).toBe(false);
    expect(isEmptyString(values, '')).toBe(true);
  });

  it('isEmail', () => {
    const { isEmail } = validationRules;

    // NOTE: ！！！空值返回true！！！
    expect(isEmail(values)).toBe(true);
    expect(isEmail(values, null)).toBe(true);
    expect(isEmail(values, '')).toBe(true);

    expect(isEmail(values, 'foo')).toBe(false);
    expect(isEmail(values, 'foo@')).toBe(false);
    expect(isEmail(values, 'foo@.')).toBe(false);
    expect(isEmail(values, 'foo@.com')).toBe(false);
    expect(isEmail(values, 'foo@bar.com')).toBe(true);
    expect(isEmail(values, 'foo1@bar.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar1.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar1-2.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar1-2*.com')).toBe(false);
    expect(isEmail(values, 'Foo1-*2@bar1-2.g.com')).toBe(true);
    expect(isEmail(values, 'foo1-*2@Bar1-2.edu.cn')).toBe(true);
  });

  it('isUrl', () => {
    const { isUrl } = validationRules;

    // NOTE: ！！！空值返回true！！！
    expect(isUrl(values)).toBe(true);
    expect(isUrl(values, null)).toBe(true);
    expect(isUrl(values, '')).toBe(true);

    expect(isUrl(values, 'www')).toBe(false);
    expect(isUrl(values, 'www.foo')).toBe(false);
    expect(isUrl(values, 'www.foo.com')).toBe(false);
    expect(isUrl(values, 'http://www')).toBe(false);
    expect(isUrl(values, 'http://foo')).toBe(false);
    expect(isUrl(values, 'http://foo.bar')).toBe(true);
    expect(isUrl(values, 'http://www.foo')).toBe(true);
    expect(isUrl(values, 'https://www.foo')).toBe(true);
    expect(isUrl(values, 'ftp://www.foo')).toBe(true);
    expect(isUrl(values, 'sftp://www.foo')).toBe(true);
    expect(isUrl(values, 'http://foo.com')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo')).toBe(true);
    expect(isUrl(values, 'http://foo.com/#foo')).toBe(true);
    expect(isUrl(values, 'http://foo.com/#foo/Foo-bar')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo-bar?foo=bar')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo-bar?foo=bar')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo-bar?foo=bar&bar=foo')).toBe(true);
  });

  it('isTrue', () => {
    const { isTrue } = validationRules;

    // return value === true
    expect(isTrue(values)).toBe(false);
    expect(isTrue(values, 1)).toBe(false);
    expect(isTrue(values, true)).toBe(true);
  });

  it('isFalse', () => {
    const { isFalse } = validationRules;

    // return value === false
    expect(isFalse(values)).toBe(false);
    expect(isFalse(values, 0)).toBe(false);
    expect(isFalse(values, false)).toBe(true);
  });

  it('isNumeric', () => {
    const { isNumeric } = validationRules;

    // NOTE: 空值返回true
    expect(isNumeric()).toBe(true);
    expect(isNumeric(values)).toBe(true);
    expect(isNumeric(values, null)).toBe(true);
    expect(isNumeric(values, '')).toBe(true);

    // NOTE: 按type判断和正则判断不匹配
    expect(isNumeric(values, 0)).toBe(true);
    expect(isNumeric(values, -1)).toBe(true);
    expect(isNumeric(values, -1.1)).toBe(true);
    expect(isNumeric(values, -1.1e12)).toBe(true);
    expect(isNumeric(values, 0b101)).toBe(true);
    expect(isNumeric(values, 0o17)).toBe(true);
    expect(isNumeric(values, 0x1a)).toBe(true);

    expect(isNumeric(values, '0')).toBe(true);
    expect(isNumeric(values, '-1')).toBe(true);
    expect(isNumeric(values, '-1.1')).toBe(true);
    expect(isNumeric(values, '-1.1e12')).toBe(false);
    expect(isNumeric(values, '0b101')).toBe(false);
    expect(isNumeric(values, '0o17')).toBe(false);
    expect(isNumeric(values, '0x1a')).toBe(false);
  });

  it('isInt', () => {
    const { isInt } = validationRules;

    // NOTE: 空值返回true
    expect(isInt()).toBe(true);
    expect(isInt(values)).toBe(true);
    expect(isInt(values, null)).toBe(true);
    expect(isInt(values, '')).toBe(true);

    // RegExp.test() support Number input
    expect(isInt(values, 1)).toBe(true);
    expect(isInt(values, 0b1)).toBe(true);
    expect(isInt(values, 0o1)).toBe(true);
    expect(isInt(values, 0x1)).toBe(true);
    expect(isInt(values, '1')).toBe(true);
    expect(isInt(values, 0)).toBe(true);
    expect(isInt(values, '0')).toBe(true);
    expect(isInt(values, '+0')).toBe(true);
    expect(isInt(values, '-0')).toBe(true);
    expect(isInt(values, '+1')).toBe(true);
    expect(isInt(values, '-1')).toBe(true);
    expect(isInt(values, -1234567)).toBe(true);
    expect(isInt(values, '+1234567')).toBe(true);
    expect(isInt(values, 0.1)).toBe(false);
    expect(isInt(values, '0.1')).toBe(false);
  });

  it('isFloat', () => {
    const { isFloat } = validationRules;

    // NOTE: 空值返回true
    expect(isFloat()).toBe(true);
    expect(isFloat(values)).toBe(true);
    expect(isFloat(values, null)).toBe(true);
    expect(isFloat(values, '')).toBe(true);

    expect(isFloat(values, 1.1)).toBe(true);
    expect(isFloat(values, 1)).toBe(true);
    expect(isFloat(values, 0b1)).toBe(true);
    expect(isFloat(values, 0o1)).toBe(true);
    expect(isFloat(values, 0x1)).toBe(true);
    expect(isFloat(values, '1.1')).toBe(true);
    expect(isFloat(values, 0)).toBe(true);
    expect(isFloat(values, '0')).toBe(true);
    expect(isFloat(values, '+0')).toBe(true);
    expect(isFloat(values, '-0')).toBe(true);
    expect(isFloat(values, '+1')).toBe(true);
    expect(isFloat(values, '-1')).toBe(true);
    expect(isFloat(values, -1234.567)).toBe(true);
    expect(isFloat(values, '+1234.567')).toBe(true);
    expect(isFloat(values, 0.1)).toBe(true);
    expect(isFloat(values, '0.1')).toBe(true);
  });

  xit('isWords', () => {
    const { isWords } = validationRules;

    // NOTE: 空值返回true
    expect(isWords()).toBe(true);
    expect(isWords(values)).toBe(true);
    expect(isWords(values, null)).toBe(true);
    expect(isWords(values, '')).toBe(true);

    expect(isWords(values, 'foo bar')).toBe(true);
    expect(isWords(values, 'foo-bar')).toBe(false);
    expect(isWords(values, 'foo_bar')).toBe(false);
    expect(isWords(values, 'foo bar *')).toBe(false);
    expect(isWords(values, 'foo.bar')).toBe(false);
    expect(isWords(values, 'À')).toBe(false);
  });

  xit('isSpecialWords', () => {
    const { isSpecialWords } = validationRules;

    // NOTE: 空值返回true
    expect(isSpecialWords()).toBe(true);
    expect(isSpecialWords(values)).toBe(true);
    expect(isSpecialWords(values, null)).toBe(true);
    expect(isSpecialWords(values, '')).toBe(true);

    expect(isSpecialWords(values, 'foo bar')).toBe(true);
    expect(isSpecialWords(values, 'foo-bar')).toBe(false);
    expect(isSpecialWords(values, 'foo_bar')).toBe(false);
    expect(isSpecialWords(values, 'foo bar *')).toBe(false);
    expect(isSpecialWords(values, 'foo.bar')).toBe(false);
    expect(isSpecialWords(values, 'Àſ')).toBe(true);
  });

  it('isLength', () => {
    const { isLength } = validationRules;

    // NOTE: 空值返回true
    expect(isLength()).toBe(true);
    expect(isLength(values)).toBe(true);
    expect(isLength(values, null)).toBe(true);
    expect(isLength(values, '')).toBe(true);

    expect(isLength(values, 'foo', 3)).toBe(true);
    expect(isLength(values, 'foo', 2)).toBe(false);
    expect(isLength(values, [1, 2, 3], 3)).toBe(true);
    expect(isLength(values, [1, 2, 3], 2)).toBe(false);
    expect(isLength(values, { length: 3 }, 3)).toBe(true);
    expect(isLength(values, { length: 3 }, 2)).toBe(false);
  });

  it('equals', () => {
    const { equals } = validationRules;

    // NOTE: 空值返回true
    expect(equals()).toBe(true);
    expect(equals(values)).toBe(true);
    expect(equals(values, null)).toBe(true);
    expect(equals(values, '')).toBe(true);

    // return value == eql
    expect(equals(values, 12, 12)).toBe(true);
    expect(equals(values, 12, '12')).toBe(true);
    expect(equals(values, 0x12, '18')).toBe(true);
    expect(equals(values, [1, 2], '1,2')).toBe(true);
    expect(
      equals(
        values,
        {
          foo: 'bar',
          valueOf() {
            return 12;
          }
        },
        12
      )
    ).toBe(true);
  });

  it('equalsField', () => {
    const { equalsField } = validationRules;

    // return values[field] == value
    const specialValues = {};
    specialValues.foo = 12;
    specialValues.bar = '1,2';
    expect(() => {
      equalsField();
    }).toThrow();
    expect(equalsField(specialValues, 12, 'foo')).toBe(true);
    expect(equalsField(specialValues, 12, 'bar')).toBe(false);
    expect(equalsField(specialValues, '12', 'foo')).toBe(true);
    expect(
      equalsField(
        specialValues,
        {
          foo: 'bar',
          valueOf() {
            return 12;
          }
        },
        'foo'
      )
    ).toBe(true);
    expect(equalsField(specialValues, [1, 2], 'bar')).toBe(true);
  });

  it('maxLength', () => {
    const { maxLength } = validationRules;

    // NOTE: 空值(undefined, null)返回true
    expect(maxLength()).toBe(true);
    expect(maxLength(values)).toBe(true);
    expect(maxLength(values, null)).toBe(true);
    expect(maxLength(values, '')).toBe(false);

    // return value.length <= maxLength
    expect(maxLength(values, 'foo', 3)).toBe(true);
    expect(maxLength(values, 'foo', 4)).toBe(true);
    expect(maxLength(values, 'foo', 2)).toBe(false);
    expect(maxLength(values, [1, 2, 3], 2)).toBe(false);
    expect(maxLength(values, [1, 2, 3], 3)).toBe(true);
    expect(maxLength(values, [1, 2, 3], 4)).toBe(true);
    expect(maxLength(values, { length: 3 }, 4)).toBe(true);
    expect(maxLength(values, { length: 3 }, 3)).toBe(true);
    expect(maxLength(values, { length: 3 }, 2)).toBe(false);
  });

  it('minLength', () => {
    const { minLength } = validationRules;

    // NOTE: 空值(undefined, null, '')返回true
    expect(minLength()).toBe(true);
    expect(minLength(values)).toBe(true);
    expect(minLength(values, null)).toBe(true);
    expect(minLength(values, '')).toBe(true);

    // return value.length >= minLength
    expect(minLength(values, 'foo', 3)).toBe(true);
    expect(minLength(values, 'foo', 4)).toBe(false);
    expect(minLength(values, 'foo', 2)).toBe(true);
    expect(minLength(values, [1, 2, 3], 2)).toBe(true);
    expect(minLength(values, [1, 2, 3], 3)).toBe(true);
    expect(minLength(values, [1, 2, 3], 4)).toBe(false);
    expect(minLength(values, { length: 3 }, 4)).toBe(false);
    expect(minLength(values, { length: 3 }, 3)).toBe(true);
    expect(minLength(values, { length: 3 }, 2)).toBe(true);
  });
});
<commit_msg>fix: modify test of validate rule<commit_after>import validationRules from 'form/validationRules';

describe('Validation-Rules', () => {
  // values no use most of the time as arg[0] of validation functions
  const values = {};
  it('required', () => {
    const { required } = validationRules;
    expect(required(values)).toBe(false);
    expect(required(values, '')).toBe(false);
    expect(required(values, ' ')).toBe(true);
  });

  it('isExisty', () => {
    const { isExisty } = validationRules;
    expect(isExisty(values)).toBe(false);
    expect(isExisty(values, null)).toBe(false);
    expect(isExisty(values, '')).toBe(true);
  });

  it('matchRegex', () => {
    const { matchRegex } = validationRules;
    expect(matchRegex(values)).toBe(true);
    expect(matchRegex(values, null)).toBe(true);
    expect(matchRegex(values, '')).toBe(true);
    expect(matchRegex(values, 'bac', /c/)).toBe(true);
    expect(matchRegex(values, 'bac', /^c/)).toBe(false);
    expect(matchRegex(values, 'abc', /^a/)).toBe(true);
  });

  it('isUndefined', () => {
    const { isUndefined } = validationRules;
    expect(isUndefined(values)).toBe(true);
    let t;
    expect(isUndefined(values, t)).toBe(true);
    expect(isUndefined(values, '')).toBe(false);
  });

  it('isEmptyString', () => {
    const { isEmptyString } = validationRules;
    // return value === ''
    expect(isEmptyString(values, '11')).toBe(false);
    expect(isEmptyString(values, 1)).toBe(false);
    expect(isEmptyString(values, '')).toBe(true);
  });

  it('isEmail', () => {
    const { isEmail } = validationRules;

    // NOTE: ！！！空值返回true！！！
    expect(isEmail(values)).toBe(true);
    expect(isEmail(values, null)).toBe(true);
    expect(isEmail(values, '')).toBe(true);

    expect(isEmail(values, 'foo')).toBe(false);
    expect(isEmail(values, 'foo@')).toBe(false);
    expect(isEmail(values, 'foo@.')).toBe(false);
    expect(isEmail(values, 'foo@.com')).toBe(false);
    expect(isEmail(values, 'foo@bar.com')).toBe(true);
    expect(isEmail(values, 'foo1@bar.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar1.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar1-2.com')).toBe(true);
    expect(isEmail(values, 'foo1-2@bar1-2*.com')).toBe(false);
    expect(isEmail(values, 'Foo1-*2@bar1-2.g.com')).toBe(true);
    expect(isEmail(values, 'foo1-*2@Bar1-2.edu.cn')).toBe(true);
  });

  it('isUrl', () => {
    const { isUrl } = validationRules;

    // NOTE: ！！！空值返回true！！！
    expect(isUrl(values)).toBe(true);
    expect(isUrl(values, null)).toBe(true);
    expect(isUrl(values, '')).toBe(true);

    expect(isUrl(values, 'www')).toBe(false);
    expect(isUrl(values, 'www.foo')).toBe(false);
    expect(isUrl(values, 'www.foo.com')).toBe(false);
    expect(isUrl(values, 'http://www')).toBe(false);
    expect(isUrl(values, 'http://foo')).toBe(false);
    expect(isUrl(values, 'http://foo.bar')).toBe(true);
    expect(isUrl(values, 'http://www.foo')).toBe(true);
    expect(isUrl(values, 'https://www.foo')).toBe(true);
    expect(isUrl(values, 'ftp://www.foo')).toBe(true);
    expect(isUrl(values, 'sftp://www.foo')).toBe(true);
    expect(isUrl(values, 'http://foo.com')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo')).toBe(true);
    expect(isUrl(values, 'http://foo.com/#foo')).toBe(true);
    expect(isUrl(values, 'http://foo.com/#foo/Foo-bar')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo-bar?foo=bar')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo-bar?foo=bar')).toBe(true);
    expect(isUrl(values, 'http://foo.com/foo-bar?foo=bar&bar=foo')).toBe(true);
  });

  it('isTrue', () => {
    const { isTrue } = validationRules;

    // return value === true
    expect(isTrue(values)).toBe(false);
    expect(isTrue(values, 1)).toBe(false);
    expect(isTrue(values, true)).toBe(true);
  });

  it('isFalse', () => {
    const { isFalse } = validationRules;

    // return value === false
    expect(isFalse(values)).toBe(false);
    expect(isFalse(values, 0)).toBe(false);
    expect(isFalse(values, false)).toBe(true);
  });

  it('isNumeric', () => {
    const { isNumeric } = validationRules;

    // NOTE: 空值返回true
    expect(isNumeric()).toBe(true);
    expect(isNumeric(values)).toBe(true);
    expect(isNumeric(values, null)).toBe(true);
    expect(isNumeric(values, '')).toBe(true);

    // NOTE: 按type判断和正则判断不匹配
    expect(isNumeric(values, 0)).toBe(true);
    expect(isNumeric(values, -1)).toBe(true);
    expect(isNumeric(values, -1.1)).toBe(true);
    expect(isNumeric(values, -1.1e12)).toBe(true);
    expect(isNumeric(values, 0b101)).toBe(true);
    expect(isNumeric(values, 0o17)).toBe(true);
    expect(isNumeric(values, 0x1a)).toBe(true);

    expect(isNumeric(values, '0')).toBe(true);
    expect(isNumeric(values, '-1')).toBe(true);
    expect(isNumeric(values, '-1.1')).toBe(true);
    expect(isNumeric(values, '-1.1e12')).toBe(false);
    expect(isNumeric(values, '0b101')).toBe(false);
    expect(isNumeric(values, '0o17')).toBe(false);
    expect(isNumeric(values, '0x1a')).toBe(false);
  });

  it('isInt', () => {
    const { isInt } = validationRules;

    // NOTE: 空值返回true
    expect(isInt()).toBe(true);
    expect(isInt(values)).toBe(true);
    expect(isInt(values, null)).toBe(true);
    expect(isInt(values, '')).toBe(true);

    // RegExp.test() support Number input
    expect(isInt(values, 1)).toBe(true);
    expect(isInt(values, 0b1)).toBe(true);
    expect(isInt(values, 0o1)).toBe(true);
    expect(isInt(values, 0x1)).toBe(true);
    expect(isInt(values, '1')).toBe(true);
    expect(isInt(values, 0)).toBe(true);
    expect(isInt(values, '0')).toBe(true);
    expect(isInt(values, '+0')).toBe(true);
    expect(isInt(values, '-0')).toBe(true);
    expect(isInt(values, '+1')).toBe(true);
    expect(isInt(values, '-1')).toBe(true);
    expect(isInt(values, -1234567)).toBe(true);
    expect(isInt(values, '+1234567')).toBe(true);
    expect(isInt(values, 0.1)).toBe(false);
    expect(isInt(values, '0.1')).toBe(false);
  });

  it('isFloat', () => {
    const { isFloat } = validationRules;

    // NOTE: 空值返回true
    expect(isFloat()).toBe(true);
    expect(isFloat(values)).toBe(true);
    expect(isFloat(values, null)).toBe(true);
    expect(isFloat(values, '')).toBe(true);

    expect(isFloat(values, 1.1)).toBe(true);
    expect(isFloat(values, 1)).toBe(true);
    expect(isFloat(values, 0b1)).toBe(true);
    expect(isFloat(values, 0o1)).toBe(true);
    expect(isFloat(values, 0x1)).toBe(true);
    expect(isFloat(values, '1.1')).toBe(true);
    expect(isFloat(values, 0)).toBe(true);
    expect(isFloat(values, '0')).toBe(true);
    expect(isFloat(values, '+0')).toBe(true);
    expect(isFloat(values, '-0')).toBe(true);
    expect(isFloat(values, '+1')).toBe(true);
    expect(isFloat(values, '-1')).toBe(true);
    expect(isFloat(values, -1234.567)).toBe(true);
    expect(isFloat(values, '+1234.567')).toBe(true);
    expect(isFloat(values, 0.1)).toBe(true);
    expect(isFloat(values, '0.1')).toBe(true);
  });

  xit('isWords', () => {
    const { isWords } = validationRules;

    // NOTE: 空值返回true
    expect(isWords()).toBe(true);
    expect(isWords(values)).toBe(true);
    expect(isWords(values, null)).toBe(true);
    expect(isWords(values, '')).toBe(true);

    expect(isWords(values, 'foo bar')).toBe(true);
    expect(isWords(values, 'foo-bar')).toBe(false);
    expect(isWords(values, 'foo_bar')).toBe(false);
    expect(isWords(values, 'foo bar *')).toBe(false);
    expect(isWords(values, 'foo.bar')).toBe(false);
    expect(isWords(values, 'À')).toBe(false);
  });

  xit('isSpecialWords', () => {
    const { isSpecialWords } = validationRules;

    // NOTE: 空值返回true
    expect(isSpecialWords()).toBe(true);
    expect(isSpecialWords(values)).toBe(true);
    expect(isSpecialWords(values, null)).toBe(true);
    expect(isSpecialWords(values, '')).toBe(true);

    expect(isSpecialWords(values, 'foo bar')).toBe(true);
    expect(isSpecialWords(values, 'foo-bar')).toBe(false);
    expect(isSpecialWords(values, 'foo_bar')).toBe(false);
    expect(isSpecialWords(values, 'foo bar *')).toBe(false);
    expect(isSpecialWords(values, 'foo.bar')).toBe(false);
    expect(isSpecialWords(values, 'Àſ')).toBe(true);
  });

  it('isLength', () => {
    const { isLength } = validationRules;

    // NOTE: 空值返回true
    expect(isLength()).toBe(true);
    expect(isLength(values)).toBe(true);
    expect(isLength(values, null)).toBe(true);
    expect(isLength(values, '')).toBe(true);

    expect(isLength(values, 'foo', 3)).toBe(true);
    expect(isLength(values, 'foo', 2)).toBe(false);
    expect(isLength(values, [1, 2, 3], 3)).toBe(true);
    expect(isLength(values, [1, 2, 3], 2)).toBe(false);
    expect(isLength(values, { length: 3 }, 3)).toBe(true);
    expect(isLength(values, { length: 3 }, 2)).toBe(false);
  });

  it('equals', () => {
    const { equals } = validationRules;

    // NOTE: 空值返回true
    expect(equals()).toBe(true);
    expect(equals(values)).toBe(true);
    expect(equals(values, null)).toBe(true);
    expect(equals(values, '')).toBe(true);

    // return value == eql
    expect(equals(values, 12, 12)).toBe(true);
    expect(equals(values, 12, '12')).toBe(true);
    expect(equals(values, 0x12, '18')).toBe(true);
    expect(equals(values, [1, 2], '1,2')).toBe(true);
    expect(
      equals(
        values,
        {
          foo: 'bar',
          valueOf() {
            return 12;
          }
        },
        12
      )
    ).toBe(true);
  });

  it('equalsField', () => {
    const { equalsField } = validationRules;

    // return values[field] == value
    const specialValues = {};
    specialValues.foo = 12;
    specialValues.bar = '1,2';
    // expect(() => {
    //   equalsField();
    // }).toThrow();
    expect(equalsField(specialValues, 12, 'foo')).toBe(true);
    expect(equalsField(specialValues, 12, 'bar')).toBe(false);
    expect(equalsField(specialValues, '12', 'foo')).toBe(true);
    expect(
      equalsField(
        specialValues,
        {
          foo: 'bar',
          valueOf() {
            return 12;
          }
        },
        'foo'
      )
    ).toBe(true);
    expect(equalsField(specialValues, [1, 2], 'bar')).toBe(true);
  });

  it('maxLength', () => {
    const { maxLength } = validationRules;

    // NOTE: 空值(undefined, null)返回true
    expect(maxLength()).toBe(true);
    expect(maxLength(values)).toBe(true);
    expect(maxLength(values, null)).toBe(true);
    expect(maxLength(values, '')).toBe(false);

    // return value.length <= maxLength
    expect(maxLength(values, 'foo', 3)).toBe(true);
    expect(maxLength(values, 'foo', 4)).toBe(true);
    expect(maxLength(values, 'foo', 2)).toBe(false);
    expect(maxLength(values, [1, 2, 3], 2)).toBe(false);
    expect(maxLength(values, [1, 2, 3], 3)).toBe(true);
    expect(maxLength(values, [1, 2, 3], 4)).toBe(true);
    expect(maxLength(values, { length: 3 }, 4)).toBe(true);
    expect(maxLength(values, { length: 3 }, 3)).toBe(true);
    expect(maxLength(values, { length: 3 }, 2)).toBe(false);
  });

  it('minLength', () => {
    const { minLength } = validationRules;

    // NOTE: 空值(undefined, null, '')返回true
    expect(minLength()).toBe(true);
    expect(minLength(values)).toBe(true);
    expect(minLength(values, null)).toBe(true);
    expect(minLength(values, '')).toBe(true);

    // return value.length >= minLength
    expect(minLength(values, 'foo', 3)).toBe(true);
    expect(minLength(values, 'foo', 4)).toBe(false);
    expect(minLength(values, 'foo', 2)).toBe(true);
    expect(minLength(values, [1, 2, 3], 2)).toBe(true);
    expect(minLength(values, [1, 2, 3], 3)).toBe(true);
    expect(minLength(values, [1, 2, 3], 4)).toBe(false);
    expect(minLength(values, { length: 3 }, 4)).toBe(false);
    expect(minLength(values, { length: 3 }, 3)).toBe(true);
    expect(minLength(values, { length: 3 }, 2)).toBe(true);
  });
});
<|endoftext|>


<commit_before>class Paginator {

  constructor(params) {
    // normalize params object
    if (!params.options)  { params.options  = {}; }
    if (!params.labels)   { params.labels   = {}; }
    if (!params.features) { params.features = {}; }

    // parameter defaults
    this.callback     = params.callback;
    this.totalItems   = params.totalItems;
    this.currentPage  = params.currentPage  || 1;
    this.itemsPerPage = params.itemsPerPage || 10;

    this.calculateTotalPages();   // this.totalPages
    this.calculatePreviousPage(); // this.previousPage
    this.calculateNextPage();     // this.nextPage

    this.firstPage   = 1;
    this.lastPage    = this.totalPages;

    // user preferences
    this.options = {
      innerPagesCount: this.setValue(params.options.innerPagesCount, 1),
      outerPagesCount: this.setValue(params.options.outerPagesCount, 1)
    };

    this.labels = {
      currentPage:  params.labels.currentPage   || this.currentPage,
      previousPage: params.labels.previousPage  || '❮',
      nextPage:     params.labels.nextPage      || '❯',
      firstPage:    params.labels.firstPage     || this.firstPage,
      lastPage:     params.labels.lastPage      || this.lastPage,
      gapPage:      params.labels.gapPage       || '…'
    };

    // feature switches
    this.features = {
      hideAuto:     params.features.hideAuto      || false, // hide the whole pagination if it's one page
      hideGaps:     params.features.hideGaps      || false, // hide the gaps between outer and inner pages
      hideAdjacent: params.features.hideAdjacent  || false, // hide the next and previous page
      hideDisabled: params.features.hideDisabled  || false, // hide all pages with page state disabled
    };

    // internals
    this.pageStates = {
      disabled: 'disabled',
      hide:     'hide',
      current:  'current',
      first:    'first',
      last:     'last',
      previous: 'previous',
      next:     'next',
      gap:      'gap'
    };
    // up to date Pages array of Page objects
    this.pages = [];
    // TODO make this another class and use real objects
    // NB filled with page object:
    //page = {
    //  order: 0
    //  label: 'previous',
    //  state: ['disabled', 'previous'],
    //  node: <DOM/NODE>
    //};
  }

  calculateTotalPages() {
    this.totalPages = Math.ceil(this.totalItems/this.itemsPerPage);
  }

  calculatePreviousPage() {
    if (this.currentPage <= this.totalPages && this.currentPage > 1) {
      this.previousPage = (this.currentPage-1);
    } else {
      this.previousPage = null;
    }
  }

  calculateNextPage() {
    if (this.currentPage < this.totalPages && this.totalPages > 1) {
      this.nextPage = (this.currentPage+1);
    } else {
      this.nextPage = null;
    }
  }

  setValue(integerValue, defaultValue) {
    if ( isNaN(parseInt(integerValue)) ) {
      return defaultValue;
    } else {
      return integerValue;
    }
  }

  // Pass item range for user appplication query purposes
  getItemsRange(page = this.currentPage) {
    var start = ((page - 1) * this.itemsPerPage);
    var end = (start + this.itemsPerPage);
    return {
      start:  start,
      end:    end
    };
  }

  sortPages() {
    this.pages.sort(function (a, b) {
      return (a.order - b.order);
    });
  }

  buildPreviousPage(order) {
    const dataset = {page: this.previousPage};
    const label = this.labels.previousPage;
    const states = [this.pageStates.previous];
    if (this.previousPage == null) {
      states.push(this.pageStates.disabled);
    }
    // feature => hideAdjacent
    if (this.features.hideAdjacent) {
      states.push(this.pageStates.hide);
    }
    // feature => hideDisabled
    if (this.features.hideDisabled && states.includes(this.pageStates.disabled)) {
      states.push(this.pageStates.hide);
    }
    const page = new Page(order, label, states, dataset);
    this.pages.unshift(page);
  }

  buildNextPage(order) {
    const dataset = {page: this.nextPage};
    const label = this.labels.nextPage;
    const states = [this.pageStates.next];
    if (this.nextPage == null) {
      states.push(this.pageStates.disabled);
    }
    // feature => hideAdjacent
    if (this.features.hideAdjacent) {
      states.push(this.pageStates.hide);
    }
    // feature => hideDisabled
    if (this.features.hideDisabled && states.includes(this.pageStates.disabled)) {
      states.push(this.pageStates.hide);
    }
    const page = new Page(order, label, states, dataset);
    this.pages.push(page);
  }

  buildGaps() {
    const outerEdgeLeft  = this.firstPage + this.options.outerPagesCount;
    const outerEdgeRight = this.lastPage  - this.options.outerPagesCount;
    const innerEdgeLeft  = this.currentPage - this.options.innerPagesCount;
    const innerEdgeRight = this.currentPage + this.options.innerPagesCount;

    const label = this.labels.gapPage;
    const states = [this.pageStates.gap];
    // feature => hideGaps
    if (this.features.hideGaps) {
      states.push(this.pageStates.hide);
    }
    // left side
    if (innerEdgeLeft > outerEdgeLeft) {
      const order = (this.currentPage - this.options.innerPagesCount - 1);
      const page = new Page(order, label, states);
      this.pages.push(page);
    }
    // right side
    if (outerEdgeRight > innerEdgeRight) {
      const order = (this.currentPage + this.options.innerPagesCount);
      const page = new Page(order, label, states);
      this.pages.push(page);
    }
  }

  buildAllPages() {
    const outerEdgeLeft  = this.firstPage + this.options.outerPagesCount;
    const outerEdgeRight = this.lastPage  - this.options.outerPagesCount;
    const innerEdgeLeft  = this.currentPage - this.options.innerPagesCount;
    const innerEdgeRight = this.currentPage + this.options.innerPagesCount;
    for (let i = 1; i <= this.totalPages; i++) {
      if (
        ( i <  outerEdgeLeft || i >  outerEdgeRight ) ||
        ( i >= innerEdgeLeft && i <= innerEdgeRight )
      ) {
        // Determine page class and name
        const states = [];
        let label = i;
        if (i === this.currentPage) {
          states.push(this.pageStates.current);
        }
        if (i === this.firstPage) {
          label = this.labels.firstPage;
          states.push(this.pageStates.first);
        }
        if (i === this.lastPage) {
          label = this.labels.lastPage;
          states.push(this.pageStates.last);
        }
        // Push page
        const page = new Page(i, label, states, {page: i});
        this.pages.push(page);
      }
    }
  }

  clearPages() {
    this.pages = [];
  }

  // Generate DOM and populate targeted element
  // TODO use this only for DOM render and another method to build full Pages array
  render(containerSelector) {
    // feature => hideAdjacent
    if (this.features.hideAuto && this.totalPages < 2) {
      return;
    }

    // Find the pagination elements we want to fill in
    const containers = document.querySelectorAll(containerSelector);

    // Create DOM list
    const list = document.createElement('ul');
    // This would contain all list pages

    var self = this; // TODO should be able to remove this when refactoring

    // recalculate in case the user has tampered with the object
    this.calculateTotalPages();   // this.totalPages
    this.calculatePreviousPage(); // this.previousPage
    this.calculateNextPage();     // this.nextPage

    this.clearPages();
    this.buildAllPages();
    this.buildPreviousPage(0); // default to first position
    this.buildNextPage(this.totalPages+1); // default to last position
    this.buildGaps();

    this.sortPages();

    for (let i = 0; i < self.pages.length; i++) {
      list.appendChild(self.pages[i].node);
    }
    // Append the new DOM structure to all matched container
    for (const container of containers) {
      // clear the container for fresh render
      container.innerHTML = '';
      // copy the created nodes
      const listCopy = list.cloneNode(true);
      container.appendChild(listCopy);
      // add event listeners
      const links = container.querySelectorAll('a'); // NB returns a static NodeList, not live/dynamic NodeList, but it's more flexible to work with
      for (const link of links) {
        if (self.callback) {
          link.addEventListener('click', function() {
            const page = parseInt(this.dataset.page); // the clicked page in integer
            self.callback({
              page:       page,
              itemsRange: self.getItemsRange(page)
            });
          });
        } else {
          // TODO same url and attach page variable at the end
          // or just make your own callback that does that
          // if user one is missing; this might be better
        }
      }
    }
  }

  update(containerSelector) {
    // Find the pagination elements we want to fill in
    const containers = document.querySelectorAll(containerSelector);
    // Drop current contents
    for (const container of containers) {
      container.innerHTML = '';
    }
    // re-render
    this.render(containerSelector);
  }

}
<commit_msg>Bugfix: not assigning current page label when provided<commit_after>class Paginator {

  constructor(params) {
    // normalize params object
    if (!params.options)  { params.options  = {}; }
    if (!params.labels)   { params.labels   = {}; }
    if (!params.features) { params.features = {}; }

    // parameter defaults
    this.callback     = params.callback;
    this.totalItems   = params.totalItems;
    this.currentPage  = params.currentPage  || 1;
    this.itemsPerPage = params.itemsPerPage || 10;

    this.calculateTotalPages();   // this.totalPages
    this.calculatePreviousPage(); // this.previousPage
    this.calculateNextPage();     // this.nextPage

    this.firstPage   = 1;
    this.lastPage    = this.totalPages;

    // user preferences
    this.options = {
      innerPagesCount: this.setValue(params.options.innerPagesCount, 1),
      outerPagesCount: this.setValue(params.options.outerPagesCount, 1)
    };

    this.labels = {
      currentPage:  params.labels.currentPage   || this.currentPage,
      previousPage: params.labels.previousPage  || '❮',
      nextPage:     params.labels.nextPage      || '❯',
      firstPage:    params.labels.firstPage     || this.firstPage,
      lastPage:     params.labels.lastPage      || this.lastPage,
      gapPage:      params.labels.gapPage       || '…'
    };

    // feature switches
    this.features = {
      hideAuto:     params.features.hideAuto      || false, // hide the whole pagination if it's one page
      hideGaps:     params.features.hideGaps      || false, // hide the gaps between outer and inner pages
      hideAdjacent: params.features.hideAdjacent  || false, // hide the next and previous page
      hideDisabled: params.features.hideDisabled  || false, // hide all pages with page state disabled
    };

    // internals
    this.pageStates = {
      disabled: 'disabled',
      hide:     'hide',
      current:  'current',
      first:    'first',
      last:     'last',
      previous: 'previous',
      next:     'next',
      gap:      'gap'
    };
    // up to date Pages array of Page objects
    this.pages = [];
    // TODO make this another class and use real objects
    // NB filled with page object:
    //page = {
    //  order: 0
    //  label: 'previous',
    //  state: ['disabled', 'previous'],
    //  node: <DOM/NODE>
    //};
  }

  calculateTotalPages() {
    this.totalPages = Math.ceil(this.totalItems/this.itemsPerPage);
  }

  calculatePreviousPage() {
    if (this.currentPage <= this.totalPages && this.currentPage > 1) {
      this.previousPage = (this.currentPage-1);
    } else {
      this.previousPage = null;
    }
  }

  calculateNextPage() {
    if (this.currentPage < this.totalPages && this.totalPages > 1) {
      this.nextPage = (this.currentPage+1);
    } else {
      this.nextPage = null;
    }
  }

  setValue(integerValue, defaultValue) {
    if ( isNaN(parseInt(integerValue)) ) {
      return defaultValue;
    } else {
      return integerValue;
    }
  }

  // Pass item range for user appplication query purposes
  getItemsRange(page = this.currentPage) {
    var start = ((page - 1) * this.itemsPerPage);
    var end = (start + this.itemsPerPage);
    return {
      start:  start,
      end:    end
    };
  }

  sortPages() {
    this.pages.sort(function (a, b) {
      return (a.order - b.order);
    });
  }

  buildPreviousPage(order) {
    const dataset = {page: this.previousPage};
    const label = this.labels.previousPage;
    const states = [this.pageStates.previous];
    if (this.previousPage == null) {
      states.push(this.pageStates.disabled);
    }
    // feature => hideAdjacent
    if (this.features.hideAdjacent) {
      states.push(this.pageStates.hide);
    }
    // feature => hideDisabled
    if (this.features.hideDisabled && states.includes(this.pageStates.disabled)) {
      states.push(this.pageStates.hide);
    }
    const page = new Page(order, label, states, dataset);
    this.pages.unshift(page);
  }

  buildNextPage(order) {
    const dataset = {page: this.nextPage};
    const label = this.labels.nextPage;
    const states = [this.pageStates.next];
    if (this.nextPage == null) {
      states.push(this.pageStates.disabled);
    }
    // feature => hideAdjacent
    if (this.features.hideAdjacent) {
      states.push(this.pageStates.hide);
    }
    // feature => hideDisabled
    if (this.features.hideDisabled && states.includes(this.pageStates.disabled)) {
      states.push(this.pageStates.hide);
    }
    const page = new Page(order, label, states, dataset);
    this.pages.push(page);
  }

  buildGaps() {
    const outerEdgeLeft  = this.firstPage + this.options.outerPagesCount;
    const outerEdgeRight = this.lastPage  - this.options.outerPagesCount;
    const innerEdgeLeft  = this.currentPage - this.options.innerPagesCount;
    const innerEdgeRight = this.currentPage + this.options.innerPagesCount;

    const label = this.labels.gapPage;
    const states = [this.pageStates.gap];
    // feature => hideGaps
    if (this.features.hideGaps) {
      states.push(this.pageStates.hide);
    }
    // left side
    if (innerEdgeLeft > outerEdgeLeft) {
      const order = (this.currentPage - this.options.innerPagesCount - 1);
      const page = new Page(order, label, states);
      this.pages.push(page);
    }
    // right side
    if (outerEdgeRight > innerEdgeRight) {
      const order = (this.currentPage + this.options.innerPagesCount);
      const page = new Page(order, label, states);
      this.pages.push(page);
    }
  }

  buildAllPages() {
    const outerEdgeLeft  = this.firstPage + this.options.outerPagesCount;
    const outerEdgeRight = this.lastPage  - this.options.outerPagesCount;
    const innerEdgeLeft  = this.currentPage - this.options.innerPagesCount;
    const innerEdgeRight = this.currentPage + this.options.innerPagesCount;
    for (let i = 1; i <= this.totalPages; i++) {
      if (
        ( i <  outerEdgeLeft || i >  outerEdgeRight ) ||
        ( i >= innerEdgeLeft && i <= innerEdgeRight )
      ) {
        // Determine page class and name
        const states = [];
        let label = i;
        if (i === this.currentPage) {
          label = this.labels.currentPage;
          states.push(this.pageStates.current);
        }
        if (i === this.firstPage) {
          label = this.labels.firstPage;
          states.push(this.pageStates.first);
        }
        if (i === this.lastPage) {
          label = this.labels.lastPage;
          states.push(this.pageStates.last);
        }
        // Push page
        const page = new Page(i, label, states, {page: i});
        this.pages.push(page);
      }
    }
  }

  clearPages() {
    this.pages = [];
  }

  // Generate DOM and populate targeted element
  // TODO use this only for DOM render and another method to build full Pages array
  render(containerSelector) {
    // feature => hideAdjacent
    if (this.features.hideAuto && this.totalPages < 2) {
      return;
    }

    // Find the pagination elements we want to fill in
    const containers = document.querySelectorAll(containerSelector);

    // Create DOM list
    const list = document.createElement('ul');
    // This would contain all list pages

    var self = this; // TODO should be able to remove this when refactoring

    // recalculate in case the user has tampered with the object
    this.calculateTotalPages();   // this.totalPages
    this.calculatePreviousPage(); // this.previousPage
    this.calculateNextPage();     // this.nextPage

    this.clearPages();
    this.buildAllPages();
    this.buildPreviousPage(0); // default to first position
    this.buildNextPage(this.totalPages+1); // default to last position
    this.buildGaps();

    this.sortPages();

    for (let i = 0; i < self.pages.length; i++) {
      list.appendChild(self.pages[i].node);
    }
    // Append the new DOM structure to all matched container
    for (const container of containers) {
      // clear the container for fresh render
      container.innerHTML = '';
      // copy the created nodes
      const listCopy = list.cloneNode(true);
      container.appendChild(listCopy);
      // add event listeners
      const links = container.querySelectorAll('a'); // NB returns a static NodeList, not live/dynamic NodeList, but it's more flexible to work with
      for (const link of links) {
        if (self.callback) {
          link.addEventListener('click', function() {
            const page = parseInt(this.dataset.page); // the clicked page in integer
            self.callback({
              page:       page,
              itemsRange: self.getItemsRange(page)
            });
          });
        } else {
          // TODO same url and attach page variable at the end
          // or just make your own callback that does that
          // if user one is missing; this might be better
        }
      }
    }
  }

  update(containerSelector) {
    // Find the pagination elements we want to fill in
    const containers = document.querySelectorAll(containerSelector);
    // Drop current contents
    for (const container of containers) {
      container.innerHTML = '';
    }
    // re-render
    this.render(containerSelector);
  }

}
<|endoftext|>


<commit_before>/**
 * This widget shows a list of metadata in a given item.
 */
girder.views.MetadataWidget = girder.View.extend({
    modes: {
        simple: {
            editor: function (args) {
                return new girder.views.MetadatumEditWidget(args);
            },
            displayValue: function () {
                return this.value;
            },
            template: girder.templates.metadatumView
        },
        json: {
            editor: function (args) {
                if (args.value) {
                    args.value = JSON.parse(args.value);
                }
                return new girder.views.JsonMetadatumEditWidget(args);
            },
            displayValue: function () {
                return JSON.stringify(this.value, null, 4);
            },
            validation: {
                from: {
                    simple: [
                        function (value) {
                            try {
                                var jsonValue = JSON.parse(value);
                                /* This may succeed when we don't want it to (for instance with the
                                 * value 'false' or '1234'), so check and only switch to JSON if we
                                 * got an object back. */
                                if (jsonValue && typeof jsonValue === 'object' && jsonValue !== null) {
                                    return true;
                                }
                            } catch (e) {}

                            return false;
                        },
                        'The simple field is not valid JSON and can not be converted.'
                    ]
                }
            },
            template: girder.templates.jsonMetadatumView
        }
    },

    events: {
        'click .g-add-json-metadata': function (event) {
            this.addMetadata(event, 'json');
        },
        'click .g-add-simple-metadata': function (event) {
            this.addMetadata(event, 'simple');
        }
    },

    initialize: function (settings) {
        this.item = settings.item;
        this.accessLevel = settings.accessLevel;
        this.item.on('g:changed', function () {
            this.render();
        }, this);
        this.render();
    },

    setItem: function (item) {
        this.item = item;
        return this;
    },

    // Does not support modal editing
    getModeFromValue: function (value) {
        return (typeof value === 'object') ? 'json' : 'simple';
    },

    addMetadata: function (event, mode) {
        var EditWidget = this.modes[mode].editor;
        var newRow = $('
').attr({
            'class': 'g-widget-metadata-row editing'
        }).appendTo(this.$el.find('.g-widget-metadata-container'));

        var widget = new girder.views.MetadatumWidget({
            el: newRow,
            mode: mode,
            key: '',
            value: '',
            item: this.item,
            accessLevel: this.accessLevel,
            girder: girder,
            parentView: this
        });

        var newEditRow = widget.$el.append('

');

        new EditWidget({
            el: newEditRow.find('div'),
            item: this.item,
            key: '',
            value: '',
            accessLevel: this.accessLevel,
            newDatum: true,
            parentView: widget
        }).render();
    },

    render: function () {
        var _this = this;
        var metaDict = this.item.attributes.meta || {};
        var metaKeys = Object.keys(metaDict);
        metaKeys.sort(girder.localeSort);

        // Metadata header
        this.$el.html(girder.templates.metadataWidget({
            item: this.item,
            accessLevel: this.accessLevel,
            girder: girder
        }));

        // Append each metadatum
        _.each(metaKeys, function (metaKey) {
            _this.$el.append(new girder.views.MetadatumWidget({
                mode: _this.getModeFromValue(metaDict[metaKey]),
                key: metaKey,
                value: metaDict[metaKey],
                accessLevel: _this.accessLevel,
                girder: girder,
                parentView: _this
            }).render().$el);
        });

        this.$('.g-widget-metadata-add-button').tooltip({
            container: this.$el,
            placement: 'left',
            animation: false,
            delay: {show: 100}
        });

        return this;
    }
});

girder.views.MetadatumWidget = girder.View.extend({
    events: {
        'click .g-widget-metadata-edit-button': 'editMetadata'
    },

    initialize: function (settings) {
        if (!_.has(this.parentView.modes, settings.mode)) {
            throw 'Unsupported metadatum mode ' + settings.mode + ' detected.';
        }

        this.mode = settings.mode;
        this.key = settings.key;
        this.value = settings.value;
        this.accessLevel = settings.accessLevel;
        this.parentView = settings.parentView;
    },

    _validate: function (to, value) {
        var newMode = this.parentView.modes[to];

        if (_.has(newMode, 'validation') &&
            _.has(newMode.validation, 'from') &&
            _.has(newMode.validation.from, this.mode)) {

            var validate = newMode.validation.from[this.mode][0];
            var msg = newMode.validation.from[this.mode][1];

            if (!validate(value)) {
                girder.events.trigger('g:alert', {
                    text: msg,
                    type: 'warning'
                });
                return false;
            }
        }

        return true;
    },

    // @todo too much duplication with editMetadata
    toggleEditor: function (event, newEditorMode, existingEditor, overrides) {
        if (!this._validate(newEditorMode, (overrides || {}).value || existingEditor.$el.attr('g-value'))) {
            return;
        }

        var row = existingEditor.$el;
        existingEditor.destroy();
        row.addClass('editing').empty();

        var opts = _.extend({
            el: row,
            item: this.parentView.item,
            key: row.attr('g-key'),
            value: row.attr('g-value'),
            accessLevel: this.accessLevel,
            newDatum: false,
            parentView: this
        }, (overrides || {}));

        this.parentView.modes[newEditorMode].editor(opts).render();
    },

    editMetadata: function (event) {
        var row = $(event.currentTarget.parentElement);
        row.addClass('editing').empty();

        var newEditRow = row.append('

');

        var opts = {
            el: newEditRow.find('div'),
            item: this.parentView.item,
            key: row.attr('g-key'),
            value: row.attr('g-value'),
            accessLevel: this.accessLevel,
            newDatum: false,
            parentView: this
        };

        this.parentView.modes[this.mode].editor(opts).render();
    },

    render: function () {
        this.$el.attr({
            'class': 'g-widget-metadata-row',
            'g-key': this.key,
            'g-value': _.bind(this.parentView.modes[this.mode].displayValue, this)()
        }).empty();

        this.$el.html(this.parentView.modes[this.mode].template({
            key: this.key,
            value: _.bind(this.parentView.modes[this.mode].displayValue, this)(),
            accessLevel: this.accessLevel,
            girder: girder
        }));

        return this;
    }
});

girder.views.MetadatumEditWidget = girder.View.extend({
    events: {
        'click .g-widget-metadata-cancel-button': 'cancelEdit',
        'click .g-widget-metadata-save-button': 'save',
        'click .g-widget-metadata-delete-button': 'deleteMetadatum',
        'click .g-widget-metadata-toggle-button': function (event) {
            var editorType;
            // @todo modal
            // in the future this event will have the new editorType (assuming a dropdown)
            if (this instanceof girder.views.JsonMetadatumEditWidget) {
                editorType = 'simple';
            } else {
                editorType = 'json';
            }

            this.parentView.toggleEditor(event, editorType, this, {
                // Save state before toggling editor
                key: this.$el.find('.g-widget-metadata-key-input').val(),
                value: this.getCurrentValue()
            });
        }
    },

    editTemplate: girder.templates.metadatumEditWidget,

    getCurrentValue: function () {
        return this.$el.find('.g-widget-metadata-value-input').val();
    },

    getModeConfig: function (mode) {
        return this.parentView.parentView.modes[mode || this.mode];
    },

    deleteMetadatum: function (event) {
        event.stopImmediatePropagation();
        var metadataList = $(event.currentTarget.parentElement).parent();
        var params = {
            text: 'Are you sure you want to delete the metadatum ' +
                _.escape(this.key) + '?',
            escapedHtml: true,
            yesText: 'Delete',
            confirmCallback: _.bind(function () {
                this.item.removeMetadata(this.key, function () {
                    metadataList.remove();
                });
            }, this)
        };
        girder.confirm(params);
    },

    cancelEdit: function (event) {
        event.stopImmediatePropagation();
        var curRow = $(event.currentTarget.parentElement).parent();
        if (this.newDatum) {
            curRow.remove();
        } else {
            this.parentView.render();
        }
    },

    save: function (event, value) {
        event.stopImmediatePropagation();
        var curRow = $(event.currentTarget.parentElement),
            tempKey = curRow.find('.g-widget-metadata-key-input').val(),
            tempValue = value || curRow.find('.g-widget-metadata-value-input').val();

        if (this.newDatum && tempKey === '') {
            girder.events.trigger('g:alert', {
                text: 'A key is required for all metadata.',
                type: 'warning'
            });
            return;
        }

        var saveCallback = _.bind(function () {
            var mode;
            this.key = tempKey;
            this.value = tempValue;

            this.parentView.key = this.key;
            this.parentView.value = this.value;

            if (this instanceof girder.views.JsonMetadatumEditWidget) {
                this.parentView.mode = 'json';
                mode = 'json';
            } else {
                mode = 'simple';
                this.parentView.mode = 'simple';
            }

            this.parentView.render();

            this.newDatum = false;
        }, this);

        var errorCallback = function (out) {
            girder.events.trigger('g:alert', {
                text: out.message,
                type: 'danger'
            });
        };

        if (this.newDatum) {
            this.item.addMetadata(tempKey, tempValue, saveCallback, errorCallback);
        } else {
            this.item.editMetadata(tempKey, this.key, tempValue, saveCallback, errorCallback);
        }
    },

    initialize: function (settings) {
        this.item = settings.item;
        this.key = settings.key || '';
        this.value = settings.value || '';
        this.accessLevel = settings.accessLevel;
        this.newDatum = settings.newDatum;
    },

    render: function () {
        this.$el.html(this.editTemplate({
            item: this.item,
            key: this.key,
            value: this.value,
            accessLevel: this.accessLevel,
            newDatum: this.newDatum,
            girder: girder
        }));
        this.$el.find('.g-widget-metadata-key-input').focus();

        this.$('[title]').tooltip({
            container: this.$el,
            placement: 'bottom',
            animation: false,
            delay: {show: 100}
        });

        return this;
    }

});

girder.views.JsonMetadatumEditWidget = girder.views.MetadatumEditWidget.extend({
    editTemplate: girder.templates.jsonMetadatumEditWidget,

    getCurrentValue: function () {
        return this.editor.getText();
    },

    save: function (event) {
        try {
            girder.views.MetadatumEditWidget.prototype.save.apply(
                this, [event, this.editor.get()]);
        } catch (err) {
            girder.events.trigger('g:alert', {
                text: 'The field contains invalid JSON and can not be saved.',
                type: 'warning'
            });
        }
    },

    render: function () {
        girder.views.MetadatumEditWidget.prototype.render.apply(this, arguments);

        this.editor = new JSONEditor(this.$el.find('.g-json-editor')[0], {
            mode: 'tree',
            modes: ['code', 'tree'],
            error: function () {
                girder.events.trigger('g:alert', {
                    text: 'The field contains invalid JSON and can not be viewed in Tree Mode.',
                    type: 'warning'
                });
            }
        });

        if (this.value) {
            this.editor.set(this.value);
            this.editor.expandAll();
        }

        return this;
    }
});
<commit_msg>Fixes jsstyle errors.<commit_after>/**
 * This widget shows a list of metadata in a given item.
 */
girder.views.MetadataWidget = girder.View.extend({
    modes: {
        simple: {
            editor: function (args) {
                return new girder.views.MetadatumEditWidget(args);
            },
            displayValue: function () {
                return this.value;
            },
            template: girder.templates.metadatumView
        },
        json: {
            editor: function (args) {
                if (args.value) {
                    args.value = JSON.parse(args.value);
                }
                return new girder.views.JsonMetadatumEditWidget(args);
            },
            displayValue: function () {
                return JSON.stringify(this.value, null, 4);
            },
            validation: {
                from: {
                    simple: [
                        function (value) {
                            try {
                                var jsonValue = JSON.parse(value);
                                /* This may succeed when we don't want it to (for instance with the
                                 * value 'false' or '1234'), so check and only switch to JSON if we
                                 * got an object back. */
                                if (jsonValue && typeof jsonValue === 'object' && jsonValue !== null) {
                                    return true;
                                }
                            } catch (e) {}

                            return false;
                        },
                        'The simple field is not valid JSON and can not be converted.'
                    ]
                }
            },
            template: girder.templates.jsonMetadatumView
        }
    },

    events: {
        'click .g-add-json-metadata': function (event) {
            this.addMetadata(event, 'json');
        },
        'click .g-add-simple-metadata': function (event) {
            this.addMetadata(event, 'simple');
        }
    },

    initialize: function (settings) {
        this.item = settings.item;
        this.accessLevel = settings.accessLevel;
        this.item.on('g:changed', function () {
            this.render();
        }, this);
        this.render();
    },

    setItem: function (item) {
        this.item = item;
        return this;
    },

    // Does not support modal editing
    getModeFromValue: function (value) {
        return (typeof value === 'object') ? 'json' : 'simple';
    },

    addMetadata: function (event, mode) {
        var EditWidget = this.modes[mode].editor;
        var newRow = $('
').attr({
            class: 'g-widget-metadata-row editing'
        }).appendTo(this.$el.find('.g-widget-metadata-container'));

        var widget = new girder.views.MetadatumWidget({
            el: newRow,
            mode: mode,
            key: '',
            value: '',
            item: this.item,
            accessLevel: this.accessLevel,
            girder: girder,
            parentView: this
        });

        var newEditRow = widget.$el.append('

');

        new EditWidget({
            el: newEditRow.find('div'),
            item: this.item,
            key: '',
            value: '',
            accessLevel: this.accessLevel,
            newDatum: true,
            parentView: widget
        }).render();
    },

    render: function () {
        var _this = this;
        var metaDict = this.item.attributes.meta || {};
        var metaKeys = Object.keys(metaDict);
        metaKeys.sort(girder.localeSort);

        // Metadata header
        this.$el.html(girder.templates.metadataWidget({
            item: this.item,
            accessLevel: this.accessLevel,
            girder: girder
        }));

        // Append each metadatum
        _.each(metaKeys, function (metaKey) {
            _this.$el.append(new girder.views.MetadatumWidget({
                mode: _this.getModeFromValue(metaDict[metaKey]),
                key: metaKey,
                value: metaDict[metaKey],
                accessLevel: _this.accessLevel,
                girder: girder,
                parentView: _this
            }).render().$el);
        });

        this.$('.g-widget-metadata-add-button').tooltip({
            container: this.$el,
            placement: 'left',
            animation: false,
            delay: {show: 100}
        });

        return this;
    }
});

girder.views.MetadatumWidget = girder.View.extend({
    events: {
        'click .g-widget-metadata-edit-button': 'editMetadata'
    },

    initialize: function (settings) {
        if (!_.has(this.parentView.modes, settings.mode)) {
            throw 'Unsupported metadatum mode ' + settings.mode + ' detected.';
        }

        this.mode = settings.mode;
        this.key = settings.key;
        this.value = settings.value;
        this.accessLevel = settings.accessLevel;
        this.parentView = settings.parentView;
    },

    _validate: function (to, value) {
        var newMode = this.parentView.modes[to];

        if (_.has(newMode, 'validation') &&
            _.has(newMode.validation, 'from') &&
            _.has(newMode.validation.from, this.mode)) {

            var validate = newMode.validation.from[this.mode][0];
            var msg = newMode.validation.from[this.mode][1];

            if (!validate(value)) {
                girder.events.trigger('g:alert', {
                    text: msg,
                    type: 'warning'
                });
                return false;
            }
        }

        return true;
    },

    // @todo too much duplication with editMetadata
    toggleEditor: function (event, newEditorMode, existingEditor, overrides) {
        if (!this._validate(newEditorMode, (overrides || {}).value || existingEditor.$el.attr('g-value'))) {
            return;
        }

        var row = existingEditor.$el;
        existingEditor.destroy();
        row.addClass('editing').empty();

        var opts = _.extend({
            el: row,
            item: this.parentView.item,
            key: row.attr('g-key'),
            value: row.attr('g-value'),
            accessLevel: this.accessLevel,
            newDatum: false,
            parentView: this
        }, (overrides || {}));

        this.parentView.modes[newEditorMode].editor(opts).render();
    },

    editMetadata: function (event) {
        var row = $(event.currentTarget.parentElement);
        row.addClass('editing').empty();

        var newEditRow = row.append('

');

        var opts = {
            el: newEditRow.find('div'),
            item: this.parentView.item,
            key: row.attr('g-key'),
            value: row.attr('g-value'),
            accessLevel: this.accessLevel,
            newDatum: false,
            parentView: this
        };

        this.parentView.modes[this.mode].editor(opts).render();
    },

    render: function () {
        this.$el.attr({
            class: 'g-widget-metadata-row',
            'g-key': this.key,
            'g-value': _.bind(this.parentView.modes[this.mode].displayValue, this)()
        }).empty();

        this.$el.html(this.parentView.modes[this.mode].template({
            key: this.key,
            value: _.bind(this.parentView.modes[this.mode].displayValue, this)(),
            accessLevel: this.accessLevel,
            girder: girder
        }));

        return this;
    }
});

girder.views.MetadatumEditWidget = girder.View.extend({
    events: {
        'click .g-widget-metadata-cancel-button': 'cancelEdit',
        'click .g-widget-metadata-save-button': 'save',
        'click .g-widget-metadata-delete-button': 'deleteMetadatum',
        'click .g-widget-metadata-toggle-button': function (event) {
            var editorType;
            // @todo modal
            // in the future this event will have the new editorType (assuming a dropdown)
            if (this instanceof girder.views.JsonMetadatumEditWidget) {
                editorType = 'simple';
            } else {
                editorType = 'json';
            }

            this.parentView.toggleEditor(event, editorType, this, {
                // Save state before toggling editor
                key: this.$el.find('.g-widget-metadata-key-input').val(),
                value: this.getCurrentValue()
            });
        }
    },

    editTemplate: girder.templates.metadatumEditWidget,

    getCurrentValue: function () {
        return this.$el.find('.g-widget-metadata-value-input').val();
    },

    getModeConfig: function (mode) {
        return this.parentView.parentView.modes[mode || this.mode];
    },

    deleteMetadatum: function (event) {
        event.stopImmediatePropagation();
        var metadataList = $(event.currentTarget.parentElement).parent();
        var params = {
            text: 'Are you sure you want to delete the metadatum ' +
                _.escape(this.key) + '?',
            escapedHtml: true,
            yesText: 'Delete',
            confirmCallback: _.bind(function () {
                this.item.removeMetadata(this.key, function () {
                    metadataList.remove();
                });
            }, this)
        };
        girder.confirm(params);
    },

    cancelEdit: function (event) {
        event.stopImmediatePropagation();
        var curRow = $(event.currentTarget.parentElement).parent();
        if (this.newDatum) {
            curRow.remove();
        } else {
            this.parentView.render();
        }
    },

    save: function (event, value) {
        event.stopImmediatePropagation();
        var curRow = $(event.currentTarget.parentElement),
            tempKey = curRow.find('.g-widget-metadata-key-input').val(),
            tempValue = value || curRow.find('.g-widget-metadata-value-input').val();

        if (this.newDatum && tempKey === '') {
            girder.events.trigger('g:alert', {
                text: 'A key is required for all metadata.',
                type: 'warning'
            });
            return;
        }

        var saveCallback = _.bind(function () {
            var mode;
            this.key = tempKey;
            this.value = tempValue;

            this.parentView.key = this.key;
            this.parentView.value = this.value;

            if (this instanceof girder.views.JsonMetadatumEditWidget) {
                this.parentView.mode = 'json';
                mode = 'json';
            } else {
                mode = 'simple';
                this.parentView.mode = 'simple';
            }

            this.parentView.render();

            this.newDatum = false;
        }, this);

        var errorCallback = function (out) {
            girder.events.trigger('g:alert', {
                text: out.message,
                type: 'danger'
            });
        };

        if (this.newDatum) {
            this.item.addMetadata(tempKey, tempValue, saveCallback, errorCallback);
        } else {
            this.item.editMetadata(tempKey, this.key, tempValue, saveCallback, errorCallback);
        }
    },

    initialize: function (settings) {
        this.item = settings.item;
        this.key = settings.key || '';
        this.value = settings.value || '';
        this.accessLevel = settings.accessLevel;
        this.newDatum = settings.newDatum;
    },

    render: function () {
        this.$el.html(this.editTemplate({
            item: this.item,
            key: this.key,
            value: this.value,
            accessLevel: this.accessLevel,
            newDatum: this.newDatum,
            girder: girder
        }));
        this.$el.find('.g-widget-metadata-key-input').focus();

        this.$('[title]').tooltip({
            container: this.$el,
            placement: 'bottom',
            animation: false,
            delay: {show: 100}
        });

        return this;
    }

});

girder.views.JsonMetadatumEditWidget = girder.views.MetadatumEditWidget.extend({
    editTemplate: girder.templates.jsonMetadatumEditWidget,

    getCurrentValue: function () {
        return this.editor.getText();
    },

    save: function (event) {
        try {
            girder.views.MetadatumEditWidget.prototype.save.apply(
                this, [event, this.editor.get()]);
        } catch (err) {
            girder.events.trigger('g:alert', {
                text: 'The field contains invalid JSON and can not be saved.',
                type: 'warning'
            });
        }
    },

    render: function () {
        girder.views.MetadatumEditWidget.prototype.render.apply(this, arguments);

        this.editor = new JSONEditor(this.$el.find('.g-json-editor')[0], {
            mode: 'tree',
            modes: ['code', 'tree'],
            error: function () {
                girder.events.trigger('g:alert', {
                    text: 'The field contains invalid JSON and can not be viewed in Tree Mode.',
                    type: 'warning'
                });
            }
        });

        if (this.value) {
            this.editor.set(this.value);
            this.editor.expandAll();
        }

        return this;
    }
});
<|endoftext|>


<commit_before>import { Interpreter, Variable, Func, Application } from './lib/lambda'

describe('interpreter', () => {

  let interpreter;

  beforeEach(() => {
    interpreter = new Interpreter();
  });

  const test = (fixtures) =>
    fixtures.forEach(([expr, expected, comment = '']) => {
      it(`should interpret ${expr} ${comment}`, () => {
        expect(parser.parse(expr)).toEqual(expected);
      });
    })

  describe('β-normal form: no further β-reduction is possible', () => {

    test([
      [
        new Variable('x'),
        'x',
        'variable'
      ],
      [
        new Func('x',
          new Variable('y')
        ),
        '(λx.y)', 'function'
      ],
      [
        new Application(
          new Variable('x'),
          new Variable('y')
        ),
        'xy', 'application'
      ]
    ]);
  });

  //TODO: One β-reduction remaining
  //TODO: Several β-reductions
  //TODO: Several pathes to do β-reduction
  //TODO: Variable name overshadows a variable from an enclosing context: α-reduction is needed to rename the variables
  //TODO: Evaluate f(Yf):  Y-combinator applied to a function, should be a fixed point
});<commit_msg>Fix the broken tests.<commit_after>import { Interpreter, Variable, Func, Application } from './lib/lambda'

describe('interpreter', () => {

  let interpreter;

  beforeEach(() => {
    interpreter = new Interpreter();
  });

  const test = (fixtures) =>
    fixtures.forEach(([expr, expected, comment = '']) => {
      it(`should interpret ${expr} ${comment}`, () => {
        expect(interpreter.eval(expr).toString()).toEqual(expected);
      });
    })

  describe('β-normal form: no further β-reduction is possible', () => {

    test([
      [
        new Variable('x'),
        'x',
        'variable'
      ],
      [
        new Func('x',
          new Variable('y')
        ),
        '(λx.y)', 'function'
      ],
      [
        new Application(
          new Variable('x'),
          new Variable('y')
        ),
        '(xy)', 'application'
      ]
    ]);
  });

  //TODO: One β-reduction remaining
  //TODO: Several β-reductions
  //TODO: Several pathes to do β-reduction
  //TODO: Variable name overshadows a variable from an enclosing context: α-reduction is needed to rename the variables
  //TODO: Evaluate f(Yf):  Y-combinator applied to a function, should be a fixed point
});<|endoftext|>


<commit_before>/*globals LoadChartHelper,LoadChartsSettings,nv,localSettings*/
var LoadChart = (function () {
    'use strict';

    var chartLengths = {
        long: 35040,
        short: 8760
    };

    function formatDateFromFrame(frame) {
        var multiplier = -1,
            len = this.data[0].values.length;

        if (len === chartLengths.long) {
            multiplier = 900000;
        } else if (len === chartLengths.short) {
            multiplier = 3600000;
        }

        if (multiplier === -1) {
            return frame;
        } else {
            return LoadChartHelper.formatDate(new Date(frame * multiplier));
        }
    }

    function loadChartLocation() {
        return parseInt(this.intoSelector.replace(/\D/g, ''), 10);
    }

    function createChart() {
        var chart = nv.models.lineWithFocusChart();
        chart.options({
            duration: 0,
            transitionDuration: 0,
            interpolate: 'linear',
            forceY: [0.0]
        });
        chart.useVoronoi(false);
        chart.lines.duration(0);
        chart.lines2.duration(0);
        chart.lines2.forceY([0.0]);
        chart.lines.interpolate('step-after');
        chart.lines2.interpolate('step-after');
        chart.xAxis.tickFormat(formatDateFromFrame.bind(this));
        chart.x2Axis.axisLabel("Time").tickFormat(formatDateFromFrame.bind(this));

        chart.yAxis.axisLabel(this.axisLabels[this.curve_type])
            .axisLabelDistance(0).tickFormat(d3.format(',.3r'));

        chart.y2Axis.axisLabel(this.axisLabels[this.curve_type])
            .axisLabelDistance(0).tickFormat(d3.format(',.3r'));

        chart.brush.on('brushend', this.setGlobalBrushFocus);

        LoadChartHelper.charts[loadChartLocation.call(this) - 1] = chart;
        LoadChartHelper.updateBrush(loadChartLocation.call(this));

        $("g.tick.zero text").text("0.00");

        return chart;
    }

    function addGraph(data) {
        var chart = createChart.call(this);
        d3.select(this.intoSelector).datum(data).call(chart);
        chart.update();
        LoadChartHelper.toggleCapacity(loadChartLocation.call(this));
    }

    function generateCapacity(data) {
        var total,
            top_line = data[0].values.map(function (sample) {
                return { x: sample.x, y: this.capacity };
            }.bind(this)),
            bottom_line = data[0].values.map(function (sample) {
                return { x: sample.x, y: this.capacity * -1 };
            }.bind(this));

        bottom_line.reverse().unshift({
            x: bottom_line[0].x + 1,
            y: bottom_line[0].y
        });

        total = bottom_line.concat(top_line);

        return {
            key: "Capacity",
            type: "capacity",
            color: LoadChartsSettings.capacity.color,
            values: total,
            disabled: !LoadChartsSettings.capacity.enabled
        };
    }

    function sampledData(loads) {
        var chunkSize, endAt, startAt, zeroWeek;

        if (LoadChartHelper.currentWeek && LoadChartHelper.currentWeek !== 0) {
            chunkSize = Math.floor(loads.length / 52);
            zeroWeek  = LoadChartHelper.currentWeek - 1;
            startAt   = zeroWeek * chunkSize;
            endAt     = startAt + chunkSize;

            loads     = loads.slice(startAt, endAt);
        }

        return loads.map(function (y, x) {
            return { x: x, y: y };
        });
    }

    function renderChart() {
        $(this.intoSelector).empty();

        var results = [];

        this.data.forEach(function (datum) {
            var color = (
                LoadChartsSettings[datum.type || this.curve_type] ||
                LoadChartsSettings['default']
            ).color;

            if (datum.values) {
                results.push({
                    key:    datum.name,
                    type:   datum.type,
                    values: sampledData.call(this, datum.values),
                    area:   datum.area,
                    color:  color
                });
            }
        }.bind(this));

        if (this.capacity) {
            results.push(generateCapacity.call(this, results));
        }

        // Add an extra data point to make the "step-after" smoothing fit better
        // (otherwise it appears that the last frame is not present).
        results.forEach(function (datum) {
            var last = datum.values[datum.values.length - 1];
            if (last) {
                datum.values.push({ x: last.x + 1, y: last.y });
            }
        });

        nv.addGraph(addGraph.bind(this, results));
    }

    function selectDatePortion(dateEl) {
        var value = parseInt(dateEl.val(), 10);

        LoadChartHelper.currentWeek = value;
        LoadChartHelper.forceReload = true;
        LoadChartHelper.clearBrush();

        $("select[name=date-select]").val(value);

        // Load large chart
        if (window.currentTree.resolution === 'high' && value !== 0) {
            renderChart.call(this, value);
        } else if (value === 0) {
            window.currentTree.lesses[1].strategies.toggleLoading();
            window.currentTree.set('low').update();
        } else {
            window.currentTree.lesses[1].strategies.toggleLoading();
            window.currentTree.set('high').update();
        }
    }

    function drawDateSelect() {
        var endWeek, optionEl, startWeek, week, i,
            epoch = new Date(0),
            msInWeek = 604800000,
            dateEl = $('<select name="date-select" class="form-control" style="max-width: 300px"></select>');

        dateEl.append($('<option value="0">Whole year</option>'));

        for (week = 0; week < 52; week++) {
            startWeek = new Date(epoch.getDate() + (msInWeek * week));
            endWeek = new Date(startWeek.getDate() + (msInWeek * week) + msInWeek - (msInWeek / 7));
            if (week === 51) {
                endWeek = new Date(endWeek.getDate() - 1000);
            }
            optionEl = $("<option value='" + (week + 1) + "'></option>");
            optionEl.text((LoadChartHelper.formatDate(startWeek)) + " - " + (LoadChartHelper.formatDate(endWeek)));
            dateEl.append(optionEl);
        }

        dateEl.change(selectDatePortion.bind(this, dateEl));

        $(this.intoSelector).after(dateEl);

        if (LoadChartHelper.currentWeek) {
            return $("select[name=date-select]").val(LoadChartHelper.currentWeek);
        }
    }

    LoadChart.prototype = {
        intoSelector: null,
        axisLabels: {
            "default": 'kW',
            flex: 'kW',
            inflex: 'kW',
            use: '',
            availability: '',
            price_curve: '€',
            behavior_profile: 'On/Off'
        },

        render: function (intoSelector, week) {
            if (week === null) {
                week = 0;
            }

            this.intoSelector = intoSelector;
            renderChart.call(this, week);
            drawDateSelect.call(this);
        },

        setGlobalBrushFocus: function () {
            LoadChartHelper.globalBrushExtent = d3.event.target.extent();

            localSettings.set('global_brush_extent', d3.event.target.extent());
        }
    };

    function LoadChart(data, capacity, curve_type) {
        this.data       = data;
        this.capacity   = capacity;
        this.curve_type = curve_type;
    }

    return LoadChart;
}());

window.LoadChart = LoadChart;
<commit_msg>Fix error for localSettings<commit_after>/*globals LoadChartHelper,LoadChartsSettings,nv,localSettings*/
var LoadChart = (function () {
    'use strict';

    var chartLengths = {
        long: 35040,
        short: 8760
    };

    function formatDateFromFrame(frame) {
        var multiplier = -1,
            len = this.data[0].values.length;

        if (len === chartLengths.long) {
            multiplier = 900000;
        } else if (len === chartLengths.short) {
            multiplier = 3600000;
        }

        if (multiplier === -1) {
            return frame;
        } else {
            return LoadChartHelper.formatDate(new Date(frame * multiplier));
        }
    }

    function loadChartLocation() {
        return parseInt(this.intoSelector.replace(/\D/g, ''), 10);
    }

    function createChart() {
        var chart = nv.models.lineWithFocusChart();
        chart.options({
            duration: 0,
            transitionDuration: 0,
            interpolate: 'linear',
            forceY: [0.0]
        });
        chart.useVoronoi(false);
        chart.lines.duration(0);
        chart.lines2.duration(0);
        chart.lines2.forceY([0.0]);
        chart.lines.interpolate('step-after');
        chart.lines2.interpolate('step-after');
        chart.xAxis.tickFormat(formatDateFromFrame.bind(this));
        chart.x2Axis.axisLabel("Time").tickFormat(formatDateFromFrame.bind(this));

        chart.yAxis.axisLabel(this.axisLabels[this.curve_type])
            .axisLabelDistance(0).tickFormat(d3.format(',.3r'));

        chart.y2Axis.axisLabel(this.axisLabels[this.curve_type])
            .axisLabelDistance(0).tickFormat(d3.format(',.3r'));

        chart.brush.on('brushend', this.setGlobalBrushFocus);

        LoadChartHelper.charts[loadChartLocation.call(this) - 1] = chart;
        LoadChartHelper.updateBrush(loadChartLocation.call(this));

        $("g.tick.zero text").text("0.00");

        return chart;
    }

    function addGraph(data) {
        var chart = createChart.call(this);
        d3.select(this.intoSelector).datum(data).call(chart);
        chart.update();
        LoadChartHelper.toggleCapacity(loadChartLocation.call(this));
    }

    function generateCapacity(data) {
        var total,
            top_line = data[0].values.map(function (sample) {
                return { x: sample.x, y: this.capacity };
            }.bind(this)),
            bottom_line = data[0].values.map(function (sample) {
                return { x: sample.x, y: this.capacity * -1 };
            }.bind(this));

        bottom_line.reverse().unshift({
            x: bottom_line[0].x + 1,
            y: bottom_line[0].y
        });

        total = bottom_line.concat(top_line);

        return {
            key: "Capacity",
            type: "capacity",
            color: LoadChartsSettings.capacity.color,
            values: total,
            disabled: !LoadChartsSettings.capacity.enabled
        };
    }

    function sampledData(loads) {
        var chunkSize, endAt, startAt, zeroWeek;

        if (LoadChartHelper.currentWeek && LoadChartHelper.currentWeek !== 0) {
            chunkSize = Math.floor(loads.length / 52);
            zeroWeek  = LoadChartHelper.currentWeek - 1;
            startAt   = zeroWeek * chunkSize;
            endAt     = startAt + chunkSize;

            loads     = loads.slice(startAt, endAt);
        }

        return loads.map(function (y, x) {
            return { x: x, y: y };
        });
    }

    function renderChart() {
        $(this.intoSelector).empty();

        var results = [];

        this.data.forEach(function (datum) {
            var color = (
                LoadChartsSettings[datum.type || this.curve_type] ||
                LoadChartsSettings['default']
            ).color;

            if (datum.values) {
                results.push({
                    key:    datum.name,
                    type:   datum.type,
                    values: sampledData.call(this, datum.values),
                    area:   datum.area,
                    color:  color
                });
            }
        }.bind(this));

        if (this.capacity) {
            results.push(generateCapacity.call(this, results));
        }

        // Add an extra data point to make the "step-after" smoothing fit better
        // (otherwise it appears that the last frame is not present).
        results.forEach(function (datum) {
            var last = datum.values[datum.values.length - 1];
            if (last) {
                datum.values.push({ x: last.x + 1, y: last.y });
            }
        });

        nv.addGraph(addGraph.bind(this, results));
    }

    function selectDatePortion(dateEl) {
        var value = parseInt(dateEl.val(), 10);

        LoadChartHelper.currentWeek = value;
        LoadChartHelper.forceReload = true;
        LoadChartHelper.clearBrush();

        $("select[name=date-select]").val(value);

        // Load large chart
        if (window.currentTree.resolution === 'high' && value !== 0) {
            renderChart.call(this, value);
        } else if (value === 0) {
            window.currentTree.lesses[1].strategies.toggleLoading();
            window.currentTree.set('low').update();
        } else {
            window.currentTree.lesses[1].strategies.toggleLoading();
            window.currentTree.set('high').update();
        }
    }

    function drawDateSelect() {
        var endWeek, optionEl, startWeek, week, i,
            epoch = new Date(0),
            msInWeek = 604800000,
            dateEl = $('<select name="date-select" class="form-control" style="max-width: 300px"></select>');

        dateEl.append($('<option value="0">Whole year</option>'));

        for (week = 0; week < 52; week++) {
            startWeek = new Date(epoch.getDate() + (msInWeek * week));
            endWeek = new Date(startWeek.getDate() + (msInWeek * week) + msInWeek - (msInWeek / 7));
            if (week === 51) {
                endWeek = new Date(endWeek.getDate() - 1000);
            }
            optionEl = $("<option value='" + (week + 1) + "'></option>");
            optionEl.text((LoadChartHelper.formatDate(startWeek)) + " - " + (LoadChartHelper.formatDate(endWeek)));
            dateEl.append(optionEl);
        }

        dateEl.change(selectDatePortion.bind(this, dateEl));

        $(this.intoSelector).after(dateEl);

        if (LoadChartHelper.currentWeek) {
            return $("select[name=date-select]").val(LoadChartHelper.currentWeek);
        }
    }

    LoadChart.prototype = {
        intoSelector: null,
        axisLabels: {
            "default": 'kW',
            flex: 'kW',
            inflex: 'kW',
            use: '',
            availability: '',
            price_curve: '€',
            behavior_profile: 'On/Off'
        },

        render: function (intoSelector, week) {
            if (week === null) {
                week = 0;
            }

            this.intoSelector = intoSelector;
            renderChart.call(this, week);
            drawDateSelect.call(this);
        },

        setGlobalBrushFocus: function () {
            LoadChartHelper.globalBrushExtent = d3.event.target.extent();

            if (typeof(localSettings) !== "undefined") {
                localSettings.set('global_brush_extent', d3.event.target.extent());
            }
        }
    };

    function LoadChart(data, capacity, curve_type) {
        this.data       = data;
        this.capacity   = capacity;
        this.curve_type = curve_type;
    }

    return LoadChart;
}());

window.LoadChart = LoadChart;
<|endoftext|>


<commit_before>const m = require('mithril')
import session from '../../session'
import Post from '../../model/Post'

const Postform = {
  oninit: () => {
    Post.loadDraft()
  },

  createPost: ()  => {
    if (Post.draft != "") {
      Post.create(Post.draft)
    }
  },

  view: () => {
    return [
      m('header.text-center', m('h4', "Share a link! Help other dev learn!")),
      m('div.input-group', [
        m('input.form-input.input-lg', {
          type: 'url', placeholder: 'http://awesome.link/', value: Post.draft,
          oninput: m.withAttr("value", function(value) {Post.draft = value})
        }),
        m('button.btn.btn-primary.btn-action.btn-lg' + (Post.postStatus == "posting" ? ".loading" : ''), {onclick: (e) => Post.create(Post.draft)}, m('i.icon.icon-plus'))
      ]),
      m('footer.text-center', [
        m('h4', 'Or share with our bot'),
        m('p', [
          m('a.btn.btn-lg', {href: 'https://telegram.me/BetterdevBot'},'Telegram'),
          //' ',
          //m('a.btn.btn-lg', {href: "https://slack.com/oauth/authorize?&client_id=73737491668.207721165366&scope=bot,incoming-webhook,chat:write:bot,links:read"}, 'Slack')
        ])
      ])
    ]
  }
}

const LoginRemind = {
  view: () => {
    return m('header.text-center', [
      m('h4', "Login to share links! Help people learn!"),
      m('a.btn.btn-primary', {onclick: session.login}, 'Login')
    ])
  }
}

const PostformView = {
  view: () => {
    return session.isSignedIn() ? m(Postform) : m(LoginRemind)
  }
}

export { PostformView as default }
<commit_msg>Add slack button<commit_after>const m = require('mithril')
import session from '../../session'
import Post from '../../model/Post'

const Postform = {
  oninit: () => {
    Post.loadDraft()
  },

  createPost: ()  => {
    if (Post.draft != "") {
      Post.create(Post.draft)
    }
  },

  view: () => {
    return [
      m('header.text-center', m('h4', "Share a link! Help other dev learn!")),
      m('div.input-group', [
        m('input.form-input.input-lg', {
          type: 'url', placeholder: 'http://awesome.link/', value: Post.draft,
          oninput: m.withAttr("value", function(value) {Post.draft = value})
        }),
        m('button.btn.btn-primary.btn-action.btn-lg' + (Post.postStatus == "posting" ? ".loading" : ''), {onclick: (e) => Post.create(Post.draft)}, m('i.icon.icon-plus'))
      ]),
      m('footer.text-center', [
        m('h4', 'Or share with our bot'),
        m('p', [
          m('a.btn.btn-lg', {href: 'https://telegram.me/BetterdevBot'},'Telegram'),
          ' ',
          m('a.btn.btn-lg', {href: "https://slack.com/oauth/authorize?&client_id=73737491668.207721165366&scope=bot,chat:write:bot,links:read,incoming-webhook&redirect_uri=http://127.0.0.1:4000/bot/slack"}, 'Slack')
        ])
      ])
    ]
  }
}

const LoginRemind = {
  view: () => {
    return m('header.text-center', [
      m('h4', "Login to share links! Help people learn!"),
      m('a.btn.btn-primary', {onclick: session.login}, 'Login')
    ])
  }
}

const PostformView = {
  view: () => {
    return session.isSignedIn() ? m(Postform) : m(LoginRemind)
  }
}

export { PostformView as default }
<|endoftext|>


<commit_before>/*
 * Copyright 2019 Google LLC

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *  https://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {TripsLayer} from '@deck.gl/geo-layers';
import * as GoogleMapsClient from '@google/maps';

export class TripsLayerExample {
  constructor() {}
  static *getLayers(google_map) {
    const builder = new TripsBuilder();
    let trips = builder.getTrips(google_map, this.getMapOptions());

    // Limit how long the demo runs
    for (let current_time = 0; current_time <= 1000; current_time++) {
      yield [
        new TripsLayer({
          id: 'trips-layer',
          data: trips,
          getPath: d => d.segments,
          getColor: d => d.mode === 'driving' ? [239, 126, 35] : [85, 181, 238],
          opacity: 0.6,
          widthMinPixels: 2,
          rounded: true,
          trailLength: 50,
          currentTime: current_time
        })
      ]
    }
  }

  static getMapOptions() {
    return {
      center: {lat: 40.757870, lng: -73.985625},
      zoom: 16
    }
  }
  static getMetadata() {
    return {
      name: 'Trips Layer',
      thumbnail: 'trips.png'
    }
  }
}

class TripsBuilder {
  constructor() {}

  // Builds trips using Places Library for endpoints and Directions Service for segments
  async getTrips(google_map, map_options) {
    let trips = [];
    let places = await this.getPlaces(google_map.api, google_map.map, map_options.center);

    // Build the set of trips and get directions between endpoints
    places.forEach(async (place, index) => {
      const origin = [
        place.geometry.location.lat(),
        place.geometry.location.lng()
      ];
      for (let j = index + 1; j < places.length; j++) {
        const dest = [
          places[j].geometry.location.lat(),
          places[j].geometry.location.lng()
        ];
        const trip = this.getDirections(google_map.api, origin, dest);
        trips.push(trip);
      }
    });
    trips = await Promise.all(trips);
    return trips;
  }

  // Gets Places around the map center to use as trip endpoints
  async getPlaces(api, map, center) {
    const places_service = new api.places.PlacesService(map);
    const OPTIONS = {
      location: new api.LatLng(center),
      radius: '2000',
      type: ['restaurant']
    };
    const places_request = new Promise((resolve, reject) => {
      places_service.nearbySearch(OPTIONS, (res, status) => {
        if (status === 'OK') {
          resolve(res);
        }
        reject(status);
      });
    });
    const places = await places_request;
    return places;
  }

  // Gets Directions to define the trip route
  async getDirections (api, origin, dest) {
    const base_uri = 'http://localhost:1337/directions';
    const qs = `origin_lat=${origin[0]}&origin_lng=${origin[1]}
                &dest_lat=${dest[0]}&dest_lng=${dest[1]}`;
    let request = fetch(`${base_uri}?${qs}`)
    let response = await request;
    response = await response.json();
    return response;
    // const directionsService = new api.DirectionsService();
    // let directions = new Promise((resolve, reject) => {
    //   directionsService.route(OPTIONS, (response, status) => {
    //     if (status === 'OK') {
    //       let duration = response.routes[0].legs[0].duration.value;
    //       let route = response.routes[0].legs[0].steps;
    //       route = this.formatRoute(route);
    //       resolve(route);
    //     }
    //     // Handle rate limiting from Directions Service if we hit it
    //     if (status === 'OVER_QUERY_LIMIT') {
    //       this.getRoute(directionsService, OPTIONS);
    //     }
    //   });
    // });
    // return directions;
  }

  // Formats Directions Service response to be TripsLayer-friendly
  // formatRoute(route) {
  //   let timestamp = 0;
  //   route = route.map(step => {
  //     let formatted_step = [
  //         step.start_location.lng(),
  //         step.start_location.lat(),
  //         timestamp
  //     ]
  //     timestamp += step.duration.value
  //     return formatted_step;
  //   });
  //   return route;
  // }
}<commit_msg>update trips layer location<commit_after>/*
 * Copyright 2019 Google LLC

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at

 *  https://www.apache.org/licenses/LICENSE-2.0

 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {TripsLayer} from '@deck.gl/geo-layers';
import * as GoogleMapsClient from '@google/maps';

export class TripsLayerExample {
  constructor() {}
  static *getLayers(google_map) {
    const builder = new TripsBuilder();
    let trips = builder.getTrips(google_map, this.getMapOptions());

    // Limit how long the demo runs
    for (let current_time = 0; current_time <= 1000; current_time++) {
      yield [
        new TripsLayer({
          id: 'trips-layer',
          data: trips,
          getPath: d => d.segments,
          getColor: d => d.mode === 'driving' ? [239, 126, 35] : [85, 181, 238],
          opacity: 0.6,
          widthMinPixels: 2,
          rounded: true,
          trailLength: 75,
          currentTime: current_time
        })
      ]
    }
  }

  static getMapOptions() {
    return {
      center: {lat: 37.791250, lng: -122.407463},
      zoom: 16
    }
  }
  static getMetadata() {
    return {
      name: 'Trips Layer',
      thumbnail: 'trips.png'
    }
  }
}

class TripsBuilder {
  constructor() {}

  // Builds trips using Places Library for endpoints and Directions Service for segments
  async getTrips(google_map, map_options) {
    let trips = [];
    let places = await this.getPlaces(google_map.api, google_map.map, map_options.center);

    // Build the set of trips and get directions between endpoints
    places.forEach(async (place, index) => {
      const origin = [
        place.geometry.location.lat(),
        place.geometry.location.lng()
      ];
      for (let j = index + 1; j < places.length; j++) {
        const dest = [
          places[j].geometry.location.lat(),
          places[j].geometry.location.lng()
        ];
        const trip = this.getDirections(google_map.api, origin, dest);
        trips.push(trip);
      }
    });
    trips = await Promise.all(trips);
    return trips;
  }

  // Gets Places around the map center to use as trip endpoints
  async getPlaces(api, map, center) {
    const places_service = new api.places.PlacesService(map);
    const OPTIONS = {
      location: new api.LatLng(center),
      radius: '2000',
      type: ['restaurant']
    };
    const places_request = new Promise((resolve, reject) => {
      places_service.nearbySearch(OPTIONS, (res, status) => {
        if (status === 'OK') {
          resolve(res);
        }
        reject(status);
      });
    });
    const places = await places_request;
    return places;
  }

  // Gets Directions to define the trip route
  async getDirections (api, origin, dest) {
    const base_uri = 'http://localhost:1337/directions';
    const qs = `origin_lat=${origin[0]}&origin_lng=${origin[1]}
                &dest_lat=${dest[0]}&dest_lng=${dest[1]}`;
    let request = fetch(`${base_uri}?${qs}`)
    let response = await request;
    response = await response.json();
    return response;
    // const directionsService = new api.DirectionsService();
    // let directions = new Promise((resolve, reject) => {
    //   directionsService.route(OPTIONS, (response, status) => {
    //     if (status === 'OK') {
    //       let duration = response.routes[0].legs[0].duration.value;
    //       let route = response.routes[0].legs[0].steps;
    //       route = this.formatRoute(route);
    //       resolve(route);
    //     }
    //     // Handle rate limiting from Directions Service if we hit it
    //     if (status === 'OVER_QUERY_LIMIT') {
    //       this.getRoute(directionsService, OPTIONS);
    //     }
    //   });
    // });
    // return directions;
  }

  // Formats Directions Service response to be TripsLayer-friendly
  // formatRoute(route) {
  //   let timestamp = 0;
  //   route = route.map(step => {
  //     let formatted_step = [
  //         step.start_location.lng(),
  //         step.start_location.lat(),
  //         timestamp
  //     ]
  //     timestamp += step.duration.value
  //     return formatted_step;
  //   });
  //   return route;
  // }
}<|endoftext|>