<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @noflow
 */

import { describe, it } from 'mocha';
import { expect } from 'chai';
import dedent from '../../jsutils/dedent';
import { buildClientSchema } from '../buildClientSchema';
import { introspectionFromSchema } from '../introspectionFromSchema';
import {
  buildSchema,
  printSchema,
  graphqlSync,
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLEnumType,
  GraphQLInt,
  GraphQLFloat,
  GraphQLString,
  GraphQLBoolean,
  GraphQLID,
} from '../../';

/**
 * This function does a full cycle of going from a string with the contents of
 * the SDL, build in-memory GraphQLSchema from it, produce a client-side
 * representation of the schema by using "buildClientSchema"and then finally
 * printing that that schema into the SDL
 */
function cycleIntrospection(sdlString) {
  const serverSchema = buildSchema(sdlString);
  const initialIntrospection = introspectionFromSchema(serverSchema);
  const clientSchema = buildClientSchema(initialIntrospection);

  /**
   * If the client then runs the introspection query against the client-side
   * schema, it should get a result identical to what was returned by the server
   */
  const secondIntrospection = introspectionFromSchema(clientSchema);
  expect(secondIntrospection).to.deep.equal(initialIntrospection);

  return printSchema(clientSchema);
}

describe('Type System: build schema from introspection', () => {
  it('builds a simple schema', () => {
    const sdl = dedent`
      schema {
        query: Simple
      }

      """This is simple type"""
      type Simple {
        """This is a string field"""
        string: String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a simple schema with all operation types', () => {
    const sdl = dedent`
      schema {
        query: QueryType
        mutation: MutationType
        subscription: SubscriptionType
      }

      """This is a simple mutation type"""
      type MutationType {
        """Set the string field"""
        string: String
      }

      """This is a simple query type"""
      type QueryType {
        """This is a string field"""
        string: String
      }

      """This is a simple subscription type"""
      type SubscriptionType {
        """This is a string field"""
        string: String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('uses built-in scalars when possible', () => {
    const sdl = dedent`
      scalar CustomScalar

      type Query {
        int: Int
        float: Float
        string: String
        boolean: Boolean
        id: ID
        custom: CustomScalar
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);

    const schema = buildSchema(sdl);
    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection);

    // Built-ins are used
    expect(clientSchema.getType('Int')).to.equal(GraphQLInt);
    expect(clientSchema.getType('Float')).to.equal(GraphQLFloat);
    expect(clientSchema.getType('String')).to.equal(GraphQLString);
    expect(clientSchema.getType('Boolean')).to.equal(GraphQLBoolean);
    expect(clientSchema.getType('ID')).to.equal(GraphQLID);

    // Custom are built
    const customScalar = schema.getType('CustomScalar');
    expect(clientSchema.getType('CustomScalar')).not.to.equal(customScalar);
  });

  it('builds a schema with a recursive type reference', () => {
    const sdl = dedent`
      schema {
        query: Recur
      }

      type Recur {
        recur: Recur
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with a circular type reference', () => {
    const sdl = dedent`
      type Dog {
        bestFriend: Human
      }

      type Human {
        bestFriend: Dog
      }

      type Query {
        dog: Dog
        human: Human
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with an interface', () => {
    const sdl = dedent`
      type Dog implements Friendly {
        bestFriend: Friendly
      }

      interface Friendly {
        """The best friend of this friendly thing"""
        bestFriend: Friendly
      }

      type Human implements Friendly {
        bestFriend: Friendly
      }

      type Query {
        friendly: Friendly
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with an implicit interface', () => {
    const sdl = dedent`
      type Dog implements Friendly {
        bestFriend: Friendly
      }

      interface Friendly {
        """The best friend of this friendly thing"""
        bestFriend: Friendly
      }

      type Query {
        dog: Dog
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with a union', () => {
    const sdl = dedent`
      type Dog {
        bestFriend: Friendly
      }

      union Friendly = Dog | Human

      type Human {
        bestFriend: Friendly
      }

      type Query {
        friendly: Friendly
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with complex field values', () => {
    const sdl = dedent`
      type Query {
        string: String
        listOfString: [String]
        nonNullString: String!
        nonNullListOfString: [String]!
        nonNullListOfNonNullString: [String!]!
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with field arguments', () => {
    const sdl = dedent`
      type Query {
        """A field with a single arg"""
        one(
          """This is an int arg"""
          intArg: Int
        ): String

        """A field with a two args"""
        two(
          """This is an list of int arg"""
          listArg: [Int]

          """This is a required arg"""
          requiredArg: Boolean!
        ): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with default value on custom scalar field', () => {
    const sdl = dedent`
      scalar CustomScalar

      type Query {
        testField(testArg: CustomScalar = "default"): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with an enum', () => {
    const foodEnum = new GraphQLEnumType({
      name: 'Food',
      description: 'Varieties of food stuffs',
      values: {
        VEGETABLES: {
          description: 'Foods that are vegetables.',
          value: 1,
        },
        FRUITS: {
          description: 'Foods that are fruits.',
          value: 2,
        },
        OILS: {
          description: 'Foods that are oils.',
          value: 3,
        },
        DAIRY: {
          description: 'Foods that are dairy.',
          value: 4,
        },
        MEAT: {
          description: 'Foods that are meat.',
          value: 5,
        },
      },
    });
    const schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'EnumFields',
        fields: {
          food: {
            description: 'Repeats the arg you give it',
            type: foodEnum,
            args: {
              kind: {
                description: 'what kind of food?',
                type: foodEnum,
              },
            },
          },
        },
      }),
    });

    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection);
    const secondIntrospection = introspectionFromSchema(clientSchema);
    expect(secondIntrospection).to.deep.equal(introspection);

    const clientFoodEnum = clientSchema.getType('Food');

    // It's also an Enum type on the client.
    expect(clientFoodEnum).to.be.an.instanceOf(GraphQLEnumType);

    // Client types do not get server-only values, so `value` mirrors `name`,
    // rather than using the integers defined in the "server" schema.
    expect(clientFoodEnum.getValues()).to.deep.equal([
      {
        name: 'VEGETABLES',
        value: 'VEGETABLES',
        description: 'Foods that are vegetables.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'FRUITS',
        value: 'FRUITS',
        description: 'Foods that are fruits.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'OILS',
        value: 'OILS',
        description: 'Foods that are oils.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'DAIRY',
        value: 'DAIRY',
        description: 'Foods that are dairy.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'MEAT',
        value: 'MEAT',
        description: 'Foods that are meat.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
    ]);
  });

  it('builds a schema with an input object', () => {
    const sdl = dedent`
      """An input address"""
      input Address {
        """What street is this address?"""
        street: String!

        """The city the address is within?"""
        city: String!

        """The country (blank will assume USA)."""
        country: String = "USA"
      }

      type Query {
        """Get a geocode from an address"""
        geocode(
          """The address to lookup"""
          address: Address
        ): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with field arguments with default values', () => {
    const sdl = dedent`
      input Geo {
        lat: Float
        lon: Float
      }

      type Query {
        defaultInt(intArg: Int = 30): String
        defaultList(listArg: [Int] = [1, 2, 3]): String
        defaultObject(objArg: Geo = {lat: 37.485, lon: -122.148}): String
        defaultNull(intArg: Int = null): String
        noDefault(intArg: Int): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with custom directives', () => {
    const sdl = dedent`
      """This is a custom directive"""
      directive @customDirective on FIELD

      type Query {
        string: String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with legacy names', () => {
    const introspection = {
      __schema: {
        queryType: {
          name: 'Query',
        },
        types: [
          {
            name: 'Query',
            kind: 'OBJECT',
            fields: [
              {
                name: '__badName',
                args: [],
                type: { name: 'String' },
              },
            ],
            interfaces: [],
          },
        ],
      },
    };
    const schema = buildClientSchema(introspection, {
      allowedLegacyNames: ['__badName'],
    });
    expect(schema.__allowedLegacyNames).to.deep.equal(['__badName']);
  });

  it('builds a schema aware of deprecation', () => {
    const sdl = dedent`
      enum Color {
        """So rosy"""
        RED

        """So grassy"""
        GREEN

        """So calming"""
        BLUE

        """So sickening"""
        MAUVE @deprecated(reason: "No longer in fashion")
      }

      type Query {
        """This is a shiny string field"""
        shinyString: String

        """This is a deprecated string field"""
        deprecatedString: String @deprecated(reason: "Use shinyString")
        color: Color
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('can use client schema for limited execution', () => {
    const schema = buildSchema(`
      scalar CustomScalar

      type Query {
        foo(custom1: CustomScalar, custom2: CustomScalar): String
      }
    `);

    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection);

    const result = graphqlSync(
      clientSchema,
      'query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }',
      { foo: 'bar', unused: 'value' },
      null,
      { v: 'baz' },
    );

    expect(result.data).to.deep.equal({ foo: 'bar' });
  });

  describe('throws when given incomplete introspection', () => {
    it('throws when given empty types', () => {
      const incompleteIntrospection = {
        __schema: {
          queryType: { name: 'QueryType' },
          types: [],
        },
      };

      expect(() => buildClientSchema(incompleteIntrospection)).to.throw(
        'Invalid or incomplete schema, unknown type: QueryType. Ensure ' +
          'that a full introspection query is used in order to build a ' +
          'client schema.',
      );
    });

    it('throws when missing kind', () => {
      const incompleteIntrospection = {
        __schema: {
          queryType: { name: 'QueryType' },
          types: [{ name: 'QueryType' }],
        },
      };

      expect(() => buildClientSchema(incompleteIntrospection)).to.throw(
        'Invalid or incomplete introspection result. Ensure that a full ' +
          'introspection query is used in order to build a client schema',
      );
    });

    it('throws when missing interfaces', () => {
      const nullInterfaceIntrospection = {
        __schema: {
          queryType: { name: 'QueryType' },
          types: [
            {
              kind: 'OBJECT',
              name: 'QueryType',
              fields: [
                {
                  name: 'aString',
                  args: [],
                  type: { kind: 'SCALAR', name: 'String', ofType: null },
                  isDeprecated: false,
                },
              ],
            },
          ],
        },
      };

      expect(() => buildClientSchema(nullInterfaceIntrospection)).to.throw(
        'Introspection result missing interfaces: ' +
          '{ kind: "OBJECT", name: "QueryType", fields: [{ name: "aString", args: [], type: { kind: "SCALAR", name: "String", ofType: null }, isDeprecated: false }] }',
      );
    });

    it('throws when missing directive locations', () => {
      const introspection = {
        __schema: {
          types: [],
          directives: [{ name: 'test', args: [] }],
        },
      };

      expect(() => buildClientSchema(introspection)).to.throw(
        'Introspection result missing directive locations: ' +
          '{ name: "test", args: [] }',
      );
    });
  });

  describe('very deep decorators are not supported', () => {
    it('fails on very deep (> 7 levels) lists', () => {
      const schema = buildSchema(`
        type Query {
          foo: [[[[[[[[String]]]]]]]]
        }
      `);

      const introspection = introspectionFromSchema(schema);
      expect(() => buildClientSchema(introspection)).to.throw(
        'Decorated type deeper than introspection query.',
      );
    });

    it('fails on a very deep (> 7 levels) non-null', () => {
      const schema = buildSchema(`
        type Query {
          foo: [[[[String!]!]!]!]
        }
      `);

      const introspection = introspectionFromSchema(schema);
      expect(() => buildClientSchema(introspection)).to.throw(
        'Decorated type deeper than introspection query.',
      );
    });

    it('succeeds on deep (<= 7 levels) types', () => {
      // e.g., fully non-null 3D matrix
      const sdl = dedent`
        type Query {
          foo: [[[String!]!]!]!
        }
      `;

      expect(cycleIntrospection(sdl)).to.equal(sdl);
    });
  });

  describe('prevents infinite recursion on invalid introspection', () => {
    it('recursive interfaces', () => {
      const introspection = {
        __schema: {
          types: [
            {
              name: 'Foo',
              kind: 'OBJECT',
              fields: [],
              interfaces: [{ name: 'Foo' }],
            },
          ],
        },
      };
      expect(() => buildClientSchema(introspection)).to.throw(
        'Expected Foo to be a GraphQL Interface type.',
      );
    });

    it('recursive union', () => {
      const introspection = {
        __schema: {
          types: [
            {
              name: 'Foo',
              kind: 'UNION',
              possibleTypes: [{ name: 'Foo' }],
            },
          ],
        },
      };
      expect(() => buildClientSchema(introspection)).to.throw(
        'Expected Foo to be a GraphQL Object type.',
      );
    });
  });
});
<commit_msg>Enable Flow on buildClientSchema tests (#1665)<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict
 */

import { describe, it } from 'mocha';
import { expect } from 'chai';
import dedent from '../../jsutils/dedent';
import invariant from '../../jsutils/invariant';
import { buildClientSchema } from '../buildClientSchema';
import { introspectionFromSchema } from '../introspectionFromSchema';
import {
  buildSchema,
  printSchema,
  graphqlSync,
  isEnumType,
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLEnumType,
  GraphQLInt,
  GraphQLFloat,
  GraphQLString,
  GraphQLBoolean,
  GraphQLID,
} from '../../';

/**
 * This function does a full cycle of going from a string with the contents of
 * the SDL, build in-memory GraphQLSchema from it, produce a client-side
 * representation of the schema by using "buildClientSchema"and then finally
 * printing that that schema into the SDL
 */
function cycleIntrospection(sdlString) {
  const serverSchema = buildSchema(sdlString);
  const initialIntrospection = introspectionFromSchema(serverSchema);
  const clientSchema = buildClientSchema(initialIntrospection);

  /**
   * If the client then runs the introspection query against the client-side
   * schema, it should get a result identical to what was returned by the server
   */
  const secondIntrospection = introspectionFromSchema(clientSchema);
  expect(secondIntrospection).to.deep.equal(initialIntrospection);

  return printSchema(clientSchema);
}

describe('Type System: build schema from introspection', () => {
  it('builds a simple schema', () => {
    const sdl = dedent`
      schema {
        query: Simple
      }

      """This is simple type"""
      type Simple {
        """This is a string field"""
        string: String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a simple schema with all operation types', () => {
    const sdl = dedent`
      schema {
        query: QueryType
        mutation: MutationType
        subscription: SubscriptionType
      }

      """This is a simple mutation type"""
      type MutationType {
        """Set the string field"""
        string: String
      }

      """This is a simple query type"""
      type QueryType {
        """This is a string field"""
        string: String
      }

      """This is a simple subscription type"""
      type SubscriptionType {
        """This is a string field"""
        string: String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('uses built-in scalars when possible', () => {
    const sdl = dedent`
      scalar CustomScalar

      type Query {
        int: Int
        float: Float
        string: String
        boolean: Boolean
        id: ID
        custom: CustomScalar
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);

    const schema = buildSchema(sdl);
    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection);

    // Built-ins are used
    expect(clientSchema.getType('Int')).to.equal(GraphQLInt);
    expect(clientSchema.getType('Float')).to.equal(GraphQLFloat);
    expect(clientSchema.getType('String')).to.equal(GraphQLString);
    expect(clientSchema.getType('Boolean')).to.equal(GraphQLBoolean);
    expect(clientSchema.getType('ID')).to.equal(GraphQLID);

    // Custom are built
    const customScalar = schema.getType('CustomScalar');
    expect(clientSchema.getType('CustomScalar')).not.to.equal(customScalar);
  });

  it('builds a schema with a recursive type reference', () => {
    const sdl = dedent`
      schema {
        query: Recur
      }

      type Recur {
        recur: Recur
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with a circular type reference', () => {
    const sdl = dedent`
      type Dog {
        bestFriend: Human
      }

      type Human {
        bestFriend: Dog
      }

      type Query {
        dog: Dog
        human: Human
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with an interface', () => {
    const sdl = dedent`
      type Dog implements Friendly {
        bestFriend: Friendly
      }

      interface Friendly {
        """The best friend of this friendly thing"""
        bestFriend: Friendly
      }

      type Human implements Friendly {
        bestFriend: Friendly
      }

      type Query {
        friendly: Friendly
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with an implicit interface', () => {
    const sdl = dedent`
      type Dog implements Friendly {
        bestFriend: Friendly
      }

      interface Friendly {
        """The best friend of this friendly thing"""
        bestFriend: Friendly
      }

      type Query {
        dog: Dog
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with a union', () => {
    const sdl = dedent`
      type Dog {
        bestFriend: Friendly
      }

      union Friendly = Dog | Human

      type Human {
        bestFriend: Friendly
      }

      type Query {
        friendly: Friendly
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with complex field values', () => {
    const sdl = dedent`
      type Query {
        string: String
        listOfString: [String]
        nonNullString: String!
        nonNullListOfString: [String]!
        nonNullListOfNonNullString: [String!]!
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with field arguments', () => {
    const sdl = dedent`
      type Query {
        """A field with a single arg"""
        one(
          """This is an int arg"""
          intArg: Int
        ): String

        """A field with a two args"""
        two(
          """This is an list of int arg"""
          listArg: [Int]

          """This is a required arg"""
          requiredArg: Boolean!
        ): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with default value on custom scalar field', () => {
    const sdl = dedent`
      scalar CustomScalar

      type Query {
        testField(testArg: CustomScalar = "default"): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with an enum', () => {
    const foodEnum = new GraphQLEnumType({
      name: 'Food',
      description: 'Varieties of food stuffs',
      values: {
        VEGETABLES: {
          description: 'Foods that are vegetables.',
          value: 1,
        },
        FRUITS: {
          description: 'Foods that are fruits.',
          value: 2,
        },
        OILS: {
          description: 'Foods that are oils.',
          value: 3,
        },
        DAIRY: {
          description: 'Foods that are dairy.',
          value: 4,
        },
        MEAT: {
          description: 'Foods that are meat.',
          value: 5,
        },
      },
    });
    const schema = new GraphQLSchema({
      query: new GraphQLObjectType({
        name: 'EnumFields',
        fields: {
          food: {
            description: 'Repeats the arg you give it',
            type: foodEnum,
            args: {
              kind: {
                description: 'what kind of food?',
                type: foodEnum,
              },
            },
          },
        },
      }),
    });

    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection);
    const secondIntrospection = introspectionFromSchema(clientSchema);
    expect(secondIntrospection).to.deep.equal(introspection);

    const clientFoodEnum = clientSchema.getType('Food');

    // It's also an Enum type on the client.
    invariant(isEnumType(clientFoodEnum));

    // Client types do not get server-only values, so `value` mirrors `name`,
    // rather than using the integers defined in the "server" schema.
    expect(clientFoodEnum.getValues()).to.deep.equal([
      {
        name: 'VEGETABLES',
        value: 'VEGETABLES',
        description: 'Foods that are vegetables.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'FRUITS',
        value: 'FRUITS',
        description: 'Foods that are fruits.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'OILS',
        value: 'OILS',
        description: 'Foods that are oils.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'DAIRY',
        value: 'DAIRY',
        description: 'Foods that are dairy.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
      {
        name: 'MEAT',
        value: 'MEAT',
        description: 'Foods that are meat.',
        isDeprecated: false,
        deprecationReason: null,
        astNode: undefined,
      },
    ]);
  });

  it('builds a schema with an input object', () => {
    const sdl = dedent`
      """An input address"""
      input Address {
        """What street is this address?"""
        street: String!

        """The city the address is within?"""
        city: String!

        """The country (blank will assume USA)."""
        country: String = "USA"
      }

      type Query {
        """Get a geocode from an address"""
        geocode(
          """The address to lookup"""
          address: Address
        ): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with field arguments with default values', () => {
    const sdl = dedent`
      input Geo {
        lat: Float
        lon: Float
      }

      type Query {
        defaultInt(intArg: Int = 30): String
        defaultList(listArg: [Int] = [1, 2, 3]): String
        defaultObject(objArg: Geo = {lat: 37.485, lon: -122.148}): String
        defaultNull(intArg: Int = null): String
        noDefault(intArg: Int): String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with custom directives', () => {
    const sdl = dedent`
      """This is a custom directive"""
      directive @customDirective on FIELD

      type Query {
        string: String
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('builds a schema with legacy names', () => {
    const sdl = dedent`
      type Query {
        __badName: String
      }
    `;
    const allowedLegacyNames = ['__badName'];
    const schema = buildSchema(sdl, { allowedLegacyNames });

    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection, {
      allowedLegacyNames,
    });

    expect(schema.__allowedLegacyNames).to.deep.equal(['__badName']);
    expect(printSchema(clientSchema)).to.equal(sdl);
  });

  it('builds a schema aware of deprecation', () => {
    const sdl = dedent`
      enum Color {
        """So rosy"""
        RED

        """So grassy"""
        GREEN

        """So calming"""
        BLUE

        """So sickening"""
        MAUVE @deprecated(reason: "No longer in fashion")
      }

      type Query {
        """This is a shiny string field"""
        shinyString: String

        """This is a deprecated string field"""
        deprecatedString: String @deprecated(reason: "Use shinyString")
        color: Color
      }
    `;

    expect(cycleIntrospection(sdl)).to.equal(sdl);
  });

  it('can use client schema for limited execution', () => {
    const schema = buildSchema(`
      scalar CustomScalar

      type Query {
        foo(custom1: CustomScalar, custom2: CustomScalar): String
      }
    `);

    const introspection = introspectionFromSchema(schema);
    const clientSchema = buildClientSchema(introspection);

    const result = graphqlSync(
      clientSchema,
      'query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }',
      { foo: 'bar', unused: 'value' },
      null,
      { v: 'baz' },
    );

    expect(result.data).to.deep.equal({ foo: 'bar' });
  });

  describe('throws when given incomplete introspection', () => {
    const dummySchema = buildSchema(`
      type Query {
        foo: String
      }

      directive @Foo on QUERY
    `);

    it('throws when given empty types', () => {
      const introspection = introspectionFromSchema(dummySchema);

      // $DisableFlowOnNegativeTest
      introspection.__schema.types = [];

      expect(() => buildClientSchema(introspection)).to.throw(
        'Invalid or incomplete schema, unknown type: Query. Ensure that a ' +
          'full introspection query is used in order to build a client schema.',
      );
    });

    it('throws when missing kind', () => {
      const introspection = introspectionFromSchema(dummySchema);

      const queryTypeIntrospection = introspection.__schema.types[0];
      expect(queryTypeIntrospection).to.deep.include({
        name: 'Query',
        kind: 'OBJECT',
      });
      // $DisableFlowOnNegativeTest
      delete queryTypeIntrospection.kind;

      expect(() => buildClientSchema(introspection)).to.throw(
        'Invalid or incomplete introspection result. Ensure that a full ' +
          'introspection query is used in order to build a client schema',
      );
    });

    it('throws when missing interfaces', () => {
      const introspection = introspectionFromSchema(dummySchema);

      const queryTypeIntrospection = introspection.__schema.types[0];
      expect(queryTypeIntrospection).to.deep.include({
        name: 'Query',
        interfaces: [],
      });
      // $DisableFlowOnNegativeTest
      delete queryTypeIntrospection.interfaces;

      expect(() => buildClientSchema(introspection)).to.throw(
        'Introspection result missing interfaces: ' +
          '{ kind: "OBJECT", name: "Query", description: null, fields: [{ name: "foo", description: null, args: [], type: { kind: "SCALAR", name: "String", ofType: null }, isDeprecated: false, deprecationReason: null }], inputFields: null, enumValues: null, possibleTypes: null }',
      );
    });

    it('throws when missing directive locations', () => {
      const introspection = introspectionFromSchema(dummySchema);

      const fooDirectiveIntrospection = introspection.__schema.directives[0];
      expect(fooDirectiveIntrospection).to.deep.include({
        name: 'Foo',
        locations: ['QUERY'],
      });
      // $DisableFlowOnNegativeTest
      delete fooDirectiveIntrospection.locations;

      expect(() => buildClientSchema(introspection)).to.throw(
        'Introspection result missing directive locations: ' +
          '{ name: "Foo", description: null, args: [] }',
      );
    });
  });

  describe('very deep decorators are not supported', () => {
    it('fails on very deep (> 7 levels) lists', () => {
      const schema = buildSchema(`
        type Query {
          foo: [[[[[[[[String]]]]]]]]
        }
      `);

      const introspection = introspectionFromSchema(schema);
      expect(() => buildClientSchema(introspection)).to.throw(
        'Decorated type deeper than introspection query.',
      );
    });

    it('fails on a very deep (> 7 levels) non-null', () => {
      const schema = buildSchema(`
        type Query {
          foo: [[[[String!]!]!]!]
        }
      `);

      const introspection = introspectionFromSchema(schema);
      expect(() => buildClientSchema(introspection)).to.throw(
        'Decorated type deeper than introspection query.',
      );
    });

    it('succeeds on deep (<= 7 levels) types', () => {
      // e.g., fully non-null 3D matrix
      const sdl = dedent`
        type Query {
          foo: [[[String!]!]!]!
        }
      `;

      expect(cycleIntrospection(sdl)).to.equal(sdl);
    });
  });

  describe('prevents infinite recursion on invalid introspection', () => {
    it('recursive interfaces', () => {
      const sdl = `
        type Query {
          foo: Foo
        }

        type Foo implements Foo {
          foo: String
        }
      `;
      const schema = buildSchema(sdl, { assumeValid: true });
      const introspection = introspectionFromSchema(schema);

      expect(introspection.__schema.types[1]).to.deep.include({
        name: 'Foo',
        interfaces: [{ kind: 'OBJECT', name: 'Foo', ofType: null }],
      });

      expect(() => buildClientSchema(introspection)).to.throw(
        'Expected Foo to be a GraphQL Interface type.',
      );
    });

    it('recursive union', () => {
      const sdl = `
        type Query {
          foo: Foo
        }

        union Foo = Foo
      `;
      const schema = buildSchema(sdl, { assumeValid: true });
      const introspection = introspectionFromSchema(schema);

      expect(introspection.__schema.types[1]).to.deep.include({
        name: 'Foo',
        possibleTypes: [{ kind: 'UNION', name: 'Foo', ofType: null }],
      });

      expect(() => buildClientSchema(introspection)).to.throw(
        'Expected Foo to be a GraphQL Object type.',
      );
    });
  });
});
<|endoftext|>


<commit_before>import { is } from '../utils'

export default {
  props: {
    src: {
      type: null,
      required: true
    },
    placeholder: {
      type: String
    },
    blur: {
      type: Number
    },
    aspectRatio: {
      type: Number
    },
    noRatio: {
      type: Boolean
    },
    fallback: {
      type: String
    }
  },

  data () {
    return {
      isRendered: false,
      options: {},
      defaultBlur: 20,
      image: null,
      placeholderImage: null,
      aspectRatioDetect: 0.5625,
      isPollingKilled: false,
      cached: false,
      imageError: false
    }
  },

  watch: {
    src () {
      this.handleImageLoading()
    }
  },

  computed: {
    shouldPlaceholderRender () {
      return !!this.placeholderImage
    },

    shouldImageRender () {
      return this.isRendered
    },

    calculatedRatio () {
      return this.aspectRatio || this.aspectRatioDetect
    },

    wrapperStyle () {
      if (this.noRatio) {
        return {}
      }

      return {
        paddingBottom: `${this.calculatedRatio * 100}%`
      }
    },

    blurStyle () {
      let blur = this.defaultBlur

      if (is(this.blur)) {
        return this.getBlurStyle(this.blur)
      }

      if (is(this.options.blur)) {
        return this.getBlurStyle(this.options.blur)
      }

      return this.getBlurStyle(blur)
    }
  },

  mounted () {
    this.handleImageLoading()
  },

  methods: {
    getBlurStyle (amount) {
      if (amount === 0) {
        return
      }

      return {
        filter: `blur(${amount}px)`
      }
    },

    defineAspectRatio (img) {
      const delay = this.options.timeout || 2500
      const pollInterval = this.options.pollInterval || 80

      const poll = setInterval(() => {
        if (!img.naturalWidth) {
          return
        }

        clearInterval(poll)

        const { naturalHeight, naturalWidth } = img

        this.aspectRatioDetect = naturalHeight / naturalWidth
      }, pollInterval)

      setTimeout(() => {
        if (img.naturalWidth) {
          return
        }

        clearInterval(poll)
        this.isPollingKilled = true
      }, delay)
    },

    loadImage () {
      const image = new Image()
      const delay = this.options.delay || 0
      const fallbackSrc = this.fallback || this.options.fallback
      console.log(fallbackSrc, this.imageError)
      const imageSource = this.imageError ? fallbackSrc : this.src

      // reset the image holder
      this.image = null
      this.isRendered = false

      if (!this.aspectRatio) {
        this.defineAspectRatio(image)
      }

      image.onload = () => {
        if (this.image) {
          return
        }

        if (this.isPollingKilled && !this.aspectRatio) {
          this.defineAspectRatio(image)
        }

        // assign the image
        this.image = imageSource

        let ctx
        try {
          // the drawImage it's a synchronous method, so when it's done
          // the nextTick will notify the view that we're ready
          // to fadeIn the main image
          ctx = this.$refs.canvas.getContext('2d')
          ctx.drawImage(this.$refs.main, 0, 0)
        } catch (e) {
          // see https://github.com/MatteoGabriele/vue-progressive-image/issues/30
        }

        // next tick to know when the image is rendered
        this.$nextTick(() => {
          // timeout for a custom delay
          setTimeout(() => {
            this.isRendered = true
          }, delay)
        })

        this.imageError = false

        this.$emit('onLoad', image.src)
      }

      image.onerror = error => {
        this.$emit('onError', error)

        if (process.env.NODE_ENV !== 'production' && !this.fallback) {
          console.warn('[vue-progressive-image] An error occured during the image loading')
        }

        if (this.fallback || this.options.fallback) {
          this.imageError = true
          this.handleImageLoading()
        }
      }

      image.src = imageSource
    },

    loadPlaceholder () {
      if (!this.placeholder && !this.options.placeholder) {
        return
      }

      const image = new Image()
      let src = this.placeholder

      if (this.options.placeholder && !this.placeholder) {
        src = this.options.placeholder
      }

      image.onload = () => {
        this.placeholderImage = src

        // Dispatches an event on placeholder image load
        this.$emit('onLoadPlaceholder', src)
      }

      image.onerror = error => {
        this.$emit('onPlaceholderError', error)

        if (process.env.NODE_ENV !== 'production') {
          console.warn('[vue-progressive-image] An error occured during the placeholder image loading')
        }
      }

      image.src = src
    },

    handleImageLoading () {
      this.loadPlaceholder()
      this.loadImage()
    }
  }
}
<commit_msg>fix(image): remove console logs<commit_after>import { is } from '../utils'

export default {
  props: {
    src: {
      type: null,
      required: true
    },
    placeholder: {
      type: String
    },
    blur: {
      type: Number
    },
    aspectRatio: {
      type: Number
    },
    noRatio: {
      type: Boolean
    },
    fallback: {
      type: String
    }
  },

  data () {
    return {
      isRendered: false,
      options: {},
      defaultBlur: 20,
      image: null,
      placeholderImage: null,
      aspectRatioDetect: 0.5625,
      isPollingKilled: false,
      cached: false,
      imageError: false
    }
  },

  watch: {
    src () {
      this.handleImageLoading()
    }
  },

  computed: {
    shouldPlaceholderRender () {
      return !!this.placeholderImage
    },

    shouldImageRender () {
      return this.isRendered
    },

    calculatedRatio () {
      return this.aspectRatio || this.aspectRatioDetect
    },

    wrapperStyle () {
      if (this.noRatio) {
        return {}
      }

      return {
        paddingBottom: `${this.calculatedRatio * 100}%`
      }
    },

    blurStyle () {
      let blur = this.defaultBlur

      if (is(this.blur)) {
        return this.getBlurStyle(this.blur)
      }

      if (is(this.options.blur)) {
        return this.getBlurStyle(this.options.blur)
      }

      return this.getBlurStyle(blur)
    }
  },

  mounted () {
    this.handleImageLoading()
  },

  methods: {
    getBlurStyle (amount) {
      if (amount === 0) {
        return
      }

      return {
        filter: `blur(${amount}px)`
      }
    },

    defineAspectRatio (img) {
      const delay = this.options.timeout || 2500
      const pollInterval = this.options.pollInterval || 80

      const poll = setInterval(() => {
        if (!img.naturalWidth) {
          return
        }

        clearInterval(poll)

        const { naturalHeight, naturalWidth } = img

        this.aspectRatioDetect = naturalHeight / naturalWidth
      }, pollInterval)

      setTimeout(() => {
        if (img.naturalWidth) {
          return
        }

        clearInterval(poll)
        this.isPollingKilled = true
      }, delay)
    },

    loadImage () {
      const image = new Image()
      const delay = this.options.delay || 0
      const fallbackSrc = this.fallback || this.options.fallback
      const imageSource = this.imageError ? fallbackSrc : this.src

      // reset the image holder
      this.image = null
      this.isRendered = false

      if (!this.aspectRatio) {
        this.defineAspectRatio(image)
      }

      image.onload = () => {
        if (this.image) {
          return
        }

        if (this.isPollingKilled && !this.aspectRatio) {
          this.defineAspectRatio(image)
        }

        // assign the image
        this.image = imageSource

        let ctx
        try {
          // the drawImage it's a synchronous method, so when it's done
          // the nextTick will notify the view that we're ready
          // to fadeIn the main image
          ctx = this.$refs.canvas.getContext('2d')
          ctx.drawImage(this.$refs.main, 0, 0)
        } catch (e) {
          // see https://github.com/MatteoGabriele/vue-progressive-image/issues/30
        }

        // next tick to know when the image is rendered
        this.$nextTick(() => {
          // timeout for a custom delay
          setTimeout(() => {
            this.isRendered = true
          }, delay)
        })

        this.imageError = false

        this.$emit('onLoad', image.src)
      }

      image.onerror = error => {
        this.$emit('onError', error)

        if (process.env.NODE_ENV !== 'production' && !this.fallback) {
          console.warn('[vue-progressive-image] An error occured during the image loading')
        }

        if (this.fallback || this.options.fallback) {
          this.imageError = true
          this.handleImageLoading()
        }
      }

      image.src = imageSource
    },

    loadPlaceholder () {
      if (!this.placeholder && !this.options.placeholder) {
        return
      }

      const image = new Image()
      let src = this.placeholder

      if (this.options.placeholder && !this.placeholder) {
        src = this.options.placeholder
      }

      image.onload = () => {
        this.placeholderImage = src

        // Dispatches an event on placeholder image load
        this.$emit('onLoadPlaceholder', src)
      }

      image.onerror = error => {
        this.$emit('onPlaceholderError', error)

        if (process.env.NODE_ENV !== 'production') {
          console.warn('[vue-progressive-image] An error occured during the placeholder image loading')
        }
      }

      image.src = src
    },

    handleImageLoading () {
      this.loadPlaceholder()
      this.loadImage()
    }
  }
}
<|endoftext|>


<commit_before>import React from 'react';
import contractApi from './../../ContractApi';

class SyndicatePositions extends React.Component {

  constructor(){
    super();
    this.state = {positions: {}};
  }

  componentWillMount(){

    contractApi.getAllInsuranceContracts()
    .then((result) =>{
      console.log(result);

      let data = [];
      var accounts = result.accounts;
      var premiums = result.premiums;
      var payouts = result.payouts;
      var startBlocks = result.startBlocks;
      var endBlocks = result.endBlocks;

      for (var i = 0; i < accounts.length; i++) {
        data.push({
          account: accounts[i],
          premium: premiums[i].c[0],
          payouts: payouts[i].c[0],
          startBlocks: startBlocks[i].c[0],
          endBlocks: endBlocks[i].c[0],
        })
        }
      this.setState({
        positions : data
      });
    }

  );
  }
  render(){
    if(Object.keys(this.state.positions).length === 0 ){
      return (

          Loading positions...

      );
    }
      else{
        return (

Investor Positions

                  Account
                  Premium
                  Payouts
                  Start Block

                {this.state.positions.map((val) =>

                     {val.account}

                     {val.premium}

                     {val.payouts}

                     {val.startBlocks}

                )}

        );
    }
  }
}
export default SyndicatePositions;
<commit_msg>styling table<commit_after>import React from 'react';
import contractApi from './../../ContractApi';

class SyndicatePositions extends React.Component {

  constructor(){
    super();
    this.state = {positions: {}};
  }

  componentWillMount(){

    contractApi.getAllInsuranceContracts()
    .then((result) =>{
      console.log(result);

      let data = [];
      var accounts = result.accounts;
      var premiums = result.premiums;
      var payouts = result.payouts;
      var startBlocks = result.startBlocks;
      var endBlocks = result.endBlocks;

      for (var i = 0; i < accounts.length; i++) {
        data.push({
          account: accounts[i],
          premium: premiums[i].c[0],
          payouts: payouts[i].c[0],
          startBlocks: startBlocks[i].c[0],
          endBlocks: endBlocks[i].c[0],
        })
        }
      this.setState({
        positions : data
      });
    }

  );
  }
  render(){
    if(Object.keys(this.state.positions).length === 0 ){
      return (

          Loading positions...

      );
    }
      else{
        return(

                   Positions

                        Account
                        Premium
                        Payouts
                        Start Block
                        End Block
                        Status

                    {this.state.positions.map((val) =>

                           {val.account}

                           {val.premium}

                           {val.payouts}

                           {val.endBlocks}

                           {val.startBlocks}

                              Active

                      )}

        );
    }
  }
}
export default SyndicatePositions;
<|endoftext|>


<commit_before>/**
 * @file PopupCalculation vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import Dom from './Dom';
import Position from '../_statics/Position';

function topVerticalBottom(triggerEl, triggerOffset) {
    return triggerOffset.top + triggerEl.offsetHeight;
}

function topVerticalTop(triggerOffset, popupEl) {
    return triggerOffset.top - popupEl.offsetHeight
        - parseInt(getComputedStyle(popupEl).marginTop)
        - parseInt(getComputedStyle(popupEl).marginBottom);
}

function topHorizontalTop(triggerOffset) {
    return triggerOffset.top;
}

function topHorizontalMiddle(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.top + triggerEl.offsetHeight / 2 - popupEl.offsetHeight / 2;
}

function topHorizontalBottom(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.top + triggerEl.offsetHeight - popupEl.offsetHeight;
}

function leftVerticalLeft(triggerOffset) {
    return triggerOffset.left;
}

function leftVerticalCenter(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.left + triggerEl.offsetWidth / 2 - popupEl.offsetWidth / 2;
}

function leftVerticalRight(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.left - (popupEl.offsetWidth - triggerEl.offsetWidth);
}

function leftHorizontalLeft(triggerOffset, popupEl) {
    return triggerOffset.left - popupEl.offsetWidth
        - parseInt(getComputedStyle(popupEl).marginLeft)
        - parseInt(getComputedStyle(popupEl).marginRight);
}

function leftHorizontalRight(triggerEl, triggerOffset) {
    return triggerOffset.left + triggerEl.offsetWidth;
}

function getStyle(triggerEl, popupEl, position) {

    if (!triggerEl || !popupEl) {
        return;
    }

    const triggerOffset = Dom.getOffset(triggerEl);
    let result = {};

    switch (position) {
        case Position.TOP_LEFT: {
            result.left = leftVerticalLeft(triggerOffset);
            result.top = topVerticalTop(triggerOffset, popupEl);
            break;
        }
        case Position.TOP: {
            result.left = leftVerticalCenter(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalTop(triggerOffset, popupEl);
            break;
        }
        case Position.TOP_RIGHT: {
            result.left = leftVerticalRight(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalTop(triggerOffset, popupEl);
            break;
        }
        case Position.BOTTOM_LEFT: {
            result.left = leftVerticalLeft(triggerOffset);
            result.top = topVerticalBottom(triggerEl, triggerOffset);
            break;
        }
        case Position.BOTTOM: {
            result.left = leftVerticalCenter(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalBottom(triggerEl, triggerOffset);
            break;
        }
        case Position.BOTTOM_RIGHT: {
            result.left = leftVerticalRight(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalBottom(triggerEl, triggerOffset);
            break;
        }
        case Position.LEFT_TOP: {
            result.left = leftHorizontalLeft(triggerOffset, popupEl);
            result.top = topHorizontalTop(triggerOffset);
            break;
        }
        case Position.LEFT: {
            result.left = leftHorizontalLeft(triggerOffset, popupEl);
            result.top = topHorizontalMiddle(triggerEl, triggerOffset, popupEl);
            break;
        }
        case Position.LEFT_BOTTOM: {
            result.left = leftHorizontalLeft(triggerOffset, popupEl);
            result.top = topHorizontalBottom(triggerEl, triggerOffset, popupEl);
            break;
        }
        case Position.RIGHT_TOP: {
            result.left = leftHorizontalRight(triggerEl, triggerOffset);
            result.top = topHorizontalTop(triggerOffset);
            break;
        }
        case Position.RIGHT: {
            result.left = leftHorizontalRight(triggerEl, triggerOffset);
            result.top = topHorizontalMiddle(triggerEl, triggerOffset, popupEl);
            break;
        }
        case Position.RIGHT_BOTTOM: {
            result.left = leftHorizontalRight(triggerEl, triggerOffset);
            result.top = topHorizontalBottom(triggerEl, triggerOffset, popupEl);
            break;
        }
    }

    if (isTriggerPositionFixed) {
        result.position = 'fixed';
        result.left -= Dom.getScrollLeft();
        result.top -= Dom.getScrollTop();
    }

    return result;

}

export default {
    getStyle
};<commit_msg>update PopupCalculation<commit_after>/**
 * @file PopupCalculation vendor
 * @author liangxiaojun(liangxiaojun@derbysoft.com)
 */

import Dom from './Dom';
import Position from '../_statics/Position';

function topVerticalBottom(triggerEl, triggerOffset) {
    return triggerOffset.top + triggerEl.offsetHeight;
}

function topVerticalTop(triggerOffset, popupEl) {
    return triggerOffset.top - popupEl.offsetHeight
        - parseInt(getComputedStyle(popupEl).marginTop)
        - parseInt(getComputedStyle(popupEl).marginBottom);
}

function topHorizontalTop(triggerOffset) {
    return triggerOffset.top;
}

function topHorizontalMiddle(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.top + triggerEl.offsetHeight / 2 - popupEl.offsetHeight / 2;
}

function topHorizontalBottom(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.top + triggerEl.offsetHeight - popupEl.offsetHeight;
}

function leftVerticalLeft(triggerOffset) {
    return triggerOffset.left;
}

function leftVerticalCenter(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.left + triggerEl.offsetWidth / 2 - popupEl.offsetWidth / 2;
}

function leftVerticalRight(triggerEl, triggerOffset, popupEl) {
    return triggerOffset.left - (popupEl.offsetWidth - triggerEl.offsetWidth);
}

function leftHorizontalLeft(triggerOffset, popupEl) {
    return triggerOffset.left - popupEl.offsetWidth
        - parseInt(getComputedStyle(popupEl).marginLeft)
        - parseInt(getComputedStyle(popupEl).marginRight);
}

function leftHorizontalRight(triggerEl, triggerOffset) {
    return triggerOffset.left + triggerEl.offsetWidth;
}

function getStyle(triggerEl, popupEl, position, isTriggerPositionFixed) {

    if (!triggerEl || !popupEl) {
        return;
    }

    const triggerOffset = Dom.getOffset(triggerEl);
    let result = {};

    switch (position) {
        case Position.TOP_LEFT: {
            result.left = leftVerticalLeft(triggerOffset);
            result.top = topVerticalTop(triggerOffset, popupEl);
            break;
        }
        case Position.TOP: {
            result.left = leftVerticalCenter(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalTop(triggerOffset, popupEl);
            break;
        }
        case Position.TOP_RIGHT: {
            result.left = leftVerticalRight(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalTop(triggerOffset, popupEl);
            break;
        }
        case Position.BOTTOM_LEFT: {
            result.left = leftVerticalLeft(triggerOffset);
            result.top = topVerticalBottom(triggerEl, triggerOffset);
            break;
        }
        case Position.BOTTOM: {
            result.left = leftVerticalCenter(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalBottom(triggerEl, triggerOffset);
            break;
        }
        case Position.BOTTOM_RIGHT: {
            result.left = leftVerticalRight(triggerEl, triggerOffset, popupEl);
            result.top = topVerticalBottom(triggerEl, triggerOffset);
            break;
        }
        case Position.LEFT_TOP: {
            result.left = leftHorizontalLeft(triggerOffset, popupEl);
            result.top = topHorizontalTop(triggerOffset);
            break;
        }
        case Position.LEFT: {
            result.left = leftHorizontalLeft(triggerOffset, popupEl);
            result.top = topHorizontalMiddle(triggerEl, triggerOffset, popupEl);
            break;
        }
        case Position.LEFT_BOTTOM: {
            result.left = leftHorizontalLeft(triggerOffset, popupEl);
            result.top = topHorizontalBottom(triggerEl, triggerOffset, popupEl);
            break;
        }
        case Position.RIGHT_TOP: {
            result.left = leftHorizontalRight(triggerEl, triggerOffset);
            result.top = topHorizontalTop(triggerOffset);
            break;
        }
        case Position.RIGHT: {
            result.left = leftHorizontalRight(triggerEl, triggerOffset);
            result.top = topHorizontalMiddle(triggerEl, triggerOffset, popupEl);
            break;
        }
        case Position.RIGHT_BOTTOM: {
            result.left = leftHorizontalRight(triggerEl, triggerOffset);
            result.top = topHorizontalBottom(triggerEl, triggerOffset, popupEl);
            break;
        }
    }

    if (isTriggerPositionFixed) {
        result.position = 'fixed';
        result.left -= Dom.getScrollLeft();
        result.top -= Dom.getScrollTop();
    }

    return result;

}

export default {
    getStyle
};<|endoftext|>


<commit_before>var Service            = require('./service');
var delay              = require('./helpers/delay');
var urlTransform       = require('./helpers/url-transform');
var socketMessageEvent = require('./helpers/message-event');
var globalContext      = require('./helpers/global-context');

function MockServer(url) {
  var service = new Service();
  this.url    = urlTransform(url);

  globalContext.MockSocket.services[this.url] = service;

  this.service   = service;
  service.server = this;
}

MockServer.prototype = {
  service: null,

  /*
  * This is the main function for the mock server to subscribe to the on events.
  *
  * ie: mockServer.on('connection', function() { console.log('a mock client connected'); });
  *
  * @param {type: string}: The event key to subscribe to. Valid keys are: connection, message, and close.
  * @param {callback: function}: The callback which should be called when a certain event is fired.
  */
  on: function(type, callback) {
    var observerKey;

    if(typeof callback !== 'function' || typeof type !== 'string') {
      return false;
    }

    switch(type) {
      case 'connection':
        observerKey = 'clientHasJoined';
        break;
      case 'message':
        observerKey = 'clientHasSentMessage';
        break;
      case 'close':
        observerKey = 'clientHasLeft';
        break;
    }

    // Make sure that the observerKey is valid before observing on it.
    if(typeof observerKey === 'string') {
      this.service.clearAll(observerKey);
      this.service.setCallbackObserver(observerKey, callback, this);
    }
  },

  /*
  * This send function will notify all mock clients via their onmessage callbacks that the server
  * has a message for them.
  *
  * @param {data: *}: Any javascript object which will be crafted into a MessageObject.
  */
  send: function(data) {
    delay(function() {
      this.service.sendMessageToClients(socketMessageEvent('message', data, this.url));
    }, this);
  },

  /*
  * Notifies all mock clients that the server is closing and their onclose callbacks should fire.
  */
  close: function() {
    delay(function() {
      this.service.closeConnectionFromServer(socketMessageEvent('close', null, this.url));
    }, this);
  }
};

module.exports = MockServer;
<commit_msg>Allow to create mock server that can not be connected to<commit_after>var Service            = require('./service');
var delay              = require('./helpers/delay');
var urlTransform       = require('./helpers/url-transform');
var socketMessageEvent = require('./helpers/message-event');
var globalContext      = require('./helpers/global-context');

function MockServer(url) {
  var service = new Service();
  this.url    = urlTransform(url);

  globalContext.MockSocket.services[this.url] = service;

  this.service   = service;
  // ignore possible query parameters
  if(url.indexOf(MockServer.unresolvableURL) === -1) {
    service.server = this;
  }
}

/*
* This URL can be used to emulate server that does not establish connection
*/
MockServer.unresolvableURL = "ws://unresolvable_url";

MockServer.prototype = {
  service: null,

  /*
  * This is the main function for the mock server to subscribe to the on events.
  *
  * ie: mockServer.on('connection', function() { console.log('a mock client connected'); });
  *
  * @param {type: string}: The event key to subscribe to. Valid keys are: connection, message, and close.
  * @param {callback: function}: The callback which should be called when a certain event is fired.
  */
  on: function(type, callback) {
    var observerKey;

    if(typeof callback !== 'function' || typeof type !== 'string') {
      return false;
    }

    switch(type) {
      case 'connection':
        observerKey = 'clientHasJoined';
        break;
      case 'message':
        observerKey = 'clientHasSentMessage';
        break;
      case 'close':
        observerKey = 'clientHasLeft';
        break;
    }

    // Make sure that the observerKey is valid before observing on it.
    if(typeof observerKey === 'string') {
      this.service.clearAll(observerKey);
      this.service.setCallbackObserver(observerKey, callback, this);
    }
  },

  /*
  * This send function will notify all mock clients via their onmessage callbacks that the server
  * has a message for them.
  *
  * @param {data: *}: Any javascript object which will be crafted into a MessageObject.
  */
  send: function(data) {
    delay(function() {
      this.service.sendMessageToClients(socketMessageEvent('message', data, this.url));
    }, this);
  },

  /*
  * Notifies all mock clients that the server is closing and their onclose callbacks should fire.
  */
  close: function() {
    delay(function() {
      this.service.closeConnectionFromServer(socketMessageEvent('close', null, this.url));
    }, this);
  }
};

module.exports = MockServer;
<|endoftext|>


<commit_before>Lawnchair.adapter('html5-filesystem', (function(global){

    var StorageInfo = global.StorageInfo || global.webkitStorageInfo || {};
    var TEMPORARY = global.TEMPORARY || StorageInfo.TEMPORARY;
    var PERSISTENT = global.PERSISTENT || StorageInfo.PERSISTENT;
    var BlobBuilder = global.BlobBuilder || global.WebKitBlobBuilder;
    // BlobBuilder is depricated, use Blob
    if(BlobBuilder){
        throw('this browser has not depricated BlobBuilder. you probably want to update.');
    }else{
        console.log('this modern browser has depricated BlobBuilder, use Blob instead')
        // see: https://developer.mozilla.org/en-US/docs/DOM/Blob#Blob_constructor_example_usage
    }
    var requestFileSystem = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem;
    var FileError = global.FileError;

    var fail = function( e ) {
        var msg;
        switch (e.code) {
            case FileError.QUOTA_EXCEEDED_ERR:
                msg = 'QUOTA_EXCEEDED_ERR';
                break;
            case FileError.NOT_FOUND_ERR:
                msg = 'NOT_FOUND_ERR';
                break;
            case FileError.SECURITY_ERR:
                msg = 'SECURITY_ERR';
                break;
            case FileError.INVALID_MODIFICATION_ERR:
                msg = 'INVALID_MODIFICATION_ERR';
                break;
            case FileError.INVALID_STATE_ERR:
                msg = 'INVALID_STATE_ERR';
                break;
            default:
                msg = 'Unknown Error';
                break;
        };
        if ( console ) console.error( e, msg );
    };

    var ls = function( reader, callback, entries ) {
        var result = entries || [];
        reader.readEntries(function( results ) {
            if ( !results.length ) {
                if ( callback ) callback( result.map(function(entry) { return entry.name; }) );
            } else {
                ls( reader, callback, result.concat( Array.prototype.slice.call( results ) ) );
            }
        }, fail );
    };

    var filesystems = {};

    var root = function( store, callback ) {
        var directory = filesystems[store.name];
        if ( directory ) {
            callback( directory );
        } else {
            setTimeout(function() {
                root( store, callback );
            }, 10 );
        }
    };

    return {
        // boolean; true if the adapter is valid for the current environment
        valid: function() {
            return !!requestFileSystem;
        },

        // constructor call and callback. 'name' is the most common option
        init: function( options, callback ) {
            var me = this;
            var error = function(e) { fail(e); if ( callback ) me.fn( me.name, callback ).call( me, me ); };
            requestFileSystem( (options.storage === 'TEMPORARY' ? TEMPORARY : PERSISTENT), (options.size || 1024*1024*1024), function( fs ) {
                fs.root.getDirectory( options.name, {create:true}, function( directory ) {
                    filesystems[options.name] = directory;
                    if ( callback ) me.fn( me.name, callback ).call( me, me );
                }, error );
            }, error );
        },

        // returns all the keys in the store
        keys: function( callback ) {
            var me = this;
            root( this, function( store ) {
                ls( store.createReader(), function( entries ) {
                    if ( callback ) me.fn( 'keys', callback ).call( me, entries );
                });
            });
            return this;
        },

        // save an object
        save: function( obj, callback ) {
            var me = this;
            var key = obj.key || this.uuid();
            obj.key = key;
            var error = function(e) { fail(e); if ( callback ) me.lambda( callback ).call( me ); };
            root( this, function( store ) {
                store.getFile( key, {create:true}, function( file ) {
                    file.createWriter(function( writer ) {
                        writer.onerror = error;
                        writer.onwriteend = function() {
                            if ( callback ) me.lambda( callback ).call( me, obj );
                        };
                        // Old, depricated
                        if(BlobBuilder){
                            var builder = new BlobBuilder();
                            builder.append( JSON.stringify( obj ) );
                            writer.write( builder.getBlob( 'application/json' ) );
                        }else{
                        // new, kinky
                            writer.write( new Blob([JSON.stringify(obj)] , {'type': 'application/json'}) );
                        }
                    }, error );
                }, error );
            });
            return this;
        },

        // batch save array of objs
        batch: function( objs, callback ) {
            var me = this;
            var saved = [];
            for ( var i = 0, il = objs.length; i < il; i++ ) {
                me.save( objs[i], function( obj ) {
                    saved.push( obj );
                    if ( saved.length === il && callback ) {
                        me.lambda( callback ).call( me, saved );
                    }
                });
            }
            return this;
        },

        // retrieve obj (or array of objs) and apply callback to each
        get: function( key /* or array */, callback ) {
            var me = this;
            if ( this.isArray( key ) ) {
                var values = [];
                for ( var i = 0, il = key.length; i < il; i++ ) {
                    me.get( key[i], function( result ) {
                        if ( result ) values.push( result );
                        if ( values.length === il && callback ) {
                            me.lambda( callback ).call( me, values );
                        }
                    });
                }
            } else {
                var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); };
                root( this, function( store ) {
                    store.getFile( key, {create:false}, function( entry ) {
                        entry.file(function( file ) {
                            var reader = new FileReader();

                            reader.onerror = error;

                            reader.onload = function(e) {
                                if ( callback ) me.lambda( callback ).call( me, JSON.parse( e.target.result ) );
                            };

                            reader.readAsText( file );
                        }, error );
                    }, error );
                });
            }
            return this;
        },

        // check if an obj exists in the collection
        exists: function( key, callback ) {
            var me = this;
            root( this, function( store ) {
                store.getFile( key, {create:false}, function() {
                    if ( callback ) me.lambda( callback ).call( me, true );
                }, function() {
                    if ( callback ) me.lambda( callback ).call( me, false );
                });
            });
            return this;
        },

        // returns all the objs to the callback as an array
        all: function( callback ) {
            var me = this;
            if ( callback ) {
                this.keys(function( keys ) {
                    if ( !keys.length ) {
                        me.fn( me.name, callback ).call( me, [] );
                    } else {
                        me.get( keys, function( values ) {
                            me.fn( me.name, callback ).call( me, values );
                        });
                    }
                });
            }
            return this;
        },

        // remove a doc or collection of em
        remove: function( key /* or object */, callback ) {
            var me = this;
            var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); };
            root( this, function( store ) {
                store.getFile( (typeof key === 'string' ? key : key.key ), {create:false}, function( file ) {
                    file.remove(function() {
                        if ( callback ) me.lambda( callback ).call( me );
                    }, error );
                }, error );
            });
            return this;
        },

        // destroy everything
        nuke: function( callback ) {
            var me = this;
            var count = 0;
            this.keys(function( keys ) {
                if ( !keys.length ) {
                    if ( callback ) me.lambda( callback ).call( me );
                } else {
                    for ( var i = 0, il = keys.length; i < il; i++ ) {
                        me.remove( keys[i], function() {
                            count++;
                            if ( count === il && callback ) {
                                me.lambda( callback ).call( me );
                            }
                        });
                    }
                }
            });
            return this;
        }
    };
}(this)));
<commit_msg>Use 'console.error' instead of 'throw'<commit_after>Lawnchair.adapter('html5-filesystem', (function(global){

    var StorageInfo = global.StorageInfo || global.webkitStorageInfo || {};
    var TEMPORARY = global.TEMPORARY || StorageInfo.TEMPORARY;
    var PERSISTENT = global.PERSISTENT || StorageInfo.PERSISTENT;
    var BlobBuilder = global.BlobBuilder || global.WebKitBlobBuilder;
    // BlobBuilder is depricated, use Blob
    if(BlobBuilder){
        console.error('this browser has not depricated BlobBuilder. you probably want to update.');
    }else{
        console.log('this modern browser has depricated BlobBuilder, use Blob instead')
        // see: https://developer.mozilla.org/en-US/docs/DOM/Blob#Blob_constructor_example_usage
    }
    var requestFileSystem = global.requestFileSystem || global.webkitRequestFileSystem || global.moz_requestFileSystem;
    var FileError = global.FileError;

    var fail = function( e ) {
        var msg;
        switch (e.code) {
            case FileError.QUOTA_EXCEEDED_ERR:
                msg = 'QUOTA_EXCEEDED_ERR';
                break;
            case FileError.NOT_FOUND_ERR:
                msg = 'NOT_FOUND_ERR';
                break;
            case FileError.SECURITY_ERR:
                msg = 'SECURITY_ERR';
                break;
            case FileError.INVALID_MODIFICATION_ERR:
                msg = 'INVALID_MODIFICATION_ERR';
                break;
            case FileError.INVALID_STATE_ERR:
                msg = 'INVALID_STATE_ERR';
                break;
            default:
                msg = 'Unknown Error';
                break;
        };
        if ( console ) console.error( e, msg );
    };

    var ls = function( reader, callback, entries ) {
        var result = entries || [];
        reader.readEntries(function( results ) {
            if ( !results.length ) {
                if ( callback ) callback( result.map(function(entry) { return entry.name; }) );
            } else {
                ls( reader, callback, result.concat( Array.prototype.slice.call( results ) ) );
            }
        }, fail );
    };

    var filesystems = {};

    var root = function( store, callback ) {
        var directory = filesystems[store.name];
        if ( directory ) {
            callback( directory );
        } else {
            setTimeout(function() {
                root( store, callback );
            }, 10 );
        }
    };

    return {
        // boolean; true if the adapter is valid for the current environment
        valid: function() {
            return !!requestFileSystem;
        },

        // constructor call and callback. 'name' is the most common option
        init: function( options, callback ) {
            var me = this;
            var error = function(e) { fail(e); if ( callback ) me.fn( me.name, callback ).call( me, me ); };
            requestFileSystem( (options.storage === 'TEMPORARY' ? TEMPORARY : PERSISTENT), (options.size || 1024*1024*1024), function( fs ) {
                fs.root.getDirectory( options.name, {create:true}, function( directory ) {
                    filesystems[options.name] = directory;
                    if ( callback ) me.fn( me.name, callback ).call( me, me );
                }, error );
            }, error );
        },

        // returns all the keys in the store
        keys: function( callback ) {
            var me = this;
            root( this, function( store ) {
                ls( store.createReader(), function( entries ) {
                    if ( callback ) me.fn( 'keys', callback ).call( me, entries );
                });
            });
            return this;
        },

        // save an object
        save: function( obj, callback ) {
            var me = this;
            var key = obj.key || this.uuid();
            obj.key = key;
            var error = function(e) { fail(e); if ( callback ) me.lambda( callback ).call( me ); };
            root( this, function( store ) {
                store.getFile( key, {create:true}, function( file ) {
                    file.createWriter(function( writer ) {
                        writer.onerror = error;
                        writer.onwriteend = function() {
                            if ( callback ) me.lambda( callback ).call( me, obj );
                        };
                        // Old, depricated
                        if(BlobBuilder){
                            var builder = new BlobBuilder();
                            builder.append( JSON.stringify( obj ) );
                            writer.write( builder.getBlob( 'application/json' ) );
                        }else{
                        // new, kinky
                            writer.write( new Blob([JSON.stringify(obj)] , {'type': 'application/json'}) );
                        }
                    }, error );
                }, error );
            });
            return this;
        },

        // batch save array of objs
        batch: function( objs, callback ) {
            var me = this;
            var saved = [];
            for ( var i = 0, il = objs.length; i < il; i++ ) {
                me.save( objs[i], function( obj ) {
                    saved.push( obj );
                    if ( saved.length === il && callback ) {
                        me.lambda( callback ).call( me, saved );
                    }
                });
            }
            return this;
        },

        // retrieve obj (or array of objs) and apply callback to each
        get: function( key /* or array */, callback ) {
            var me = this;
            if ( this.isArray( key ) ) {
                var values = [];
                for ( var i = 0, il = key.length; i < il; i++ ) {
                    me.get( key[i], function( result ) {
                        if ( result ) values.push( result );
                        if ( values.length === il && callback ) {
                            me.lambda( callback ).call( me, values );
                        }
                    });
                }
            } else {
                var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); };
                root( this, function( store ) {
                    store.getFile( key, {create:false}, function( entry ) {
                        entry.file(function( file ) {
                            var reader = new FileReader();

                            reader.onerror = error;

                            reader.onload = function(e) {
                                if ( callback ) me.lambda( callback ).call( me, JSON.parse( e.target.result ) );
                            };

                            reader.readAsText( file );
                        }, error );
                    }, error );
                });
            }
            return this;
        },

        // check if an obj exists in the collection
        exists: function( key, callback ) {
            var me = this;
            root( this, function( store ) {
                store.getFile( key, {create:false}, function() {
                    if ( callback ) me.lambda( callback ).call( me, true );
                }, function() {
                    if ( callback ) me.lambda( callback ).call( me, false );
                });
            });
            return this;
        },

        // returns all the objs to the callback as an array
        all: function( callback ) {
            var me = this;
            if ( callback ) {
                this.keys(function( keys ) {
                    if ( !keys.length ) {
                        me.fn( me.name, callback ).call( me, [] );
                    } else {
                        me.get( keys, function( values ) {
                            me.fn( me.name, callback ).call( me, values );
                        });
                    }
                });
            }
            return this;
        },

        // remove a doc or collection of em
        remove: function( key /* or object */, callback ) {
            var me = this;
            var error = function(e) { fail( e ); if ( callback ) me.lambda( callback ).call( me ); };
            root( this, function( store ) {
                store.getFile( (typeof key === 'string' ? key : key.key ), {create:false}, function( file ) {
                    file.remove(function() {
                        if ( callback ) me.lambda( callback ).call( me );
                    }, error );
                }, error );
            });
            return this;
        },

        // destroy everything
        nuke: function( callback ) {
            var me = this;
            var count = 0;
            this.keys(function( keys ) {
                if ( !keys.length ) {
                    if ( callback ) me.lambda( callback ).call( me );
                } else {
                    for ( var i = 0, il = keys.length; i < il; i++ ) {
                        me.remove( keys[i], function() {
                            count++;
                            if ( count === il && callback ) {
                                me.lambda( callback ).call( me );
                            }
                        });
                    }
                }
            });
            return this;
        }
    };
}(this)));
<|endoftext|>


<commit_before>/*
Himalaya is a pure JavaScript HTML parser that converts HTML into JSON, which can then be further manipulated by other modules.
2016 (c) GPL- Michael Glazer
Modified: added fromHtml conversion to JSONML

Originally From: https://github.com/andrejewski/himalaya

Example: jsonml.fromHtml (htmlString) -> JSONML
*/

(function(global) {

  var tagStart = '<';
  var tagEnd = '>';

  var commentStart = '<!--';
  var commentEnd = '-->';

  var voidTags = [
    "!doctype", "area", "base", "br", "col", "command",
    "embed", "hr", "img", "input", "keygen", "link",
    "meta", "param", "source", "track", "wbr"
  ];
  var closingTags = [
    "colgroup", "dd", "dt", "li", "options", "p",
    "td", "tfoot", "th", "thead", "tr"
  ];
  var childlessTags = ['style', 'script', 'template'];

  function parse(str) {
    return parseUntil(str + '</root>', ['root']).nodes;
  }

  function Node(kind, data) {
    data.type = kind;
    return data;
  }

  function parseUntil(str, stack) {
    var nodes = [];

    while (str.length) {
      var nextTag = str.indexOf(tagStart);
      if (!~nextTag) {
        // only text left
        nodes.push(Node('Text', {
          content: str
        }));
        str = "";
      } else if (nextTag) {
        // text before tag
        nodes.push(Node('Text', {
          content: str.slice(0, nextTag)
        }));
        str = str.slice(nextTag);
      } else {
        if (startsWithCommentStart(str)) {
          // comment
          var end = str.indexOf(commentEnd);
          nodes.push(Node('Comment', {
            content: str.slice(commentStart.length, end)
          }));
          str = str.slice(end + commentEnd.length);
        } else if (str.charAt(nextTag + 1) !== '/') {
          // open tag
          var results = parseTag(str, stack);
          if (results.tag) {
            nodes.push(Node('Element', results.tag));
            str = results.str;
          }
          if (results.stack.length !== stack.length) {
            stack = results.stack;
            break;
          }
        } else {
          // close tag
          var endTagEnd = str.indexOf(tagEnd);
          var tagName = str.slice(2, endTagEnd)
            .trim().split(' ')[0];
          str = str.slice(endTagEnd + 1);
          var loc = stack.lastIndexOf(tagName);
          if (~loc) {
            stack = stack.slice(0, loc);
            break;
          }
        }
      }
    }

    return {
      nodes: nodes,
      stack: stack,
      str: str
    };
  }

  function parseTag(str, stack) {
    var idxTagEnd = str.indexOf(tagEnd);
    var idxSpace = str.indexOf(' ');
    var tagNameEnd = ~idxSpace ? Math.min(idxTagEnd, idxSpace) : idxTagEnd;
    var tagName = str.slice(1, tagNameEnd);
    var lowTagName = tagName.toLowerCase();

    if (stack[stack.length - 1] === tagName && ~closingTags.indexOf(lowTagName)) {
      return {
        stack: stack.slice(0, -1)
      };
    }

    var attrs = parseAttrs(str.slice(tagNameEnd));
    var tag = {
      tagName: tagName,
      attributes: attrs.attributes
    };
    str = attrs.str;

    if (startsWithSelfClose(str)) {
      str = str.slice(2);
    } else {
      str = str.slice(1);

      if (~childlessTags.indexOf(lowTagName)) {
        var end = '</' + tagName + '>';
        var idx = str.indexOf(end);
        if (!~idx) idx = Infinity;
        tag.content = str.slice(0, idx);
        str = str.slice(idx);
      } else if (!~voidTags.indexOf(lowTagName)) {
        var results = parseUntil(str, stack.concat(tagName));
        tag.children = results.nodes;
        str = results.str;
        stack = results.stack;
      }
    }

    return {
      tag: tag,
      str: str,
      stack: stack
    };
  }

  function parseAttrs(str) {
    var results = tagPairs(str.trim());
    var str = results.str;
    var attributes = results.kvs.map(function(pair) {
      var kv = splitHead(pair.trim(), '=');
      kv[1] = kv[1] ? unquote(kv[1]) : kv[0];
      return kv;
    }).reduce(function(attrs, kv) {
      var property = kv[0];
      var value = kv[1];
      if (property === 'class') {
        attrs.className = value.split(' ');
      } else if (property === 'style') {
        attrs.style = parseStyle(value);
      } else if (startsWithDataDash(property)) {
        attrs.dataset = attrs.dataset || {};
        var key = camelCase(property.slice(5));
        attrs.dataset[key] = castValue(value);
      } else {
        attrs[camelCase(property)] = castValue(value);
      }
      return attrs;
    }, {});
    return {
      str: str,
      attributes: attributes
    };
  }

  function splitHead(str, sep) {
    var idx = str.indexOf(sep);
    if (!~idx) return [str];
    return [str.slice(0, idx), str.slice(idx + sep.length)];
  }

  function tagPairs(str) {
    function _tagPairs(str) {
      var parts = [];
      var start = 0;
      var finish = 0;
      var quote = null;
      for (var i = start; i < str.length; i++) {
        var c = str.charAt(i);
        if (!quote && (c === '/' || c === tagEnd)) {
          if (start !== i) parts.push(str.slice(start, i));
          finish = i;
          break;
        } else if (c === ' ' && !quote) {
          parts.push(str.slice(start, i));
          start = i + 1;
        } else if (c === quote) {
          quote = null;
        } else if (!quote && (c === '"' || c === "'")) {
          quote = c;
        }
      }
      return {
        kvs: parts,
        str: str.slice(finish)
      };
    }

    var result = _tagPairs(str);
    var list = result.kvs.filter(function(x) {
      return x;
    });
    var len = list.length;
    var kvs = [];
    for (var idx = 0; idx < len; idx++) {
      var val = list[idx];
      if (val.indexOf('=') === -1) {
        var second = list[idx + 1];
        var third = list[idx + 2];
        if (second === '=' && third) {
          idx += 2;
          kvs.push(val + '=' + third);
          continue;
        }
      }
      kvs.push(val);
    }
    result.kvs = kvs;
    return result;
  }

  function unquote(str) {
    var car = str.charAt(0);
    var end = str.length - 1;
    if (car === '"' || car === "'" && car === str.charAt(end)) {
      return str.slice(1, end);
    }
    return str;
  }

  function parseStyle(str) {
    return str.trim().split(';').map(function(statement) {
      return statement.trim().split(':');
    }).reduce(function(styles, kv) {
      if (kv[1]) styles[camelCase(kv[0].trim())] = castValue(kv[1].trim());
      return styles;
    }, {});
  }

  function camelCase(str) {
    return str.split('-').reduce(function(str, word) {
      return str + word.charAt(0).toUpperCase() + word.slice(1);
    });
  }

  function castValue(str) {
    if (typeof str !== 'string') return str;
    var num = +str;
    if (!isNaN(num)) return num;
    return str;
  }

  function startsWithCommentStart(s) {
    return (
      s.charAt(0) === '<' &&
      s.charAt(1) === '!' &&
      s.charAt(2) === '-' &&
      s.charAt(3) === '-');
  }

  function startsWithSelfClose(s) {
    return (
      s.charAt(0) === '/' &&
      s.charAt(1) === '>');
  }

  function startsWithDataDash(s) {
    return (
      s.charAt(0) === 'd' &&
      s.charAt(1) === 'a' &&
      s.charAt(2) === 't' &&
      s.charAt(3) === 'a' &&
      s.charAt(4) === '-');
  }

  var himalaya = {
    parse: parse,
    parseTag: parseTag,
    parseUntil: parseUntil,
    parseAttrs: parseAttrs,
    parseStyle: parseStyle
  };

  var toJsonml = function(obj) {
    //tagName, attributes, children
    var a = []
    for (var i in obj) {
      var item = obj[i];
      var tag = item.tagName;
      var attrs = item.attributes;
      var attrsSize = attrs && Object.keys(attrs).length > 0;
      var childs = item.children;
      var type = item.type.toLowerCase();
      a[0] = tag;
      if (attrsSize) a[1] = attrs;
      if (type == 'text' && attrsSize === 0) a[1] = item.content;
      if (type == 'text' && attrsSize > 0) a[2] = item.content;
      if (childs && childs.length > 0) {
        for (var k in childs) {
          var sitem = childs[k]
          if (!sitem.tagName) {
            a[a.length] = sitem.content;
          } else {
            a[a.length] = toJsonml([childs[k]]);
          }
        }

      }

    }

    return a;
  }

  jsonml = global.jsonml || {};

  jsonml.fromHtml = function(htmlString) {

    return toJsonml(parse(htmlString));

  };

})(window);
<commit_msg>Update html-parser.js<commit_after>/*
Himalaya is a pure JavaScript HTML parser that converts HTML into JSON, which can then be further manipulated by other modules.
2016 (c) Michael Glazer
License MIT
Modified: added fromHtml conversion to JSONML

Originally From: https://github.com/andrejewski/himalaya

Example: jsonml.fromHtml (htmlString) -> JSONML
*/

(function(global) {

  var tagStart = '<';
  var tagEnd = '>';

  var commentStart = '<!--';
  var commentEnd = '-->';

  var voidTags = [
    "!doctype", "area", "base", "br", "col", "command",
    "embed", "hr", "img", "input", "keygen", "link",
    "meta", "param", "source", "track", "wbr"
  ];
  var closingTags = [
    "colgroup", "dd", "dt", "li", "options", "p",
    "td", "tfoot", "th", "thead", "tr"
  ];
  var childlessTags = ['style', 'script', 'template'];

  function parse(str) {
    return parseUntil(str + '</root>', ['root']).nodes;
  }

  function Node(kind, data) {
    data.type = kind;
    return data;
  }

  function parseUntil(str, stack) {
    var nodes = [];

    while (str.length) {
      var nextTag = str.indexOf(tagStart);
      if (!~nextTag) {
        // only text left
        nodes.push(Node('Text', {
          content: str
        }));
        str = "";
      } else if (nextTag) {
        // text before tag
        nodes.push(Node('Text', {
          content: str.slice(0, nextTag)
        }));
        str = str.slice(nextTag);
      } else {
        if (startsWithCommentStart(str)) {
          // comment
          var end = str.indexOf(commentEnd);
          nodes.push(Node('Comment', {
            content: str.slice(commentStart.length, end)
          }));
          str = str.slice(end + commentEnd.length);
        } else if (str.charAt(nextTag + 1) !== '/') {
          // open tag
          var results = parseTag(str, stack);
          if (results.tag) {
            nodes.push(Node('Element', results.tag));
            str = results.str;
          }
          if (results.stack.length !== stack.length) {
            stack = results.stack;
            break;
          }
        } else {
          // close tag
          var endTagEnd = str.indexOf(tagEnd);
          var tagName = str.slice(2, endTagEnd)
            .trim().split(' ')[0];
          str = str.slice(endTagEnd + 1);
          var loc = stack.lastIndexOf(tagName);
          if (~loc) {
            stack = stack.slice(0, loc);
            break;
          }
        }
      }
    }

    return {
      nodes: nodes,
      stack: stack,
      str: str
    };
  }

  function parseTag(str, stack) {
    var idxTagEnd = str.indexOf(tagEnd);
    var idxSpace = str.indexOf(' ');
    var tagNameEnd = ~idxSpace ? Math.min(idxTagEnd, idxSpace) : idxTagEnd;
    var tagName = str.slice(1, tagNameEnd);
    var lowTagName = tagName.toLowerCase();

    if (stack[stack.length - 1] === tagName && ~closingTags.indexOf(lowTagName)) {
      return {
        stack: stack.slice(0, -1)
      };
    }

    var attrs = parseAttrs(str.slice(tagNameEnd));
    var tag = {
      tagName: tagName,
      attributes: attrs.attributes
    };
    str = attrs.str;

    if (startsWithSelfClose(str)) {
      str = str.slice(2);
    } else {
      str = str.slice(1);

      if (~childlessTags.indexOf(lowTagName)) {
        var end = '</' + tagName + '>';
        var idx = str.indexOf(end);
        if (!~idx) idx = Infinity;
        tag.content = str.slice(0, idx);
        str = str.slice(idx);
      } else if (!~voidTags.indexOf(lowTagName)) {
        var results = parseUntil(str, stack.concat(tagName));
        tag.children = results.nodes;
        str = results.str;
        stack = results.stack;
      }
    }

    return {
      tag: tag,
      str: str,
      stack: stack
    };
  }

  function parseAttrs(str) {
    var results = tagPairs(str.trim());
    var str = results.str;
    var attributes = results.kvs.map(function(pair) {
      var kv = splitHead(pair.trim(), '=');
      kv[1] = kv[1] ? unquote(kv[1]) : kv[0];
      return kv;
    }).reduce(function(attrs, kv) {
      var property = kv[0];
      var value = kv[1];
      if (property === 'class') {
        attrs.className = value.split(' ');
      } else if (property === 'style') {
        attrs.style = parseStyle(value);
      } else if (startsWithDataDash(property)) {
        attrs.dataset = attrs.dataset || {};
        var key = camelCase(property.slice(5));
        attrs.dataset[key] = castValue(value);
      } else {
        attrs[camelCase(property)] = castValue(value);
      }
      return attrs;
    }, {});
    return {
      str: str,
      attributes: attributes
    };
  }

  function splitHead(str, sep) {
    var idx = str.indexOf(sep);
    if (!~idx) return [str];
    return [str.slice(0, idx), str.slice(idx + sep.length)];
  }

  function tagPairs(str) {
    function _tagPairs(str) {
      var parts = [];
      var start = 0;
      var finish = 0;
      var quote = null;
      for (var i = start; i < str.length; i++) {
        var c = str.charAt(i);
        if (!quote && (c === '/' || c === tagEnd)) {
          if (start !== i) parts.push(str.slice(start, i));
          finish = i;
          break;
        } else if (c === ' ' && !quote) {
          parts.push(str.slice(start, i));
          start = i + 1;
        } else if (c === quote) {
          quote = null;
        } else if (!quote && (c === '"' || c === "'")) {
          quote = c;
        }
      }
      return {
        kvs: parts,
        str: str.slice(finish)
      };
    }

    var result = _tagPairs(str);
    var list = result.kvs.filter(function(x) {
      return x;
    });
    var len = list.length;
    var kvs = [];
    for (var idx = 0; idx < len; idx++) {
      var val = list[idx];
      if (val.indexOf('=') === -1) {
        var second = list[idx + 1];
        var third = list[idx + 2];
        if (second === '=' && third) {
          idx += 2;
          kvs.push(val + '=' + third);
          continue;
        }
      }
      kvs.push(val);
    }
    result.kvs = kvs;
    return result;
  }

  function unquote(str) {
    var car = str.charAt(0);
    var end = str.length - 1;
    if (car === '"' || car === "'" && car === str.charAt(end)) {
      return str.slice(1, end);
    }
    return str;
  }

  function parseStyle(str) {
    return str.trim().split(';').map(function(statement) {
      return statement.trim().split(':');
    }).reduce(function(styles, kv) {
      if (kv[1]) styles[camelCase(kv[0].trim())] = castValue(kv[1].trim());
      return styles;
    }, {});
  }

  function camelCase(str) {
    return str.split('-').reduce(function(str, word) {
      return str + word.charAt(0).toUpperCase() + word.slice(1);
    });
  }

  function castValue(str) {
    if (typeof str !== 'string') return str;
    var num = +str;
    if (!isNaN(num)) return num;
    return str;
  }

  function startsWithCommentStart(s) {
    return (
      s.charAt(0) === '<' &&
      s.charAt(1) === '!' &&
      s.charAt(2) === '-' &&
      s.charAt(3) === '-');
  }

  function startsWithSelfClose(s) {
    return (
      s.charAt(0) === '/' &&
      s.charAt(1) === '>');
  }

  function startsWithDataDash(s) {
    return (
      s.charAt(0) === 'd' &&
      s.charAt(1) === 'a' &&
      s.charAt(2) === 't' &&
      s.charAt(3) === 'a' &&
      s.charAt(4) === '-');
  }

  var himalaya = {
    parse: parse,
    parseTag: parseTag,
    parseUntil: parseUntil,
    parseAttrs: parseAttrs,
    parseStyle: parseStyle
  };

  var toJsonml = function(obj) {
    //tagName, attributes, children
    var a = []
    for (var i in obj) {
      var item = obj[i];
      var tag = item.tagName;
      var attrs = item.attributes;
      var attrsSize = attrs && Object.keys(attrs).length > 0;
      var childs = item.children;
      var type = item.type.toLowerCase();
      a[0] = tag;
      if (attrsSize) a[1] = attrs;
      if (type == 'text' && attrsSize === 0) a[1] = item.content;
      if (type == 'text' && attrsSize > 0) a[2] = item.content;
      if (childs && childs.length > 0) {
        for (var k in childs) {
          var sitem = childs[k]
          if (!sitem.tagName) {
            a[a.length] = sitem.content;
          } else {
            a[a.length] = toJsonml([childs[k]]);
          }
        }

      }

    }

    return a;
  }

  jsonml = global.jsonml || {};

  jsonml.fromHtml = function(htmlString) {

    return toJsonml(parse(htmlString));

  };

})(window);
<|endoftext|>