<commit_before>import 'jest';
import { create, reveal } from 'microstates';
import logTree from '../src/utils/log-tree';

describe('type-shifting', () => {

  class Shape {
    static create({ a, b, c } = {}) {
      if (a && b && c) {
        return create(Triangle, { a, b, c });
      }
      if (a && b) {
        return create(Angle, { a, b });
      }
      if (a) {
        return create(Line, { a });
      }
    }
  }

  class Line extends Shape {
    a = Number;
    add(b) {
      let { a } = this.state;
      return create(Angle, { a, b });
    }
  }

  class Angle extends Line {
    a = Number;
    b = Number;
    add(c) {
      let { a, b } = this.state;
      return create(Triangle, { a, b, c });
    }
  }

  class Triangle extends Angle {
    c = Number;
  }

  class Glass {
    shape = Shape;
  }

  describe('create', function() {

    it('can initialize to itself', () => {
      let shape = create(Shape);
      expect(shape.state).toBeInstanceOf(Shape);
    });

    it('initializes to first type', () => {
      let triangle = Shape.create({a: 10, b: 20, c: 30 });
      expect(triangle.state).toBeInstanceOf(Triangle);
      expect(triangle.state).toMatchObject({
        a: 10,
        b: 20,
        c: 30
      });
    });

    it('initializes to second type', () => {
      let angle = Shape.create({a: 10, b: 20 });
      expect(angle.state).toBeInstanceOf(Angle);
      expect(angle.state).toMatchObject({
        a: 10,
        b: 20,
      });
    });

    it(`can be initialized from descendant's create`, function() {
      let line = Line.create({ a: 10 });
      expect(line.state).toBeInstanceOf(Line);
      expect(line.state).toMatchObject({
        a: 10
      });
    });

    it('is used to initialize composed object', function() {
      let composed = create(Glass, { shape : {a: 10, b: 20, c: 30 }});
      expect(composed.state.shape).toBeInstanceOf(Triangle);
      expect(composed.state).toMatchObject({
        shape: {
          a: 10,
          b: 20,
          c: 30
        }
      });
    });

    it('supports being initialized in parameterized arrays', () => {
      class Drawing {
        shapes = [Shape]
      }
      let drawing = create(Drawing, { shapes: [ { a: 10 }, { a: 20, b: 30 }, { a: 100, b: 200, c: 300 }]});
      expect(drawing.state.shapes[0]).toBeInstanceOf(Line);
      expect(drawing.state.shapes[1]).toBeInstanceOf(Angle);
      expect(drawing.state.shapes[2]).toBeInstanceOf(Triangle);
    });

    describe('can type-shift into a parameterized type', () => {
      class Container {
        static create(content) {
          if (Array.isArray(content)) {
            return create([String], content);
          } else {
            return create({String}, content);
          }
        }
      }
      it('can initialize into a parameterized array', () => {
        let array = Container.create(['a', 'b', 'c']);
        expect(array.state).toMatchObject(['a', 'b', 'c']);
        expect(array[0].concat).toBeInstanceOf(Function);
      });
      it('can initialize into a parameterized object', () => {
        let object = Container.create({a: 'A', b: 'B', c: 'C'});
        expect(object.state).toMatchObject({a: 'A', b: 'B', c: 'C'});
        expect(object.a.concat).toBeInstanceOf(Function);
      });
    });

  });

  describe('transitions', function() {
    let ms = create(Line);
    let line, corner, triangle;
    beforeEach(() => {
      line = ms.a.set(10);
      corner = line.add(20);
      triangle = corner.add(30);
    });
    it('constructs a line', () => {
      expect(line.state).toBeInstanceOf(Line);
      expect(line.state).toMatchObject({
        a: 10,
      });
      expect(line.valueOf()).toEqual({ a: 10 });
    });
    it('constructs a Corner', () => {
      expect(corner.state).toBeInstanceOf(Angle);
      expect(corner.state).toMatchObject({
        a: 10,
        b: 20,
      });
      expect(corner.valueOf()).toEqual({ a: 10, b: 20 });
    });
    it('constructs a Triangle', () => {
      expect(triangle.state).toBeInstanceOf(Triangle);
      expect(triangle.state).toMatchObject({
        a: 10,
        b: 20,
        c: 30,
      });
      expect(triangle.valueOf()).toEqual({ a: 10, b: 20, c: 30 });
    });
    it('can be done down tree', () => {
      let string = create(String, '100');
      let cString = triangle.c.set(string)
      expect(cString.state.c).toBe('100');
      expect(cString.c.concat).toBeDefined();
    });
  });
});

describe('type-shifting with constant values', () => {
  class Async {
    content = null;
    isLoaded = false;
    isLoading = false;
    isError = false;

    loading() {
      return create(AsyncLoading);
    }
  }

  class AsyncError extends Async {
    isError = true;
    isLoading = false;
    isLoaded = true;
  }

  class AsyncLoading extends Async {
    isLoading = true;

    loaded(content) {
      return create(class extends AsyncLoaded {
        content = content;
      });
    }

    error(msg) {
      return create(class extends AsyncError {
        error = msg;
      });
    }
  }

  class AsyncLoaded extends Async {
    isLoaded = true;
    isLoading = false;
    isError = false;
  }
  describe('successful loading siquence', () => {
    let async = create(Async);
    it('can transition to loading', () => {
      expect(async.loading().state).toMatchObject({
        content: null,
        isLoaded: false,
        isLoading: true,
        isError: false,
      });
    });
    it('can transition from loading to loaded', () => {
      expect(async.loading().loaded('GREAT SUCCESS').state).toMatchObject({
        content: 'GREAT SUCCESS',
        isLoaded: true,
        isLoading: false,
        isError: false,
      });
    });
  });
  describe('error loading sequence', () => {
    let async = create(Async);
    it('can transition from loading to error', () => {
      expect(async.loading().error(':(').state).toMatchObject({
        content: null,
        isLoaded: true,
        isError: true,
        isLoading: false,
        error: ':(',
      });
    });
  });
});

describe("type-shifting into a deeply composed microstate", () => {
  class Node {
    name = String;
    node = Node;
  }

  let root;
  beforeEach(() => {
    root = create(Node);
  })

  describe('shifting the root node', () => {

    let shiftedRoot;
    beforeEach(() => {
      shiftedRoot = root.set(
        create(Node, { name: "n1", node: { name: "n2", node: { name: "n3" } } })
      )
    });

    it("preserves type shifting value", () => {
      expect(
        shiftedRoot.state
      ).toMatchObject({
        name: 'n1',
        node: { name: 'n2', node: { name: "n3" }}
      });
    });

    it('preserves valueOf', () => {
      expect(shiftedRoot.valueOf()).toEqual({
        name: 'n1',
        node: { name: 'n2', node: { name: "n3" }}
      })
    });
  });

  describe('shifting deeply composted state with new value', () => {

    let shiftedDeeply;
    beforeEach(() => {
      shiftedDeeply = root.node.node.node.set({ name: "soooo deep", node: { name: 'one more' }});
    });

    it("preserves type shifting value", () => {
      expect(
        shiftedDeeply.state
      ).toMatchObject({
        node: { node: { node: { name: 'soooo deep', node: { name: 'one more' }}}}
      });
    });

    it('preserves valueOf', () => {
      expect(shiftedDeeply.valueOf()).toEqual({
        node: { node: { node: { name: 'soooo deep', node: { name: 'one more' }}}}
      })
    });
  });
});

describe("type-shifting in a getter", () => {
  class Node {
    depth = Number;

    get next() {
      return create(Node, { depth: this.depth + 1 }).state;
    }
  }

  let root;
  beforeEach(() => {
    root = create(Node);
  });

  it('allows to create nodes', () => {
    expect(root.state.depth).toBe(0);
    expect(root.state.next.depth).toBe(1);
    expect(root.state.next.next.depth).toBe(2);
  });
});

describe.skip("type-shifting recursively with create", () => {
  // this fails with: RangeError: Maximum call stack size exceeded
  // I suspect this is because `create` is eager, it would be cool if
  // we could make this lazy and thereby allow tree to be pulled on demand

  class Node {
    depth = Number;
    node = Node;

    static create({ depth = 0 } = {}) {
      return create(Node, { depth, node: { depth: depth + 1 } });
    }
  }

  let root;
  beforeEach(() => {
    root = create(Node);
  });

  it('allows to create nodes', () => {
    expect(root.state.depth).toBe(0);
    expect(root.state.node.depth).toBe(1)
  });
});

describe('type-shifting from create to parameterized array', () => {
  class Person {
    name = String;
  }

  class Group {
    members = [Person]

    static create({ members } = {}) {
      if (!members) {
        return create(Group, { members: [
          { name: 'Taras' },
          { name: 'Charles' },
          { name: 'Siva' }
        ]});
      }
    }
  }

  let group;
  let value;

  beforeEach(() => {
    group = create(Group);
    value = group.valueOf();
  });

  it('initializes to value', () => {
    expect(value).toMatchObject({
      members: [
        { name: 'Taras' },
        { name: 'Charles' },
        { name: 'Siva' }
      ]
    });
  });

  it('has a POJO as value', () => {
    let descriptor = Object.getOwnPropertyDescriptor(value, 'members');
    expect(descriptor).toHaveProperty('value', [
      { name: 'Taras' },
      { name: 'Charles' },
      { name: 'Siva' }
    ]);
    expect(descriptor.get).toBeUndefined();
  });

  it('provides data to parameterized array', () => {
    let { tree } = reveal(group);
    expect(group.state.members).toHaveLength(3);
    expect(group.state).toMatchObject({
      members: [
        { name: 'Taras' },
        { name: 'Charles' },
        { name: 'Siva' }
      ]
    });
    expect(group.state.members[0]).toBeInstanceOf(Person);
  });

  describe('transitioning shifted value', () => {
    let acclaimed;
    let value;

    beforeEach(() => {
      acclaimed = group.members[1].name.set('!!Charles!!');
      value = acclaimed.valueOf();
    });

    it('has the transitioned state', () => {
      expect(acclaimed.state).toMatchObject({
        members: [
          { name: 'Taras' },
          { name: '!!Charles!!' },
          { name: 'Siva' }
        ]
      })
    });

    it('carries the value of', () => {
      expect(value).toEqual({
        members: [
          { name: 'Taras' },
          { name: '!!Charles!!' },
          { name: 'Siva' }
        ]
      });
    });

    it('has a POJO as value', () => {
      let descriptor = Object.getOwnPropertyDescriptor(value, 'members');
      expect(descriptor).toHaveProperty('value', [
        { name: 'Taras' },
        { name: '!!Charles!!' },
        { name: 'Siva' }
      ]);
      expect(descriptor.get).toBeUndefined();
    });
  });
});

describe('type-shifting from create to parameterized object', () => {
  class Parent {
    name = String;
  }
  class Person {
    parents = { Parent }

    static create({ parents } = {}) {
      if (!parents) {
        return create(Person, {
          parents: {
            father: {
              name: 'John Doe'
            },
            mother: {
              name: 'Jane Doe'
            }
          }
        });
      }
    }
  }

  let person;
  beforeEach(() => {
    person = create(Person);
  });

  it('has name with initial values', () => {
    expect(person.state.parents.father).toBeInstanceOf(Parent);
    expect(person.state).toMatchObject({
      parents: {
        father: {
          name: 'John Doe'
        },
        mother: {
          name: 'Jane Doe'
        }
      }
    })
  });

  it('has valueOf', () => {
    expect(person.valueOf()).toEqual({
      parents: {
        father: {
          name: 'John Doe'
        },
        mother: {
          name: 'Jane Doe'
        }
      }
    });
  });
});

describe('type-shifting from create nodes in single operation', () => {
  class Root {
    static create(params) {
      if (!params) {
        return create(Root, { name: 'Default for Root', first: { second: { name: 'Provided name for Second' } } })
      }
    }
    name = String;
    first = class First {
      name = String;
      second = class Second {
        name = String;
        third = class Third {
          name = String;
          static create(params) {
            if (!params) {
              return create(Third, { name: 'Default for Third' });
            }
          }
        }
      }
    }
  }

  let root;
  beforeEach(() => {
    root = create(Root);
  });

  it('has state for root', () => {
    expect(root.state).toMatchObject({
      name: 'Default for Root',
      first: {
        name: '',
        second: {
          name: 'Provided name for Second',
          third: {
            name: 'Default for Third'
          }
        }
      }
    })
  });

  it('has valueOf', () => {
    expect(root.valueOf()).toMatchObject({
      name: 'Default for Root',
      first: {
        name: undefined,
        second: {
          name: 'Provided name for Second',
          third: {
            name: 'Default for Third'
          }
        }
      }
    })
  });
});

describe('type-shifting with create in from none root node', () => {

  class Root {
    first = class First {
      second = class Second {
        name = String;
        static create(props) {
          if (!props) {
            return create(Second, { name: 'default' });
          }
        }
      }
    }
  }

  let root, changed;
  beforeEach(() => {
    root = create(Root);
    changed = root.first.second.name.concat('!!!');
  });

  it('has result of create of second node', () => {
    expect(root.state).toMatchObject({
      first: {
        second: {
          name: 'default'
        }
      }
    });
  });

  it('has result after transition valueOf', () => {
    expect(changed.valueOf()).toEqual({
      first: {
        second: {
          name: 'default!!!'
        }
      }
    });
  });

});<commit_msg>All the tests from 'Ensure value from static create is provided to composed children' are passing<commit_after>import 'jest';
import { create, reveal } from 'microstates';
import logTree from '../src/utils/log-tree';

describe('type-shifting', () => {

  class Shape {
    static create({ a, b, c } = {}) {
      if (a && b && c) {
        return create(Triangle, { a, b, c });
      }
      if (a && b) {
        return create(Angle, { a, b });
      }
      if (a) {
        return create(Line, { a });
      }
    }
  }

  class Line extends Shape {
    a = Number;
    add(b) {
      let { a } = this.state;
      return create(Angle, { a, b });
    }
  }

  class Angle extends Line {
    a = Number;
    b = Number;
    add(c) {
      let { a, b } = this.state;
      return create(Triangle, { a, b, c });
    }
  }

  class Triangle extends Angle {
    c = Number;
  }

  class Glass {
    shape = Shape;
  }

  describe('create', function() {

    it('can initialize to itself', () => {
      let shape = create(Shape);
      expect(shape.state).toBeInstanceOf(Shape);
    });

    it('initializes to first type', () => {
      let triangle = Shape.create({a: 10, b: 20, c: 30 });
      expect(triangle.state).toBeInstanceOf(Triangle);
      expect(triangle.state).toMatchObject({
        a: 10,
        b: 20,
        c: 30
      });
    });

    it('initializes to second type', () => {
      let angle = Shape.create({a: 10, b: 20 });
      expect(angle.state).toBeInstanceOf(Angle);
      expect(angle.state).toMatchObject({
        a: 10,
        b: 20,
      });
    });

    it(`can be initialized from descendant's create`, function() {
      let line = Line.create({ a: 10 });
      expect(line.state).toBeInstanceOf(Line);
      expect(line.state).toMatchObject({
        a: 10
      });
    });

    it('is used to initialize composed object', function() {
      let composed = create(Glass, { shape : {a: 10, b: 20, c: 30 }});
      expect(composed.state.shape).toBeInstanceOf(Triangle);
      expect(composed.state).toMatchObject({
        shape: {
          a: 10,
          b: 20,
          c: 30
        }
      });
    });

    it('supports being initialized in parameterized arrays', () => {
      class Drawing {
        shapes = [Shape]
      }
      let drawing = create(Drawing, { shapes: [ { a: 10 }, { a: 20, b: 30 }, { a: 100, b: 200, c: 300 }]});
      expect(drawing.state.shapes[0]).toBeInstanceOf(Line);
      expect(drawing.state.shapes[1]).toBeInstanceOf(Angle);
      expect(drawing.state.shapes[2]).toBeInstanceOf(Triangle);
    });

    describe('can type-shift into a parameterized type', () => {
      class Container {
        static create(content) {
          if (Array.isArray(content)) {
            return create([String], content);
          } else {
            return create({String}, content);
          }
        }
      }
      it('can initialize into a parameterized array', () => {
        let array = Container.create(['a', 'b', 'c']);
        expect(array.state).toMatchObject(['a', 'b', 'c']);
        expect(array[0].concat).toBeInstanceOf(Function);
      });
      it('can initialize into a parameterized object', () => {
        let object = Container.create({a: 'A', b: 'B', c: 'C'});
        expect(object.state).toMatchObject({a: 'A', b: 'B', c: 'C'});
        expect(object.a.concat).toBeInstanceOf(Function);
      });
    });

  });

  describe('transitions', function() {
    let ms = create(Line);
    let line, corner, triangle;
    beforeEach(() => {
      line = ms.a.set(10);
      corner = line.add(20);
      triangle = corner.add(30);
    });
    it('constructs a line', () => {
      expect(line.state).toBeInstanceOf(Line);
      expect(line.state).toMatchObject({
        a: 10,
      });
      expect(line.valueOf()).toEqual({ a: 10 });
    });
    it('constructs a Corner', () => {
      expect(corner.state).toBeInstanceOf(Angle);
      expect(corner.state).toMatchObject({
        a: 10,
        b: 20,
      });
      expect(corner.valueOf()).toEqual({ a: 10, b: 20 });
    });
    it('constructs a Triangle', () => {
      expect(triangle.state).toBeInstanceOf(Triangle);
      expect(triangle.state).toMatchObject({
        a: 10,
        b: 20,
        c: 30,
      });
      expect(triangle.valueOf()).toEqual({ a: 10, b: 20, c: 30 });
    });
    it('can be done down tree', () => {
      let string = create(String, '100');
      let cString = triangle.c.set(string)
      expect(cString.state.c).toBe('100');
      expect(cString.c.concat).toBeDefined();
    });
  });
});

describe('type-shifting with constant values', () => {
  class Async {
    content = null;
    isLoaded = false;
    isLoading = false;
    isError = false;

    loading() {
      return create(AsyncLoading);
    }
  }

  class AsyncError extends Async {
    isError = true;
    isLoading = false;
    isLoaded = true;
  }

  class AsyncLoading extends Async {
    isLoading = true;

    loaded(content) {
      return create(class extends AsyncLoaded {
        content = content;
      });
    }

    error(msg) {
      return create(class extends AsyncError {
        error = msg;
      });
    }
  }

  class AsyncLoaded extends Async {
    isLoaded = true;
    isLoading = false;
    isError = false;
  }
  describe('successful loading siquence', () => {
    let async = create(Async);
    it('can transition to loading', () => {
      expect(async.loading().state).toMatchObject({
        content: null,
        isLoaded: false,
        isLoading: true,
        isError: false,
      });
    });
    it('can transition from loading to loaded', () => {
      expect(async.loading().loaded('GREAT SUCCESS').state).toMatchObject({
        content: 'GREAT SUCCESS',
        isLoaded: true,
        isLoading: false,
        isError: false,
      });
    });
  });
  describe('error loading sequence', () => {
    let async = create(Async);
    it('can transition from loading to error', () => {
      expect(async.loading().error(':(').state).toMatchObject({
        content: null,
        isLoaded: true,
        isError: true,
        isLoading: false,
        error: ':(',
      });
    });
  });
});

describe("type-shifting into a deeply composed microstate", () => {
  class Node {
    name = String;
    node = Node;
  }

  let root;
  beforeEach(() => {
    root = create(Node);
  })

  describe('shifting the root node', () => {

    let shiftedRoot;
    beforeEach(() => {
      shiftedRoot = root.set(
        create(Node, { name: "n1", node: { name: "n2", node: { name: "n3" } } })
      )
    });

    it("preserves type shifting value", () => {
      expect(
        shiftedRoot.state
      ).toMatchObject({
        name: 'n1',
        node: { name: 'n2', node: { name: "n3" }}
      });
    });

    it('preserves valueOf', () => {
      expect(shiftedRoot.valueOf()).toEqual({
        name: 'n1',
        node: { name: 'n2', node: { name: "n3" }}
      })
    });
  });

  describe('shifting deeply composted state with new value', () => {

    let shiftedDeeply;
    beforeEach(() => {
      shiftedDeeply = root.node.node.node.set({ name: "soooo deep", node: { name: 'one more' }});
    });

    it("preserves type shifting value", () => {
      expect(
        shiftedDeeply.state
      ).toMatchObject({
        node: { node: { node: { name: 'soooo deep', node: { name: 'one more' }}}}
      });
    });

    it('preserves valueOf', () => {
      expect(shiftedDeeply.valueOf()).toEqual({
        node: { node: { node: { name: 'soooo deep', node: { name: 'one more' }}}}
      })
    });
  });
});

describe("type-shifting in a getter", () => {
  class Node {
    depth = Number;

    get next() {
      return create(Node, { depth: this.depth + 1 }).state;
    }
  }

  let root;
  beforeEach(() => {
    root = create(Node);
  });

  it('allows to create nodes', () => {
    expect(root.state.depth).toBe(0);
    expect(root.state.next.depth).toBe(1);
    expect(root.state.next.next.depth).toBe(2);
  });
});

describe.skip("type-shifting recursively with create", () => {
  // this fails with: RangeError: Maximum call stack size exceeded
  // I suspect this is because `create` is eager, it would be cool if
  // we could make this lazy and thereby allow tree to be pulled on demand

  class Node {
    depth = Number;
    node = Node;

    static create({ depth = 0 } = {}) {
      return create(Node, { depth, node: { depth: depth + 1 } });
    }
  }

  let root;
  beforeEach(() => {
    root = create(Node);
  });

  it('allows to create nodes', () => {
    expect(root.state.depth).toBe(0);
    expect(root.state.node.depth).toBe(1)
  });
});

describe('type-shifting from create to parameterized array', () => {
  class Person {
    name = String;
  }

  class Group {
    members = [Person]

    static create({ members } = {}) {
      if (!members) {
        return create(Group, { members: [
          { name: 'Taras' },
          { name: 'Charles' },
          { name: 'Siva' }
        ]});
      }
    }
  }

  let group;
  let value;

  beforeEach(() => {
    group = create(Group);
    value = group.valueOf();
  });

  it('initializes to value', () => {
    expect(value).toMatchObject({
      members: [
        { name: 'Taras' },
        { name: 'Charles' },
        { name: 'Siva' }
      ]
    });
  });

  it('has a POJO as value', () => {
    let descriptor = Object.getOwnPropertyDescriptor(value, 'members');
    expect(descriptor).toHaveProperty('value', [
      { name: 'Taras' },
      { name: 'Charles' },
      { name: 'Siva' }
    ]);
    expect(descriptor.get).toBeUndefined();
  });

  it('provides data to parameterized array', () => {
    let { tree } = reveal(group);
    expect(group.state.members).toHaveLength(3);
    expect(group.state).toMatchObject({
      members: [
        { name: 'Taras' },
        { name: 'Charles' },
        { name: 'Siva' }
      ]
    });
    expect(group.state.members[0]).toBeInstanceOf(Person);
  });

  describe('transitioning shifted value', () => {
    let acclaimed;
    let value;

    beforeEach(() => {
      acclaimed = group.members[1].name.set('!!Charles!!');
      value = acclaimed.valueOf();
    });

    it('has the transitioned state', () => {
      expect(acclaimed.state).toMatchObject({
        members: [
          { name: 'Taras' },
          { name: '!!Charles!!' },
          { name: 'Siva' }
        ]
      })
    });

    it('carries the value of', () => {
      expect(value).toEqual({
        members: [
          { name: 'Taras' },
          { name: '!!Charles!!' },
          { name: 'Siva' }
        ]
      });
    });

    it('has a POJO as value', () => {
      let descriptor = Object.getOwnPropertyDescriptor(value, 'members');
      expect(descriptor).toHaveProperty('value', [
        { name: 'Taras' },
        { name: '!!Charles!!' },
        { name: 'Siva' }
      ]);
      expect(descriptor.get).toBeUndefined();
    });
  });
});

describe('type-shifting from create to parameterized object', () => {
  class Parent {
    name = String;
  }
  class Person {
    parents = { Parent }

    static create({ parents } = {}) {
      if (!parents) {
        return create(Person, {
          parents: {
            father: {
              name: 'John Doe'
            },
            mother: {
              name: 'Jane Doe'
            }
          }
        });
      }
    }
  }

  let person;
  beforeEach(() => {
    person = create(Person);
  });

  it('has name with initial values', () => {
    expect(person.state.parents.father).toBeInstanceOf(Parent);
    expect(person.state).toMatchObject({
      parents: {
        father: {
          name: 'John Doe'
        },
        mother: {
          name: 'Jane Doe'
        }
      }
    })
  });

  it('has valueOf', () => {
    expect(person.valueOf()).toEqual({
      parents: {
        father: {
          name: 'John Doe'
        },
        mother: {
          name: 'Jane Doe'
        }
      }
    });
  });
});

describe('type-shifting from create nodes in single operation', () => {
  class Root {
    static create(params) {
      if (!params) {
        return create(Root, { name: 'Default for Root', first: { second: { name: 'Provided name for Second' } } })
      }
    }
    name = String;
    first = class First {
      name = String;
      second = class Second {
        name = String;
        third = class Third {
          name = String;
          static create(params) {
            if (!params) {
              return create(Third, { name: 'Default for Third' });
            }
          }
        }
      }
    }
  }

  let root;
  let transitioned;
  beforeEach(() => {
    root = create(Root);
    transitioned = root.first.second.third.name.concat('!!!');
  });

  it('has state for root', () => {
    expect(root.state).toMatchObject({
      name: 'Default for Root',
      first: {
        name: '',
        second: {
          name: 'Provided name for Second',
          third: {
            name: 'Default for Third'
          }
        }
      }
    })
  });

  it('has valueOf', () => {
    expect(root.valueOf()).toMatchObject({
      name: 'Default for Root',
      first: {
        second: {
          name: 'Provided name for Second'
        }
      }
    })
  });

  it('has value from third initialized value', () => {
    expect(transitioned.valueOf()).toEqual({
      name: 'Default for Root',
      first: {
        second: {
          name: 'Provided name for Second',
          third: {
            name: 'Default for Third!!!'
          }
        }
      }
    });
  });

});

describe('type-shifting with create in from none root node', () => {

  class Root {
    first = class First {
      second = class Second {
        name = String;
        static create(props) {
          if (!props) {
            return create(Second, { name: 'default' });
          }
        }
      }
    }
  }

  let root, changed;
  beforeEach(() => {
    root = create(Root);
    changed = root.first.second.name.concat('!!!');
  });

  it('has result of create of second node', () => {
    expect(root.state).toMatchObject({
      first: {
        second: {
          name: 'default'
        }
      }
    });
  });

  it('has result after transition valueOf', () => {
    expect(changed.valueOf()).toEqual({
      first: {
        second: {
          name: 'default!!!'
        }
      }
    });
  });

});<|endoftext|>


<commit_before>/* global $ MB relEditor requests */
'use strict';
// ==UserScript==
// @name         MusicBrainz relation editor: Clone recording relations onto other recordings
// @namespace    mbz-loujine
// @author       loujine
// @version      2020.11.5.1
// @downloadURL  https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-reledit-clone_relations.user.js
// @updateURL    https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-reledit-clone_relations.user.js
// @supportURL   https://github.com/loujine/musicbrainz-scripts
// @icon         https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/icon.png
// @description  musicbrainz.org relation editor: Clone recording relations onto other recordings
// @compatible   firefox+tampermonkey
// @license      MIT
// @require      https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-loujine-common.js
// @include      http*://*musicbrainz.org/release/*/edit-relationships
// @grant        none
// @run-at       document-end
// ==/UserScript==

const MBID_REGEX = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/;

function autoCompleteRec() {
    const $input = $('input#cloneExtRecording');
    const match = $input.val().match(MBID_REGEX);
    if (match) {
        const mbid = match[0];
        requests.GET(`/ws/2/recording/${mbid}?fmt=json`, data => {
            data = JSON.parse(data);
            $input.data('mbid', mbid);
            $input.val(data.title || data.name);
            $input.css('background', '#bbffbb');
        });
    } else {
        $input.data().mbid = "";
        $input.css('background', '#ffaaaa');
    }
}

function cloneAR(refIdx) {
    refIdx = refIdx - 1 || 0;
    const vm = MB.releaseRelationshipEditor;
    const selectedRecordings = MB.relationshipEditor.UI.checkedRecordings();
    const sourceRecording = selectedRecordings.splice(refIdx, 1)[0];
    const sourceRels = sourceRecording.relationships().filter(
        rel => !['recording-recording', 'recording-work'].includes(rel.entityTypes)
    );
    let dialog;

    selectedRecordings.map(rec => {
        sourceRels.map(sourceRel => {
            dialog = new MB.relationshipEditor.UI.AddDialog({
                viewModel: vm,
                source: rec,
                target: sourceRel.entities().filter(ent => ent.entityType !== 'recording')[0],
            });

            dialog.relationship().linkTypeID(sourceRel.linkTypeID());
            dialog.relationship().setAttributes(sourceRel.attributes());

            dialog.relationship().entity0_credit(sourceRel.entity0_credit());
            dialog.relationship().entity1_credit(sourceRel.entity1_credit());

            if (sourceRel.begin_date) {
                dialog.relationship().begin_date.year(sourceRel.begin_date.year());
                dialog.relationship().begin_date.month(sourceRel.begin_date.month());
                dialog.relationship().begin_date.day(sourceRel.begin_date.day());
            }
            if (sourceRel.end_date) {
                dialog.relationship().end_date.year(sourceRel.end_date.year());
                dialog.relationship().end_date.month(sourceRel.end_date.month());
                dialog.relationship().end_date.day(sourceRel.end_date.day());
            }
            dialog.accept();
        });
    });
}

function cloneExtAR(recMBID) {
    if (recMBID.split('/').length > 1) {
        recMBID = recMBID.split('/')[4];
    }
    const vm = MB.releaseRelationshipEditor;
    const selectedRecordings = MB.relationshipEditor.UI.checkedRecordings();
    let dialog;

    requests.GET(`/ws/js/entity/${recMBID}?inc=rels`, resp => {
        const sourceRels = JSON.parse(resp).relationships.filter(
            rel => rel.target_type != 'work'
        );
        selectedRecordings.map(rec => {
            sourceRels.map(sourceRel => {
                dialog = new MB.relationshipEditor.UI.AddDialog({
                    viewModel: vm,
                    source: rec,
                    target: sourceRel.target,
                });
                dialog.accept();

                dialog.relationship().linkTypeID(sourceRel.linkTypeID);
                dialog.relationship().setAttributes(sourceRel.attributes);

                dialog.relationship().entity0_credit(sourceRel.entity0_credit);
                dialog.relationship().entity1_credit(sourceRel.entity1_credit);

                if (sourceRel.begin_date) {
                    dialog.relationship().begin_date.year(sourceRel.begin_date.year);
                    dialog.relationship().begin_date.month(sourceRel.begin_date.month);
                    dialog.relationship().begin_date.day(sourceRel.begin_date.day);
                }
                if (sourceRel.end_date) {
                    dialog.relationship().end_date.year(sourceRel.end_date.year);
                    dialog.relationship().end_date.month(sourceRel.end_date.month);
                    dialog.relationship().end_date.day(sourceRel.end_date.day);
                    dialog.accept();
                }
            });
        });
    });
}

(function displayToolbar() {
    relEditor.container(document.querySelector('div.tabs'))
             .insertAdjacentHTML('beforeend', `

            ▶ Clone recording relations to selected recordings

              Recording index in selection

            :

          <input type="text" id="cloneRef" placeholder="1 (clone from 1st selected recording)">
          OR recording link:
          <input type="text" id="cloneExtRecording" placeholder="recording mbid">

          <input type="button" id="cloneAR" value="Apply">

    `);
})();

$(document).ready(function () {
    document.getElementById('clone_rels_script_toggle').addEventListener('click', () => {
        const header = document.getElementById('clone_rels_script_toggle');
        const block = document.getElementById('clone_rels_script_block');
        const display = block.style.display;
        header.textContent = header.textContent.replace(/./, display == "block" ? "▶" : "▼");
        block.style.display = display == "block" ? "none" : "block";
    });
    $('input#cloneExtRecording').on('input', autoCompleteRec);
    let appliedNote = false;
    document.getElementById('cloneAR').addEventListener('click', () => {

        const recMBID = $('input#cloneExtRecording').data('mbid');
        if (recMBID) {
            cloneExtAR(recMBID);
        } else {
            const refIdx = parseInt(document.getElementById('cloneRef').value);
            cloneAR(refIdx);
        }
        if (!appliedNote) {
            relEditor.editNote(GM_info.script);
            appliedNote = true;
        }
    });
    return false;
});
<commit_msg>[clone relations] Fix relation direction for recording-recording links (fix #28)<commit_after>/* global $ MB relEditor requests */
'use strict';
// ==UserScript==
// @name         MusicBrainz relation editor: Clone recording relations onto other recordings
// @namespace    mbz-loujine
// @author       loujine
// @version      2021.2.26
// @downloadURL  https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-reledit-clone_relations.user.js
// @updateURL    https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mb-reledit-clone_relations.user.js
// @supportURL   https://github.com/loujine/musicbrainz-scripts
// @icon         https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/icon.png
// @description  musicbrainz.org relation editor: Clone recording relations onto other recordings
// @compatible   firefox+tampermonkey
// @license      MIT
// @require      https://raw.githubusercontent.com/loujine/musicbrainz-scripts/master/mbz-loujine-common.js
// @include      http*://*musicbrainz.org/release/*/edit-relationships
// @grant        none
// @run-at       document-end
// ==/UserScript==

const MBID_REGEX = /[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}/;

function autoCompleteRec() {
    const $input = $('input#cloneExtRecording');
    const match = $input.val().match(MBID_REGEX);
    if (match) {
        const mbid = match[0];
        requests.GET(`/ws/2/recording/${mbid}?fmt=json`, data => {
            data = JSON.parse(data);
            $input.data('mbid', mbid);
            $input.val(data.title || data.name);
            $input.css('background', '#bbffbb');
        });
    } else {
        $input.data().mbid = "";
        $input.css('background', '#ffaaaa');
    }
}

function cloneAR(refIdx) {
    refIdx = refIdx - 1 || 0;
    const vm = MB.releaseRelationshipEditor;
    const selectedRecordings = MB.relationshipEditor.UI.checkedRecordings();
    const sourceRecording = selectedRecordings.splice(refIdx, 1)[0];
    const sourceRels = sourceRecording.relationships().filter(
        rel => !['recording-recording', 'recording-work'].includes(rel.entityTypes)
    );
    let dialog;

    selectedRecordings.map(rec => {
        sourceRels.map(sourceRel => {
            dialog = new MB.relationshipEditor.UI.AddDialog({
                viewModel: vm,
                source: rec,
                target: sourceRel.entities().filter(ent => ent.entityType !== 'recording')[0],
            });

            dialog.relationship().linkTypeID(sourceRel.linkTypeID());
            dialog.relationship().setAttributes(sourceRel.attributes());

            dialog.relationship().entity0_credit(sourceRel.entity0_credit());
            dialog.relationship().entity1_credit(sourceRel.entity1_credit());

            if (sourceRel.begin_date) {
                dialog.relationship().begin_date.year(sourceRel.begin_date.year());
                dialog.relationship().begin_date.month(sourceRel.begin_date.month());
                dialog.relationship().begin_date.day(sourceRel.begin_date.day());
            }
            if (sourceRel.end_date) {
                dialog.relationship().end_date.year(sourceRel.end_date.year());
                dialog.relationship().end_date.month(sourceRel.end_date.month());
                dialog.relationship().end_date.day(sourceRel.end_date.day());
            }
            dialog.accept();
        });
    });
}

function cloneExtAR(recMBID) {
    if (recMBID.split('/').length > 1) {
        recMBID = recMBID.split('/')[4];
    }
    const vm = MB.releaseRelationshipEditor;
    const selectedRecordings = MB.relationshipEditor.UI.checkedRecordings();
    let dialog;

    requests.GET(`/ws/js/entity/${recMBID}?inc=rels`, resp => {
        const sourceRels = JSON.parse(resp).relationships.filter(
            rel => rel.target_type != 'work'
        );
        selectedRecordings.map(rec => {
            sourceRels.map(sourceRel => {
                dialog = new MB.relationshipEditor.UI.AddDialog({
                    viewModel: vm,
                    source: rec,
                    target: sourceRel.target,
                });
                dialog.accept();

                dialog.relationship().linkTypeID(sourceRel.linkTypeID);
                dialog.relationship().setAttributes(sourceRel.attributes);

                dialog.relationship().entity0_credit(sourceRel.entity0_credit);
                dialog.relationship().entity1_credit(sourceRel.entity1_credit);

                if (sourceRel.target_type === 'recording'
                        && sourceRel.direction === 'backward') {
                    dialog.changeDirection();
                }
                if (sourceRel.begin_date) {
                    dialog.relationship().begin_date.year(sourceRel.begin_date.year);
                    dialog.relationship().begin_date.month(sourceRel.begin_date.month);
                    dialog.relationship().begin_date.day(sourceRel.begin_date.day);
                }
                if (sourceRel.end_date) {
                    dialog.relationship().end_date.year(sourceRel.end_date.year);
                    dialog.relationship().end_date.month(sourceRel.end_date.month);
                    dialog.relationship().end_date.day(sourceRel.end_date.day);
                    dialog.accept();
                }
            });
        });
    });
}

(function displayToolbar() {
    relEditor.container(document.querySelector('div.tabs'))
             .insertAdjacentHTML('beforeend', `

            ▶ Clone recording relations to selected recordings

              Recording index in selection

            :

          <input type="text" id="cloneRef" placeholder="1 (clone from 1st selected recording)">
          OR recording link:
          <input type="text" id="cloneExtRecording" placeholder="recording mbid">

          <input type="button" id="cloneAR" value="Apply">

    `);
})();

$(document).ready(function () {
    document.getElementById('clone_rels_script_toggle').addEventListener('click', () => {
        const header = document.getElementById('clone_rels_script_toggle');
        const block = document.getElementById('clone_rels_script_block');
        const display = block.style.display;
        header.textContent = header.textContent.replace(/./, display == "block" ? "▶" : "▼");
        block.style.display = display == "block" ? "none" : "block";
    });
    $('input#cloneExtRecording').on('input', autoCompleteRec);
    let appliedNote = false;
    document.getElementById('cloneAR').addEventListener('click', () => {

        const recMBID = $('input#cloneExtRecording').data('mbid');
        if (recMBID) {
            cloneExtAR(recMBID);
        } else {
            const refIdx = parseInt(document.getElementById('cloneRef').value);
            cloneAR(refIdx);
        }
        if (!appliedNote) {
            relEditor.editNote(GM_info.script);
            appliedNote = true;
        }
    });
    return false;
});
<|endoftext|>


<commit_before>/*
 |--------------------------------------------------------------------------
 | Mo - Settings
 |--------------------------------------------------------------------------
 | author: @kreo
 | https://github.com/kreo
 |--------------------------------------------------------------------------
 */

/*jshint esversion: 6 */

var gulp = require("gulp");
var mo = require("mojs")(gulp, {
    source: "./source",
    dest: "./dist",
    app: "app",
    vendor: "vendor",
    bower: {
        plugins: ["slick-carousel"],
        order: [
            "jquery/*.js",
            "foundation-sites/*.js",
            "**/*.js"
        ]
    }
});

// Tasks
// ---------------------------------------------------------

mo.run([
    "default",
    "stylus",
    "sass",
    "browserify",
    "jade",
    "images",
    "fonts",
    "bower",
    "serve",
    "build"
]);

// mo.extend("custom", function(){
//     console.log("this is a custom task!");
// });
<commit_msg>added serve config<commit_after>/*
 |--------------------------------------------------------------------------
 | Mo - Settings
 |--------------------------------------------------------------------------
 | author: @kreo
 | https://github.com/kreo
 |--------------------------------------------------------------------------
 */

/*jshint esversion: 6 */

var gulp = require("gulp");
var mo = require("mojs")(gulp, {
    source: "./source",
    dest: "./dist",
    app: "app",
    vendor: "vendor",
    bower: {
        plugins: ["slick-carousel"],
        order: [
            "jquery/*.js",
            "foundation-sites/*.js",
            "**/*.js"
        ]
    },
    serve: {
        host: "project.dev",
        proxy: "project.dev/",
        port: "9001"
    }
});

// Tasks
// ---------------------------------------------------------

mo.run([
    "default",
    "stylus",
    "sass",
    "browserify",
    "jade",
    "images",
    "fonts",
    "bower",
    "serve",
    "build"
]);

// mo.extend("custom", function(){
//     console.log("this is a custom task!");
// });
<|endoftext|>


<commit_before>export default class Unserializer {
  constructor(options = {}) {
    this.options = options;
  }

  run() {
    throw new Error('Override this functions in your class');
  }
}
<commit_msg>Change class for unserializer for array of functions<commit_after><|endoftext|>


<commit_before>'use strict';

const execa = require('execa'),
  log = require('intel'),
  Promise = require('bluebird');

function buildX11FfmpegArgs({display, screen = 0, framerate = 30, origin = '0,0', size, filePath}) {
  return ['-hide_banner',
    '-an',
    '-y',
    '-framerate', framerate,
    '-video_size', size,
    '-f', 'x11grab',
    '-i', `:${display}.${screen}+${origin}`,
    '-c:v', 'libx264',
    '-pix_fmt', 'yuv420p',
    '-profile:v', 'main',
    '-crf', '18',
    '-movflags',  'faststart',
    '-preset', 'ultrafast',
    filePath];
}

function buildAVFoundationFfmpegArgs({display, framerate = 30, filePath}) {
  return ['-hide_banner',
    '-an',
    '-y',
    '-framerate', framerate,
    '-f', 'avfoundation',
    '-i', `${display}.0`,
    filePath];
}

function startRecording(ffmpegArgs, filePath) {
  function waitForRecording(readableStream) {
    return new Promise((resolve, reject) => {
      readableStream.on('data', (data) => {
        log.debug(data.toString());
        if (data.toString().match(/Press \[q\] to stop/)) {
          // readableStream.removeAllListeners('data');
          return resolve();
        }
      });
      readableStream.once('error', reject);
    });
  }

  const ffmpegProcess = execa('ffmpeg', ffmpegArgs);

  // Race to catch if ffmpeg exists early, or if recording has started
  return Promise.race([ffmpegProcess, waitForRecording(ffmpegProcess.stderr)])
    .then(() => {
      log.debug('Started ffmpeg with ' + ffmpegArgs.join(' '));

      return {
        filePath,
        ffmpegProcess
      }
    })
    .catch((e) => {
      throw e;
    });
}

module.exports = {
  /**
   * @returns A promise for a recording object. Pass it to stopRecording.
   */
  startRecordingX11({display, origin = '0,64', size, filePath}) {
    const widthAndHeight = size.split('x');
    const withoutTopBar = widthAndHeight[0] + 'x' + (parseInt(widthAndHeight[1]) - 64);

    const ffmpegArgs = buildX11FfmpegArgs({display, origin, size: withoutTopBar, filePath});
    return startRecording(ffmpegArgs, filePath);
  },
  /**
   * @returns A promise for a recording object. Pass it to stopRecording.
   */
  startRecordingAVFoundation({display, filePath}) {
    const ffmpegArgs = buildAVFoundationFfmpegArgs({display, filePath});
    return startRecording(ffmpegArgs, filePath);
  },
  /**
   * @returns A promise for a recording result, with a filePath property.
   */
  stopRecording(recording) {
    return Promise.resolve(recording)
      .then((rec) => {
        const process = rec.ffmpegProcess;
        delete rec.ffmpegProcess;
        process.stdin.write('q');
        return Promise.resolve(process)
          .then(() => rec)
          .tap(() => log.debug('Stopped ffmpeg'));
      });
  }
};
<commit_msg>hide the mouse<commit_after>'use strict';

const execa = require('execa'),
  log = require('intel'),
  Promise = require('bluebird');

function buildX11FfmpegArgs({display, screen = 0, framerate = 30, origin = '0,0', size, filePath}) {
  return ['-hide_banner',
    '-an',
    '-y',
    '-framerate', framerate,
    '-video_size', size,
    '-f', 'x11grab',
    '-draw_mouse', '0',
    '-i', `:${display}.${screen}+${origin}`,
    '-c:v', 'libx264',
    '-pix_fmt', 'yuv420p',
    '-profile:v', 'main',
    '-crf', '18',
    '-movflags',  'faststart',
    '-preset', 'ultrafast',
    filePath];
}

function buildAVFoundationFfmpegArgs({display, framerate = 30, filePath}) {
  return ['-hide_banner',
    '-an',
    '-y',
    '-framerate', framerate,
    '-f', 'avfoundation',
    '-i', `${display}.0`,
    filePath];
}

function startRecording(ffmpegArgs, filePath) {
  function waitForRecording(readableStream) {
    return new Promise((resolve, reject) => {
      readableStream.on('data', (data) => {
        log.debug(data.toString());
        if (data.toString().match(/Press \[q\] to stop/)) {
          // readableStream.removeAllListeners('data');
          return resolve();
        }
      });
      readableStream.once('error', reject);
    });
  }

  const ffmpegProcess = execa('ffmpeg', ffmpegArgs);

  // Race to catch if ffmpeg exists early, or if recording has started
  return Promise.race([ffmpegProcess, waitForRecording(ffmpegProcess.stderr)])
    .then(() => {
      log.debug('Started ffmpeg with ' + ffmpegArgs.join(' '));

      return {
        filePath,
        ffmpegProcess
      }
    })
    .catch((e) => {
      throw e;
    });
}

module.exports = {
  /**
   * @returns A promise for a recording object. Pass it to stopRecording.
   */
  startRecordingX11({display, origin = '0,64', size, filePath}) {
    const widthAndHeight = size.split('x');
    const withoutTopBar = widthAndHeight[0] + 'x' + (parseInt(widthAndHeight[1]) - 64);

    const ffmpegArgs = buildX11FfmpegArgs({display, origin, size: withoutTopBar, filePath});
    return startRecording(ffmpegArgs, filePath);
  },
  /**
   * @returns A promise for a recording object. Pass it to stopRecording.
   */
  startRecordingAVFoundation({display, filePath}) {
    const ffmpegArgs = buildAVFoundationFfmpegArgs({display, filePath});
    return startRecording(ffmpegArgs, filePath);
  },
  /**
   * @returns A promise for a recording result, with a filePath property.
   */
  stopRecording(recording) {
    return Promise.resolve(recording)
      .then((rec) => {
        const process = rec.ffmpegProcess;
        delete rec.ffmpegProcess;
        process.stdin.write('q');
        return Promise.resolve(process)
          .then(() => rec)
          .tap(() => log.debug('Stopped ffmpeg'));
      });
  }
};
<|endoftext|>


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

const fallback = require('../../lib/fallback');

const basicHTML = `

      <meta property="og:description" content="test description">
      <meta property="og:title" content="test page">
      <meta property="og:audio:url" content="test.mp3">
      <meta property="og:audio:type" content="audio/test">
      <meta name="description" content="fall back description">
      <title>fall back title</title>

hello test page

      <audio>
        <source src="fallback.mp3" type="audio/fallback">
      </audio>

  `;

describe('fall backs', function () {
  it('should use fall backs when there is no og info found', function () {
    let ogObject = {};
    const options = { ogImageFallback: true };
    const $ = cheerio.load(basicHTML);
    ogObject = fallback(ogObject, options, $);

    expect(ogObject.ogImage).to.be.eql([
      {
        url: 'fallback.png', width: '300', height: '200', type: 'png',
      },
      {
        url: 'fallback2.png', width: null, height: null, type: 'png',
      },
    ]);
    expect(ogObject.ogAudioType).to.be.eql('audio/fallback');
    expect(ogObject.ogAudioURL).to.be.eql('fallback.mp3');
    expect(ogObject.ogTitle).to.be.eql('fall back title');
    expect(ogObject.ogDescription).to.be.eql('fall back description');
    expect(ogObject).to.have.all.keys(
      'ogAudioType',
      'ogAudioURL',
      'ogImage',
      'ogTitle',
      'ogDescription',
    );
  });

  it('should not use fall backs when there is og info found', function () {
    let ogObject = {
      ogTitle: 'test page',
      ogDescription: 'test description',
      ogImage: 'test.png',
      ogAudioType: 'audio/test',
      ogAudioURL: 'test.mp3',
    };
    const options = {};
    const $ = cheerio.load(basicHTML);
    ogObject = fallback(ogObject, options, $);

    expect(ogObject.ogAudioType).to.be.eql('audio/test');
    expect(ogObject.ogAudioURL).to.be.eql('test.mp3');
    expect(ogObject.ogImage).to.be.eql('test.png');
    expect(ogObject.ogTitle).to.be.eql('test page');
    expect(ogObject.ogDescription).to.be.eql('test description');
    expect(ogObject).to.have.all.keys(
      'ogAudioType',
      'ogAudioURL',
      'ogImage',
      'ogTitle',
      'ogDescription',
    );
  });
});
<commit_msg>rewrite fallback unit tests<commit_after>const cheerio = require('cheerio');

const fallback = require('../../lib/fallback');

describe('fallback', function () {
  context('ogTitle', function () {
    it('when there is a og title already found', function () {
      let ogObject = { ogTitle: 'bar' };
      const $ = cheerio.load('<title>foo</title>');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogTitle).to.be.eql('bar');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is a title tag', function () {
      let ogObject = {};
      const $ = cheerio.load('<title>foo</title>');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogTitle).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is a meta title tag', function () {
      const $ = cheerio.load('<meta name="title" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogTitle).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is a .post-title div tag', function () {
      const $ = cheerio.load('
foo
');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogTitle).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is a .entry-title div tag', function () {
      const $ = cheerio.load('
foo
');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogTitle).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is a .title h1 a tag', function () {
      const $ = cheerio.load('
foo
');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogTitle).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is a .title h1 tag', function () {
      const $ = cheerio.load('
foo
');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogTitle).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogTitle');
    });
    it('when there is no title', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject).to.be.eql({});
    });
  });

  context('ogDescription', function () {
    it('when there is a og description already found', function () {
      let ogObject = { ogDescription: 'bar' };
      const $ = cheerio.load('<meta name="description" content="foo">');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogDescription).to.be.eql('bar');
      expect(ogObject).to.have.all.keys('ogDescription');
    });
    it('when there is a description meta tag using name', function () {
      const $ = cheerio.load('<meta name="description" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDescription).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDescription');
    });
    it('when there is a description meta tag using itemprop', function () {
      const $ = cheerio.load('<meta itemprop="description" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDescription).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDescription');
    });
    it('when there is a #description tag', function () {
      const $ = cheerio.load('
foo
');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDescription).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDescription');
    });
    it('when there is no description', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject).to.be.eql({});
    });
  });

  context('ogImage', function () {
    it('when there is a og image already found', function () {
      let ogObject = { ogImage: { url: 'bar.png', type: 'png' } };
      const $ = cheerio.load('<image src="foo.png">');
      ogObject = fallback(ogObject, { ogImageFallback: true }, $);
      expect(ogObject.ogImage.url).to.be.eql('bar.png');
      expect(ogObject.ogImage.type).to.be.eql('png');
      expect(ogObject).to.have.all.keys('ogImage');
    });
    it('when there is no og images found and ogImageFallback is set to false', function () {
      const $ = cheerio.load('<image src="foo.png">');
      const ogObject = fallback({}, { ogImageFallback: false }, $);
      expect(ogObject).to.be.eql({});
    });
    it('when there is a mix of valid and invalid images', function () {
      const $ = cheerio.load('<image width=2 src="foo.png"><image src="bar.png"><image src="foo.bar"><image>');
      const ogObject = fallback({}, { ogImageFallback: true }, $);
      expect(ogObject.ogImage).to.be.eql([{
        height: null, type: 'png', url: 'foo.png', width: '2',
      }, {
        height: null, type: 'png', url: 'bar.png', width: null,
      }]);
      expect(ogObject).to.have.all.keys('ogImage');
    });
    it('when there is no og images found and no fallback images', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, { ogImageFallback: true }, $);
      expect(ogObject).to.be.eql({});
    });
    it('when there is a og image already found but it has no type', function () {
      let ogObject = { ogImage: { url: 'bar.png' } };
      const $ = cheerio.load('<image src="foo.png">');
      ogObject = fallback(ogObject, { ogImageFallback: true }, $);
      expect(ogObject.ogImage.url).to.be.eql('bar.png');
      expect(ogObject.ogImage.type).to.be.eql('png');
      expect(ogObject).to.have.all.keys('ogImage');
    });
    it('when there is a og image already found but it has no type but that type is invalid', function () {
      let ogObject = { ogImage: { url: 'bar.foo' } };
      const $ = cheerio.load('<image src="foo.png">');
      ogObject = fallback(ogObject, { ogImageFallback: true }, $);
      expect(ogObject.ogImage.url).to.be.eql('bar.foo');
      expect(ogObject.ogImage.type).to.be.eql(undefined);
      expect(ogObject).to.have.all.keys('ogImage');
    });
  });

  context('ogAudioURL/ogAudioSecureURL', function () {
    it('when there is a og AudioURL already found', function () {
      let ogObject = { ogAudioURL: 'bar.mp3' };
      const $ = cheerio.load('<audio src="foo.png">');
      ogObject = fallback(ogObject, { }, $);
      expect(ogObject.ogAudioURL).to.be.eql('bar.mp3');
      expect(ogObject).to.have.all.keys('ogAudioURL');
    });
    it('when there is a og AudioSecureURL already found', function () {
      let ogObject = { ogAudioSecureURL: 'bar.mp3' };
      const $ = cheerio.load('<audio src="foo.png">');
      ogObject = fallback(ogObject, { }, $);
      expect(ogObject.ogAudioSecureURL).to.be.eql('bar.mp3');
      expect(ogObject).to.have.all.keys('ogAudioSecureURL');
    });
    it('when there is a audio tag without HTTPS', function () {
      const $ = cheerio.load('<audio src="foo.mp3">');
      const ogObject = fallback({ }, { }, $);
      expect(ogObject.ogAudioURL).to.be.eql('foo.mp3');
      expect(ogObject).to.have.all.keys('ogAudioURL');
    });
    it('when there is a audio tag with HTTPS', function () {
      const $ = cheerio.load('<audio src="https://foo.mp3" type="mp3">');
      const ogObject = fallback({ }, { }, $);
      expect(ogObject.ogAudioSecureURL).to.be.eql('https://foo.mp3');
      expect(ogObject.ogAudioType).to.be.eql('mp3');
      expect(ogObject).to.have.all.keys('ogAudioSecureURL', 'ogAudioType');
    });
    it('when there is a audio source tag without HTTPS', function () {
      const $ = cheerio.load('<audio><source src="foo.mp3" type="mp3"></audio>');
      const ogObject = fallback({ }, { }, $);
      expect(ogObject.ogAudioURL).to.be.eql('foo.mp3');
      expect(ogObject.ogAudioType).to.be.eql('mp3');
      expect(ogObject).to.have.all.keys('ogAudioURL', 'ogAudioType');
    });
    it('when there is a audio source tag with HTTPS', function () {
      const $ = cheerio.load('<audio><source src="https://foo.mp3"></audio>');
      const ogObject = fallback({ }, { }, $);
      expect(ogObject.ogAudioSecureURL).to.be.eql('https://foo.mp3');
      expect(ogObject).to.have.all.keys('ogAudioSecureURL');
    });
    it('when there is no', function () {
      const $ = cheerio.load('<audio>');
      const ogObject = fallback({ }, { }, $);
      expect(ogObject).to.be.eql({});
    });
  });

  context('ogLocale', function () {
    it('when there is a og locale already found', function () {
      let ogObject = { ogLocale: 'bar' };
      const $ = cheerio.load('');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogLocale).to.be.eql('bar');
      expect(ogObject).to.have.all.keys('ogLocale');
    });
    it('when there is a html tag with lang', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogLocale).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogLocale');
    });
    it('when there is a meta inLanguage tag', function () {
      const $ = cheerio.load('<meta itemprop="inLanguage" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogLocale).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogLocale');
    });
    it('when there is no ogLocale', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject).to.be.eql({});
    });
  });

  context('ogLogo', function () {
    it('when there is a og logo already found', function () {
      let ogObject = { ogLogo: 'bar' };
      const $ = cheerio.load('<meta itemprop="logo" content="foo">');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogLogo).to.be.eql('bar');
      expect(ogObject).to.have.all.keys('ogLogo');
    });
    it('when there is a meta logo tag', function () {
      const $ = cheerio.load('<meta itemprop="logo" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogLogo).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogLogo');
    });
    it('when there is a img logo tag', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogLogo).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogLogo');
    });
    it('when there is no ogLogo', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject).to.be.eql({});
    });
  });

  context('ogUrl', function () {
    it('when there is a og url already found', function () {
      let ogObject = { ogUrl: 'bar' };
      const $ = cheerio.load('<link rel="canonical" href="foo">');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogUrl).to.be.eql('bar');
      expect(ogObject).to.have.all.keys('ogUrl');
    });
    it('when there is a link tag', function () {
      const $ = cheerio.load('<link rel="canonical" href="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogUrl).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogUrl');
    });
    it('when there is a alt link tag', function () {
      const $ = cheerio.load('<link rel="alternate" hreflang="x-default" href="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogUrl).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogUrl');
    });
    it('when there is no ogUrl', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject).to.be.eql({});
    });
  });

  context('ogDate', function () {
    it('when there is a og date already found', function () {
      let ogObject = { ogDate: 'bar' };
      const $ = cheerio.load('<meta name="date" content="foo">');
      ogObject = fallback(ogObject, {}, $);
      expect(ogObject.ogDate).to.be.eql('bar');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is a meta date tag', function () {
      const $ = cheerio.load('<meta name="date" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDate).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is a meta datemodified tag', function () {
      const $ = cheerio.load('<meta itemprop="datemodified" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDate).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is a meta datepublished tag', function () {
      const $ = cheerio.load('<meta itemprop="datepublished" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDate).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is a meta date tag using itemprop', function () {
      const $ = cheerio.load('<meta itemprop="date" content="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDate).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is a time date tag using itemprop', function () {
      const $ = cheerio.load('<time itemprop="date" datetime="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDate).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is a time date tag using datetime', function () {
      const $ = cheerio.load('<time datetime="foo">');
      const ogObject = fallback({}, {}, $);
      expect(ogObject.ogDate).to.be.eql('foo');
      expect(ogObject).to.have.all.keys('ogDate');
    });
    it('when there is no ogDate', function () {
      const $ = cheerio.load('');
      const ogObject = fallback({}, {}, $);
      expect(ogObject).to.be.eql({});
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Created by kelvinzhang on 5/29/17.
 */

var canvas;
var bgCanvas;

var robotX;
var robotY;

var senseCircle = 0;
var senseRadius = 150;

var nLasers = 36;
var scanned = false;

//Results of laser scan
var z = new Array(nLasers);

function click()
{
    var coor = getClickLoc(event);
    robotX = coor.x;
    robotY = coor.y;
    senseCircle = 0;
    scanned = false;
}

function scan()
{
    //Radian between each laser
    var rad = Math.PI*2/nLasers;

    for(var i = 0; i < nLasers; i ++)
    {
        var dir = i * rad;
        var s1 = new Point(robotX, robotY);
        var t1 = new Point(robotX + cos(dir)*senseRadius, robotY + sin(dir)*senseRadius);
        z[i] = senseRadius + 100;
        for(var j = 0; j < map.length; j ++)
        {
            if(doIntersect(s1, t1, map[j].s, map[j].t))
            {
                // z[i] = senseRadius;
                var p = intersectionPoint(s1, t1, map[j].s, map[j].t);
                var dist = p.distanceTo(new Point(robotX, robotY));
                z[i] = min(z[i], dist);
            }
        }
    }
}

function update()
{
    if(!scanned)
    {
        scan();
        scanned = true;
    }
    senseCircle++;
    senseCircle %= senseRadius;
}

function draw(ctx)
{
    //ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.strokeStyle = 'black';
    ctx.drawRobot(robotX, robotY, 0, 20);

    //Radian between each laser
    var rad = Math.PI*2/nLasers;
    for(var i = 0; i < nLasers; i ++)
    {
        var dir = i * rad;
        var laserLen = senseCircle;
        if(z[i] > senseRadius)
        {
            ctx.strokeStyle = 'grey';
        }else
        {
            laserLen = min(laserLen, z[i]);
            ctx.strokeStyle = 'red';
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.arc(robotX + cos(dir)*z[i], robotY + sin(dir)*z[i], 5, 0, Math.PI*2);
            ctx.fill();
        }
        ctx.beginPath();
        ctx.moveTo(robotX, robotY);
        ctx.lineTo(robotX + cos(dir)*laserLen, robotY + sin(dir)*laserLen);
        ctx.stroke();
    }
}

function frame()
{
    var ctx = canvas.getContext('2d');
    update();
    draw(ctx);
    requestAnimationFrame(frame);
}

function parameterChanged(event)
{
    senseRadius = Number(event.target.value)/0.02;
}

function init()
{
    canvas = document.getElementById('canvas');
    bgCanvas = document.getElementById('background');

    //Draw the map
    var map = getMapForCanvas(canvas);
    drawMap(map, bgCanvas.getContext('2d'));
    robotX = floor(random()*canvas.width);
    robotY = floor(random()*canvas.height);

    //Listen to mouse click events
    background.addEventListener('click', click);
    senseRadius = getValue('fogOfWar')/0.02;
    requestAnimationFrame(frame);
}<commit_msg>fix weird floating point precision bug<commit_after>/**
 * Created by kelvinzhang on 5/29/17.
 */

var canvas;
var bgCanvas;

var robotX;
var robotY;

var senseCircle = 0;
var senseRadius = 150;

var nLasers = 36;
var scanned = false;

//Results of laser scan
var z = new Array(nLasers);

function click()
{
    var coor = getClickLoc(event);
    robotX = coor.x;
    robotY = coor.y;
    senseCircle = 0;
    scanned = false;
}

function scan()
{
    //Radian between each laser
    for(var i = 0; i < nLasers; i ++)
    {
        var dir = Math.PI*2 * i / nLasers;

        var s1 = new Point(robotX, robotY);
        var t1 = new Point(robotX + cos(dir)*senseRadius, robotY + sin(dir)*senseRadius);

        //If dir is 90 degrees
        if(i * 4 === nLasers || i * 4 === nLasers * 3)
            t1.x = robotX;
        else if(i * 2 === nLasers)
            t1.y = robotY;

        z[i] = senseRadius + 100;
        for(var j = 0; j < map.length; j ++)
        {
            if(doIntersect(s1, t1, map[j].s, map[j].t))
            {
                // z[i] = senseRadius;
                var p = intersectionPoint(s1, t1, map[j].s, map[j].t);
                var dist = p.distanceTo(new Point(robotX, robotY));
                z[i] = min(z[i], dist);
            }
        }
    }
}

function update()
{
    if(!scanned)
    {
        scan();
        scanned = true;
    }
    senseCircle++;
    senseCircle %= senseRadius;
}

function draw(ctx)
{
    //ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    ctx.strokeStyle = 'black';
    ctx.drawRobot(robotX, robotY, 0, 20);

    //Radian between each laser
    var rad = Math.PI*2/nLasers;
    for(var i = 0; i < nLasers; i ++)
    {
        var dir = i * rad;
        var laserLen = senseCircle;
        if(z[i] > senseRadius)
        {
            ctx.strokeStyle = 'grey';
        }else
        {
            laserLen = min(laserLen, z[i]);
            ctx.strokeStyle = 'red';
            ctx.fillStyle = 'red';
            ctx.beginPath();
            ctx.arc(robotX + cos(dir)*z[i], robotY + sin(dir)*z[i], 5, 0, Math.PI*2);
            ctx.fill();
        }
        ctx.beginPath();
        ctx.moveTo(robotX, robotY);
        ctx.lineTo(robotX + cos(dir)*laserLen, robotY + sin(dir)*laserLen);
        ctx.stroke();
    }
}

function frame()
{
    var ctx = canvas.getContext('2d');
    update();
    draw(ctx);
    requestAnimationFrame(frame);
}

function parameterChanged(event)
{
    senseRadius = Number(event.target.value)/0.02;
}

function init()
{
    canvas = document.getElementById('canvas');
    bgCanvas = document.getElementById('background');

    //Draw the map
    var map = getMapForCanvas(canvas);
    drawMap(map, bgCanvas.getContext('2d'));
    robotX = floor(random()*canvas.width);
    robotY = floor(random()*canvas.height);

    //Listen to mouse click events
    background.addEventListener('click', click);
    senseRadius = getValue('fogOfWar')/0.02;
    requestAnimationFrame(frame);
}<|endoftext|>


<commit_before>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.HowToVideoPanel');

/**
 * @constructor
 * @param {mirosubs.HowToVideoPanel.VideoChoice} videoChoice
 */
mirosubs.HowToVideoPanel = function(videoChoice) {
    goog.ui.Component.call(this);
    if (mirosubs.video.supportsOgg())
        this.videoPlayer_ = videoChoice[0].createPlayer();
    else if (mirosubs.video.supportsH264())
        this.videoPlayer_ = videoChoice[1].createPlayer();
    else
        this.videoPlayer_ = videoChoice[2].createPlayer();
    this.usingHtml5Video_ =
        mirosubs.video.supportsOgg() ||
        mirosubs.video.supportsH264();
};
goog.inherits(mirosubs.HowToVideoPanel, goog.ui.Component);

mirosubs.HowToVideoPanel.CONTINUE = 'continue';
mirosubs.HowToVideoPanel.VideoChoice = {
    TRANSCRIBE: [
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-overviewtypingogg899.ogv'),
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-overviewtypingmp4598.m4v'),
        new mirosubs.video.YoutubeVideoSource('w7jmT6o0Nk0')
    ],
    SYNC: [
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-syncingogg217.ogv'),
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-syncingmp4898.m4v'),
        new mirosubs.video.YoutubeVideoSource('w7jmT6o0Nk0')],
    REVIEW: [
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-reviewogg196.ogv'),
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-reviewmp4769.m4v'),
        new mirosubs.video.YoutubeVideoSource('w7jmT6o0Nk0')]
};

mirosubs.HowToVideoPanel.HTML5_VIDEO_SIZE_ =
    new goog.math.Size(720, 540);

mirosubs.HowToVideoPanel.prototype.getContentElement = function() {
    return this.contentElement_;
};

mirosubs.HowToVideoPanel.prototype.createDom = function() {
    mirosubs.HowToVideoPanel.superClass_.createDom.call(this);
    var $d = goog.bind(this.getDomHelper().createDom, this.getDomHelper());
    this.contentElement_ = $d('div');
    var el = this.getElement();
    el.className = 'mirosubs-howtopanel';
    el.appendChild($d('h2', null, 'How-To Video'));
    el.appendChild(this.contentElement_);
    this.skipVideosSpan_ = $d('span');
    el.appendChild($d('div', null, this.skipVideosSpan_,
                      goog.dom.createTextNode(' Skip these videos')));
    this.continueLink_ =
        $d('a',
           {'className': 'mirosubs-smallButton',
            'href': '#'},
           $d('span', null, 'Continue'))
    el.appendChild(this.continueLink_);
    var vidPlayer = new goog.ui.Component();
    vidPlayer.addChild(this.videoPlayer_, true);
    if (this.usingHtml5Video_) {
        var viewportSize = goog.dom.getViewportSize();
        var videoTop =
            Math.max(0, goog.style.getClientLeftTop(
                this.videoPlayer_.getElement()).y);
        var videoSize = mirosubs.HowToVideoPanel.HTML5_VIDEO_SIZE_;
        if (videoTop + videoSize.height > viewportSize.height - 60) {
            var newVideoHeight = Math.max(270, viewportSize.height - videoTop - 60);
            this.videoPlayer_.setVideoSize(
                videoSize.width * newVideoHeight / videoSize.height,
                newVideoHeight);
        }
    }
    this.addChild(vidPlayer, true);
};

mirosubs.HowToVideoPanel.prototype.enterDocument = function() {
    mirosubs.HowToVideoPanel.superClass_.enterDocument.call(this);
    if (!this.skipVideosCheckbox_) {
        this.skipVideosCheckbox_ = new goog.ui.Checkbox();
        this.skipVideosCheckbox_.decorate(this.skipVideosSpan_);
        this.skipVideosCheckbox_.setLabel(
            this.skipVideosCheckbox_.getElement().parentNode);
    }
    this.getHandler().listen(this.skipVideosCheckbox_,
                             goog.ui.Component.EventType.CHANGE,
                             this.skipVideosCheckboxChanged_);
    this.getHandler().listen(this.continueLink_, 'click', this.continue_);
};

mirosubs.HowToVideoPanel.prototype.skipVideosCheckboxChanged_ = function(e) {
    mirosubs.UserSettings.setBooleanValue(
        mirosubs.UserSettings.Settings.SKIP_HOWTO_VIDEO,
        this.skipVideosCheckbox_.getChecked());
};

mirosubs.HowToVideoPanel.prototype.continue_ = function(e) {
    e.preventDefault();
    this.dispatchEvent(mirosubs.HowToVideoPanel.CONTINUE);
};

mirosubs.HowToVideoPanel.prototype.disposeInternal = function() {
    mirosubs.HowToVideoPanel.superClass_.disposeInternal.call(this);
    this.videoPlayer_.pause();
    this.videoPlayer_.dispose();
};<commit_msg>How-to panel video starts playing as soon as it's opened<commit_after>// Universal Subtitles, universalsubtitles.org
//
// Copyright (C) 2010 Participatory Culture Foundation
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see
// http://www.gnu.org/licenses/agpl-3.0.html.

goog.provide('mirosubs.HowToVideoPanel');

/**
 * @constructor
 * @param {mirosubs.HowToVideoPanel.VideoChoice} videoChoice
 */
mirosubs.HowToVideoPanel = function(videoChoice) {
    goog.ui.Component.call(this);
    if (mirosubs.video.supportsOgg())
        this.videoPlayer_ = videoChoice[0].createPlayer();
    else if (mirosubs.video.supportsH264())
        this.videoPlayer_ = videoChoice[1].createPlayer();
    else
        this.videoPlayer_ = videoChoice[2].createPlayer();
    this.usingHtml5Video_ =
        mirosubs.video.supportsOgg() ||
        mirosubs.video.supportsH264();
};
goog.inherits(mirosubs.HowToVideoPanel, goog.ui.Component);

mirosubs.HowToVideoPanel.CONTINUE = 'continue';
mirosubs.HowToVideoPanel.VideoChoice = {
    TRANSCRIBE: [
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-overviewtypingogg899.ogv'),
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-overviewtypingmp4598.m4v'),
        new mirosubs.video.YoutubeVideoSource('w7jmT6o0Nk0')
    ],
    SYNC: [
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-syncingogg217.ogv'),
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-syncingmp4898.m4v'),
        new mirosubs.video.YoutubeVideoSource('w7jmT6o0Nk0')],
    REVIEW: [
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-reviewogg196.ogv'),
        new mirosubs.video.Html5VideoSource(
            'http://blip.tv/file/get/Miropcf-reviewmp4769.m4v'),
        new mirosubs.video.YoutubeVideoSource('w7jmT6o0Nk0')]
};

mirosubs.HowToVideoPanel.HTML5_VIDEO_SIZE_ =
    new goog.math.Size(720, 540);

mirosubs.HowToVideoPanel.prototype.getContentElement = function() {
    return this.contentElement_;
};

mirosubs.HowToVideoPanel.prototype.createDom = function() {
    mirosubs.HowToVideoPanel.superClass_.createDom.call(this);
    var $d = goog.bind(this.getDomHelper().createDom, this.getDomHelper());
    this.contentElement_ = $d('div');
    var el = this.getElement();
    el.className = 'mirosubs-howtopanel';
    el.appendChild($d('h2', null, 'How-To Video'));
    el.appendChild(this.contentElement_);
    this.skipVideosSpan_ = $d('span');
    el.appendChild($d('div', null, this.skipVideosSpan_,
                      goog.dom.createTextNode(' Skip these videos')));
    this.continueLink_ =
        $d('a',
           {'className': 'mirosubs-smallButton',
            'href': '#'},
           $d('span', null, 'Continue'))
    el.appendChild(this.continueLink_);
    var vidPlayer = new goog.ui.Component();
    vidPlayer.addChild(this.videoPlayer_, true);
    if (this.usingHtml5Video_) {
        var viewportSize = goog.dom.getViewportSize();
        var videoTop =
            Math.max(0, goog.style.getClientLeftTop(
                this.videoPlayer_.getElement()).y);
        var videoSize = mirosubs.HowToVideoPanel.HTML5_VIDEO_SIZE_;
        if (videoTop + videoSize.height > viewportSize.height - 60) {
            var newVideoHeight = Math.max(270, viewportSize.height - videoTop - 60);
            this.videoPlayer_.setVideoSize(
                videoSize.width * newVideoHeight / videoSize.height,
                newVideoHeight);
        }
    }
    this.addChild(vidPlayer, true);
};

mirosubs.HowToVideoPanel.prototype.enterDocument = function() {
    mirosubs.HowToVideoPanel.superClass_.enterDocument.call(this);
    if (!this.skipVideosCheckbox_) {
        this.skipVideosCheckbox_ = new goog.ui.Checkbox();
        this.skipVideosCheckbox_.decorate(this.skipVideosSpan_);
        this.skipVideosCheckbox_.setLabel(
            this.skipVideosCheckbox_.getElement().parentNode);
    }
    this.getHandler().listen(this.skipVideosCheckbox_,
                             goog.ui.Component.EventType.CHANGE,
                             this.skipVideosCheckboxChanged_);
    this.getHandler().listen(this.continueLink_, 'click', this.continue_);
    this.videoPlayer_.play();
};

mirosubs.HowToVideoPanel.prototype.skipVideosCheckboxChanged_ = function(e) {
    mirosubs.UserSettings.setBooleanValue(
        mirosubs.UserSettings.Settings.SKIP_HOWTO_VIDEO,
        this.skipVideosCheckbox_.getChecked());
};

mirosubs.HowToVideoPanel.prototype.continue_ = function(e) {
    e.preventDefault();
    this.dispatchEvent(mirosubs.HowToVideoPanel.CONTINUE);
};

mirosubs.HowToVideoPanel.prototype.disposeInternal = function() {
    mirosubs.HowToVideoPanel.superClass_.disposeInternal.call(this);
    this.videoPlayer_.pause();
    this.videoPlayer_.dispose();
};<|endoftext|>