<commit_before>import ListingsSerializer from './serializers/listings-serializer';
import ListingsAdapter from './adapters/listings-adapter';
import CartSerializer from './serializers/cart-serializer';
import ReferenceSerializer from './serializers/reference-serializer';
import LocalStorageAdapter from './adapters/local-storage-adapter';
import CoreObject from './metal/core-object';
import assign from './metal/assign';
import { GUID_KEY } from './metal/set-guid-for';

/**
 * @module shopify-buy
 * @submodule shop-client
 */

function fetchFactory(fetchType, type) {
  let func;

  switch (fetchType) {
      case 'all':
        func = function () {
          return this.fetchAll(type);
        };
        break;
      case 'one':
        func = function () {
          return this.fetch(type, ...arguments);
        };
        break;
      case 'query':
        func = function () {
          return this.fetchQuery(type, ...arguments);
        };
        break;
  }

  return func;
}

const ShopClient = CoreObject.extend({
  /**
   * @class ShopClient
   * @constructor
   */
  constructor(config) {
    this.config = config;

    this.serializers = {
      products: ListingsSerializer,
      collections: ListingsSerializer,
      carts: CartSerializer,
      references: ReferenceSerializer
    };

    this.adapters = {
      products: ListingsAdapter,
      collections: ListingsAdapter,
      carts: LocalStorageAdapter,
      references: LocalStorageAdapter
    };
  },

  config: null,

  /**
   * @attribute
   * @default {
   *  products: ListingsAdapter,
   *  collections: ListingsAdapter,
   *  carts: CartAdapter
   * }
   * @type Object
   * @protected
   */
  // Prevent leaky state
  get serializers() {
    return assign({}, this.shadowedSerializers);
  },

  set serializers(values) {
    this.shadowedSerializers = assign({}, values);
  },

  get adapters() {
    return assign({}, this.shadowedAdapters);
  },

  set adapters(values) {
    this.shadowedAdapters = assign({}, values);
  },

  /**
   * Fetch all of a `type`, returning a promise.
   *
   * ```javascript
   * client.fetchAll('products').then(products => {
   *   // do things with products
   * });
   * ```
   *
   * @method fetchAll
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @return {Promise|Array} a promise resolving with an array of `type`
   */
  fetchAll(type) {
    const adapter = new this.adapters[type](this.config);

    return adapter.fetchMultiple(type).then(payload => {
      return this.deserialize(type, payload, adapter, null, { multiple: true });
    });
  },

  /**
   * Fetch one of a `type`, returning a promise.
   *
   * ```javascript
   * client.fetch('products', 123).then(product => {
   *   // do things with the product
   * });
   * ```
   *
   * @method fetch
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {String|Number} id a unique identifier
   * @return {Promise|BaseModel} a promise resolving with a single instance of
   * `type` expressed as a `BaseModel`.
   */
  fetch(type, id) {
    const adapter = new this.adapters[type](this.config);

    return adapter.fetchSingle(type, id).then(payload => {
      return this.deserialize(type, payload, adapter, null, { single: true });
    });
  },

  /**
   * Fetch many of a `type`, that match `query`
   *
   * ```javascript
   * client.fetchQuery('products', { collection_id: 456 }).then(products => {
   *   // do things with the products
   * });
   * ```
   *
   * @method fetchQuery
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {Object} query a query sent to the api server.
   * @return {Promise|Array} a promise resolving with an array of `type`.
   */
  fetchQuery(type, query) {
    const adapter = new this.adapters[type](this.config);

    return adapter.fetchMultiple(type, query).then(payload => {
      return this.deserialize(type, payload, adapter, null, { multiple: true });
    });
  },

  /**
   * Create an instance of `type`, optionally including `modelAttrs`.
   *
   * ```javascript
   * client.create('carts', { line_items: [ ... ] }).then(cart => {
   *   // do things with the cart.
   * });
   * ```
   *
   * @method create
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {Object} [modelAttrs={}] attributes representing the internal state
   * of the model to be persisted.
   * @return {Promise|CartModel} a promise resolving with a single instance of
   * `type`
   */
  create(type, modelAttrs = {}) {
    const adapter = new this.adapters[type](this.config);
    const serializer = new this.serializers[type](this.config);
    const Model = serializer.modelForType(type);
    const model = new Model(modelAttrs, { shopClient: this });
    const attrs = serializer.serialize(type, model);

    return adapter.create(type, attrs).then(payload => {
      return this.deserialize(type, payload, adapter, serializer, { single: true });
    });
  },

  /**
   * Create an instance of `type`, optionally including `attrs`.
   *
   * ```javascript
   * client.create('carts', { line_items: [ ... ] }).then(cart => {
   *   // do things with the cart.
   * });
   * ```
   *
   * @method update
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {BaseModel} updatedModel The model that represents new state to
   * to persist.
   * @return {Promise|CartModel} a promise resolving with a single instance of
   * `type`
   */
  update(type, updatedModel) {
    const adapter = updatedModel.adapter;
    const serializer = updatedModel.serializer;
    const serializedModel = serializer.serialize(type, updatedModel);
    const id = updatedModel.attrs[adapter.idKeyForType(type)];

    return adapter.update(type, id, serializedModel).then(payload => {
      return this.deserialize(type, payload, adapter, serializer, { single: true });
    });
  },

  /**
   * Proxy to serializer's deserialize.
   *
   * @method deserialize
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {Object} payload The raw payload returned by the adapter.
   * @param {BaseAdapter} adapter The adapter that yielded the payload.
   * @param {BaseSerializer} existingSerializer The serializer to attach. If
   * none is passed, then `this.deserialize` will create one for the type.
   * @param {Object} opts Options that determine which deserialization method to
   * use.
   * @param {Boolean} opts.multiple true when the payload represents multiple
   * models
   * @param {Boolean} opts.single true when the payload represents one model.
   * @return {BaseModel} an instance of `type` reified into a model.
   */
  deserialize(type, payload, adapter, existingSerializer, opts = {}) {
    const serializer = (existingSerializer || new this.serializers[type](this.config));
    const meta = { shopClient: this, adapter, serializer, type };
    let serializedPayload;

    if (opts.multiple) {
      serializedPayload = serializer.deserializeMultiple(type, payload, meta);
    } else {
      serializedPayload = serializer.deserializeSingle(type, payload, meta);
    }

    return serializedPayload;
  },

  /**
    * Creates a {{#crossLink "CartModel"}}CartModel{{/crossLink}} instance, optionally including `attrs`.
    *
    * ```javascript
    * client.createCart().then(cart => {
    *   // do something with cart
    * });
    * ```
    *
    * @param {Object}[attrs={}] attributes representing the internal state of the cart to be persisted to localStorage.
    * @method createCart
    * @public
    * @return {Promise|CartModel} - new cart instance.
  */
  createCart(userAttrs = {}) {
    const baseAttrs = {
      line_items: []
    };
    const attrs = {};

    assign(attrs, baseAttrs);
    assign(attrs, userAttrs);

    return this.create('carts', attrs);
  },

  /**
    * Updates an existing {{#crossLink "CartModel"}}CartModel{{/crossLink}} instance and persists it to localStorage.
    *
    * ```javascript
    * client.createCart().then(cart => {
    *   cart.lineItems = [
    *     // ...
    *   ];
    *   client.updateCart(cart);
    * });
    * ```
    *
    * @param {Object}[attrs={}] attributes representing the internal state of the cart to be persisted to localStorage.
    * @method updateCart
    * @public
    * @return {Promise|CartModel} - updated cart instance.
  */
  updateCart(updatedCart) {
    return this.update('carts', updatedCart);
  },

  /**
   * Retrieve a previously created cart by its key.
   *
   * ```javascript
   * client.fetchCart('shopify-buy.1459804699118.2').then(cart => {
   *   console.log(cart); // The retrieved cart
   * });
   *
   * @method fetchCart
   * @public
   * @param {String} id The cart's unique identifier
   * @return {Promise|CartModel} The cart model.
   *
   */
  fetchCart: fetchFactory('one', 'carts'),

  /**
   * Convenience wrapper for {{#crossLink "ShopClient/fetchAll:method"}}
   * {{/crossLink}}. Equivalent to:
   *
   * ```javascript
   * client.fetchAll('products');
   * ```
   *
   * @method fetchAllProducts
   * @private
   * @return {Promise|Array} The product models.
   */
  fetchAllProducts: fetchFactory('all', 'products'),

  /**
   * Convenience wrapper for {{#crossLink "ShopClient/fetchAll:method"}}
   * {{/crossLink}}. Equivalent to:
   *
   * ```javascript
   * client.fetchAll('collections');
   * ```
   *
   * @method fetchAllCollections
   * @private
   * @return {Promise|Array} The collection models.
   */
  fetchAllCollections: fetchFactory('all', 'collections'),

  /**
   * Fetch one product by its ID.
   *
   * ```javascript
   * client.fetchProduct(123).then(product => {
   *   console.log(product); // The product with an ID of 123
   * });
   * ```
   *
   * @method fetchProduct
   * @public
   * @param {String|Number} id a unique identifier
   * @return {Promise|BaseModel} The product model with the specified ID.
   */
  fetchProduct: fetchFactory('one', 'products'),

  /**
   * Fetch one collection by its ID.
   *
   * ```javascript
   * client.fetchCollection(123).then(collection => {
   *   console.log(collection); // The collection with an ID of 123
   * });
   * ```
   *
   * @method fetchCollection
   * @public
   * @param {String|Number} id a unique identifier
   * @return {Promise|BaseModel} The collection model with the specified ID.
   */
  fetchCollection: fetchFactory('one', 'collections'),

  /**
   * Fetches a list of products matching a specified query.
   *
   * ```javascript
   * client.fetchQueryProducts({ collection_id: 123, tag: ['hats'] }).then(products => {
   *   console.log(products); // An array of products in collection `123` having the tag `hats`
   * });
   * ```
   * @method fetchQueryProducts
   * @public
   * @param {Object} query A query sent to the api server containing one or more of:
   *   @param {String|Number} [query.collection_id] The ID of a collection to retrieve products from
   *   @param {Array} [query.tag] A list of tags to filter the products by. Accepts up to 10 tags.
   *   @param {Array} [query.product_ids] A list of product IDs to retrieve
   *   @param {String|Number} [query.page=1] The page offset number of the current lookup (based on the `limit`)
   *   @param {String|Number} [query.limit=50] The number of products to retrieve per page
   *   @param {String} [query.handle] The handle of the product to look up
   *   @param {String} [query.updated_at_min] Products updated since the supplied timestamp (format: 2008-12-31 03:00)
   * @return {Promise|Array} The product models.
   */
  fetchQueryProducts: fetchFactory('query', 'products'),

  /**
   * Fetches a list of collections matching a specified query.
   *
   * ```javascript
   * client.fetchQueryCollections({page: 2, limit: 20}).then(collections => {
   *   console.log(collections); // An array of collection resources
   * });
   * ```
   *
   * @method fetchQueryCollections
   * @public
   * @param {Object} query a query sent to the api server.
   *   @param {String|Number} [query.page=1] the page offset number of the current lookup (based on the `limit`)
   *   @param {String|Number} [query.limit=50] the number of collections to retrieve per page
   * @return {Promise|Array} The collection models.
   */
  fetchQueryCollections: fetchFactory('query', 'collections'),

  /**
   * This method looks up a reference in localStorage to the most recent cart.
   * If one is not found, creates one. If the cart the reference points to
   * doesn't exist, create one and store the new reference.
   *
   * ```javascript
   * client.fetchRecentCart().then(cart => {
   *  // do stuff with the cart
   * });
   * ```
   *
   * @method fetchRecentCart
   * @public
   * @return {Promise|CartModel} The cart.
   */
  fetchRecentCart() {
    return this.fetch('references', `${this.config.domain}.recent-cart`).then(reference => {
      const cartId = reference.referenceId;

      return this.fetchCart(cartId);
    }).catch(() => {
      return this.createCart().then(cart => {
        const refAttrs = {
          referenceId: cart.id
        };

        refAttrs[GUID_KEY] = `${this.config.domain}.recent-cart`;

        this.create('references', refAttrs);

        return cart;
      });
    });
  }
});

export default ShopClient;
<commit_msg>Made fetchAllProducts and fetchAllCollections be public in docs<commit_after>import ListingsSerializer from './serializers/listings-serializer';
import ListingsAdapter from './adapters/listings-adapter';
import CartSerializer from './serializers/cart-serializer';
import ReferenceSerializer from './serializers/reference-serializer';
import LocalStorageAdapter from './adapters/local-storage-adapter';
import CoreObject from './metal/core-object';
import assign from './metal/assign';
import { GUID_KEY } from './metal/set-guid-for';

/**
 * @module shopify-buy
 * @submodule shop-client
 */

function fetchFactory(fetchType, type) {
  let func;

  switch (fetchType) {
      case 'all':
        func = function () {
          return this.fetchAll(type);
        };
        break;
      case 'one':
        func = function () {
          return this.fetch(type, ...arguments);
        };
        break;
      case 'query':
        func = function () {
          return this.fetchQuery(type, ...arguments);
        };
        break;
  }

  return func;
}

const ShopClient = CoreObject.extend({
  /**
   * @class ShopClient
   * @constructor
   */
  constructor(config) {
    this.config = config;

    this.serializers = {
      products: ListingsSerializer,
      collections: ListingsSerializer,
      carts: CartSerializer,
      references: ReferenceSerializer
    };

    this.adapters = {
      products: ListingsAdapter,
      collections: ListingsAdapter,
      carts: LocalStorageAdapter,
      references: LocalStorageAdapter
    };
  },

  config: null,

  /**
   * @attribute
   * @default {
   *  products: ListingsAdapter,
   *  collections: ListingsAdapter,
   *  carts: CartAdapter
   * }
   * @type Object
   * @protected
   */
  // Prevent leaky state
  get serializers() {
    return assign({}, this.shadowedSerializers);
  },

  set serializers(values) {
    this.shadowedSerializers = assign({}, values);
  },

  get adapters() {
    return assign({}, this.shadowedAdapters);
  },

  set adapters(values) {
    this.shadowedAdapters = assign({}, values);
  },

  /**
   * Fetch all of a `type`, returning a promise.
   *
   * ```javascript
   * client.fetchAll('products').then(products => {
   *   // do things with products
   * });
   * ```
   *
   * @method fetchAll
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @return {Promise|Array} a promise resolving with an array of `type`
   */
  fetchAll(type) {
    const adapter = new this.adapters[type](this.config);

    return adapter.fetchMultiple(type).then(payload => {
      return this.deserialize(type, payload, adapter, null, { multiple: true });
    });
  },

  /**
   * Fetch one of a `type`, returning a promise.
   *
   * ```javascript
   * client.fetch('products', 123).then(product => {
   *   // do things with the product
   * });
   * ```
   *
   * @method fetch
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {String|Number} id a unique identifier
   * @return {Promise|BaseModel} a promise resolving with a single instance of
   * `type` expressed as a `BaseModel`.
   */
  fetch(type, id) {
    const adapter = new this.adapters[type](this.config);

    return adapter.fetchSingle(type, id).then(payload => {
      return this.deserialize(type, payload, adapter, null, { single: true });
    });
  },

  /**
   * Fetch many of a `type`, that match `query`
   *
   * ```javascript
   * client.fetchQuery('products', { collection_id: 456 }).then(products => {
   *   // do things with the products
   * });
   * ```
   *
   * @method fetchQuery
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {Object} query a query sent to the api server.
   * @return {Promise|Array} a promise resolving with an array of `type`.
   */
  fetchQuery(type, query) {
    const adapter = new this.adapters[type](this.config);

    return adapter.fetchMultiple(type, query).then(payload => {
      return this.deserialize(type, payload, adapter, null, { multiple: true });
    });
  },

  /**
   * Create an instance of `type`, optionally including `modelAttrs`.
   *
   * ```javascript
   * client.create('carts', { line_items: [ ... ] }).then(cart => {
   *   // do things with the cart.
   * });
   * ```
   *
   * @method create
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {Object} [modelAttrs={}] attributes representing the internal state
   * of the model to be persisted.
   * @return {Promise|CartModel} a promise resolving with a single instance of
   * `type`
   */
  create(type, modelAttrs = {}) {
    const adapter = new this.adapters[type](this.config);
    const serializer = new this.serializers[type](this.config);
    const Model = serializer.modelForType(type);
    const model = new Model(modelAttrs, { shopClient: this });
    const attrs = serializer.serialize(type, model);

    return adapter.create(type, attrs).then(payload => {
      return this.deserialize(type, payload, adapter, serializer, { single: true });
    });
  },

  /**
   * Create an instance of `type`, optionally including `attrs`.
   *
   * ```javascript
   * client.create('carts', { line_items: [ ... ] }).then(cart => {
   *   // do things with the cart.
   * });
   * ```
   *
   * @method update
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {BaseModel} updatedModel The model that represents new state to
   * to persist.
   * @return {Promise|CartModel} a promise resolving with a single instance of
   * `type`
   */
  update(type, updatedModel) {
    const adapter = updatedModel.adapter;
    const serializer = updatedModel.serializer;
    const serializedModel = serializer.serialize(type, updatedModel);
    const id = updatedModel.attrs[adapter.idKeyForType(type)];

    return adapter.update(type, id, serializedModel).then(payload => {
      return this.deserialize(type, payload, adapter, serializer, { single: true });
    });
  },

  /**
   * Proxy to serializer's deserialize.
   *
   * @method deserialize
   * @private
   * @param {String} type The pluralized name of the type, in lower case.
   * @param {Object} payload The raw payload returned by the adapter.
   * @param {BaseAdapter} adapter The adapter that yielded the payload.
   * @param {BaseSerializer} existingSerializer The serializer to attach. If
   * none is passed, then `this.deserialize` will create one for the type.
   * @param {Object} opts Options that determine which deserialization method to
   * use.
   * @param {Boolean} opts.multiple true when the payload represents multiple
   * models
   * @param {Boolean} opts.single true when the payload represents one model.
   * @return {BaseModel} an instance of `type` reified into a model.
   */
  deserialize(type, payload, adapter, existingSerializer, opts = {}) {
    const serializer = (existingSerializer || new this.serializers[type](this.config));
    const meta = { shopClient: this, adapter, serializer, type };
    let serializedPayload;

    if (opts.multiple) {
      serializedPayload = serializer.deserializeMultiple(type, payload, meta);
    } else {
      serializedPayload = serializer.deserializeSingle(type, payload, meta);
    }

    return serializedPayload;
  },

  /**
    * Creates a {{#crossLink "CartModel"}}CartModel{{/crossLink}} instance, optionally including `attrs`.
    *
    * ```javascript
    * client.createCart().then(cart => {
    *   // do something with cart
    * });
    * ```
    *
    * @param {Object}[attrs={}] attributes representing the internal state of the cart to be persisted to localStorage.
    * @method createCart
    * @public
    * @return {Promise|CartModel} - new cart instance.
  */
  createCart(userAttrs = {}) {
    const baseAttrs = {
      line_items: []
    };
    const attrs = {};

    assign(attrs, baseAttrs);
    assign(attrs, userAttrs);

    return this.create('carts', attrs);
  },

  /**
    * Updates an existing {{#crossLink "CartModel"}}CartModel{{/crossLink}} instance and persists it to localStorage.
    *
    * ```javascript
    * client.createCart().then(cart => {
    *   cart.lineItems = [
    *     // ...
    *   ];
    *   client.updateCart(cart);
    * });
    * ```
    *
    * @param {Object}[attrs={}] attributes representing the internal state of the cart to be persisted to localStorage.
    * @method updateCart
    * @public
    * @return {Promise|CartModel} - updated cart instance.
  */
  updateCart(updatedCart) {
    return this.update('carts', updatedCart);
  },

  /**
   * Retrieve a previously created cart by its key.
   *
   * ```javascript
   * client.fetchCart('shopify-buy.1459804699118.2').then(cart => {
   *   console.log(cart); // The retrieved cart
   * });
   *
   * @method fetchCart
   * @public
   * @param {String} id The cart's unique identifier
   * @return {Promise|CartModel} The cart model.
   *
   */
  fetchCart: fetchFactory('one', 'carts'),

  /**
   * This function will return an `Array` of products from your store
   * ```
   * client.fetchAllProducts()
   * .then(function(products) {
   *   // all products in store
   * });
   * ```
   *
   * @method fetchAllProducts
   * @public
   * @return {Promise|Array} The product models.
   */
  fetchAllProducts: fetchFactory('all', 'products'),

  /**
   * This function will return an `Array` of collections from your store
   * ```
   * client.fetchAllCollections()
   * .then(function(collections) {
   *
   * });
   * ```
   *
   * @method fetchAllCollections
   * @public
   * @return {Promise|Array} The collection models.
   */
  fetchAllCollections: fetchFactory('all', 'collections'),

  /**
   * Fetch one product by its ID.
   *
   * ```javascript
   * client.fetchProduct(123).then(product => {
   *   console.log(product); // The product with an ID of 123
   * });
   * ```
   *
   * @method fetchProduct
   * @public
   * @param {String|Number} id a unique identifier
   * @return {Promise|BaseModel} The product model with the specified ID.
   */
  fetchProduct: fetchFactory('one', 'products'),

  /**
   * Fetch one collection by its ID.
   *
   * ```javascript
   * client.fetchCollection(123).then(collection => {
   *   console.log(collection); // The collection with an ID of 123
   * });
   * ```
   *
   * @method fetchCollection
   * @public
   * @param {String|Number} id a unique identifier
   * @return {Promise|BaseModel} The collection model with the specified ID.
   */
  fetchCollection: fetchFactory('one', 'collections'),

  /**
   * Fetches a list of products matching a specified query.
   *
   * ```javascript
   * client.fetchQueryProducts({ collection_id: 123, tag: ['hats'] }).then(products => {
   *   console.log(products); // An array of products in collection `123` having the tag `hats`
   * });
   * ```
   * @method fetchQueryProducts
   * @public
   * @param {Object} query A query sent to the api server containing one or more of:
   *   @param {String|Number} [query.collection_id] The ID of a collection to retrieve products from
   *   @param {Array} [query.tag] A list of tags to filter the products by. Accepts up to 10 tags.
   *   @param {Array} [query.product_ids] A list of product IDs to retrieve
   *   @param {String|Number} [query.page=1] The page offset number of the current lookup (based on the `limit`)
   *   @param {String|Number} [query.limit=50] The number of products to retrieve per page
   *   @param {String} [query.handle] The handle of the product to look up
   *   @param {String} [query.updated_at_min] Products updated since the supplied timestamp (format: 2008-12-31 03:00)
   * @return {Promise|Array} The product models.
   */
  fetchQueryProducts: fetchFactory('query', 'products'),

  /**
   * Fetches a list of collections matching a specified query.
   *
   * ```javascript
   * client.fetchQueryCollections({page: 2, limit: 20}).then(collections => {
   *   console.log(collections); // An array of collection resources
   * });
   * ```
   *
   * @method fetchQueryCollections
   * @public
   * @param {Object} query a query sent to the api server.
   *   @param {String|Number} [query.page=1] the page offset number of the current lookup (based on the `limit`)
   *   @param {String|Number} [query.limit=50] the number of collections to retrieve per page
   * @return {Promise|Array} The collection models.
   */
  fetchQueryCollections: fetchFactory('query', 'collections'),

  /**
   * This method looks up a reference in localStorage to the most recent cart.
   * If one is not found, creates one. If the cart the reference points to
   * doesn't exist, create one and store the new reference.
   *
   * ```javascript
   * client.fetchRecentCart().then(cart => {
   *  // do stuff with the cart
   * });
   * ```
   *
   * @method fetchRecentCart
   * @public
   * @return {Promise|CartModel} The cart.
   */
  fetchRecentCart() {
    return this.fetch('references', `${this.config.domain}.recent-cart`).then(reference => {
      const cartId = reference.referenceId;

      return this.fetchCart(cartId);
    }).catch(() => {
      return this.createCart().then(cart => {
        const refAttrs = {
          referenceId: cart.id
        };

        refAttrs[GUID_KEY] = `${this.config.domain}.recent-cart`;

        this.create('references', refAttrs);

        return cart;
      });
    });
  }
});

export default ShopClient;
<|endoftext|>


<commit_before>import { Server } from './lib/server';
import { Semaphore } from './lib/async-semaphore'

let sleep = require('./lib/sleep');

let sem = new Semaphore(1);

let killed = false;

async function main() {
    let srv = new Server('0.0.0.0', 6000);
    await srv.listen(async (socket, data) => {
        socket.write(JSON.stringify({}));
        await sem.take();
        console.log('incoming msg ' + data.msg);

        if (killed) {
            sem.leave();
            return;
        }

        await sleep(10000);

        if (data.msg === 'stop') {
            killed = true;
            srv.close();
        }

        console.log('message verarbeitet');
        sem.leave();
    });

    console.log('ENDE');
}

main();
<commit_msg>removed shutdownSrv test script<commit_after><|endoftext|>


<commit_before>Ext.define('Slate.sorter.Code', {
    extend: 'Ext.util.Sorter',

    config: {
        numberRe: /^\d+$/,
        numberDelim: '.',

        sorterFn: function(a, b) {
            var codeA = a.get('Code').toLowerCase(),
                codeB = b.get('Code').toLowerCase(),
                numberRe = this._numberRe, // eslint-disable-line no-underscore-dangle
                numberDelim = this._numberDelim, // eslint-disable-line no-underscore-dangle
                dotIndexA, dotIndexB,
                numberA, numberB;

            if (codeA == codeB) {
                return 0;
            }

            dotIndexA = codeA.lastIndexOf(numberDelim);
            dotIndexB = codeB.lastIndexOf(numberDelim);

            if (
                dotIndexA == -1
                || dotIndexB == -1
                || codeA.substr(0, dotIndexA) != codeB.substr(0, dotIndexB)
                || (numberA = codeA.substr(dotIndexA + 1)) == ''
                || (numberB = codeB.substr(dotIndexB + 1)) == ''
                || !numberRe.test(numberA)
                || !numberRe.test(numberB)
            ) {
                return codeA < codeB ? -1 : 1;
            }

            return numberA - numberB;
        }
    },

});<commit_msg>Add support for configurable function to extract code from item for sorting<commit_after>Ext.define('Slate.sorter.Code', {
    extend: 'Ext.util.Sorter',

    config: {
        numberRe: /^\d+$/,
        numberDelim: '.',

        codeFn: function(item) {
            return item.get('Code');
        },

        sorterFn: function(a, b) {
            var me = this,
                codeFn = me._codeFn, // eslint-disable-line no-underscore-dangle
                numberRe = me._numberRe, // eslint-disable-line no-underscore-dangle
                numberDelim = me._numberDelim, // eslint-disable-line no-underscore-dangle
                codeA = codeFn(a).toLowerCase(),
                codeB = codeFn(b).toLowerCase(),
                dotIndexA, dotIndexB,
                numberA, numberB;

            if (codeA == codeB) {
                return 0;
            }

            dotIndexA = codeA.lastIndexOf(numberDelim);
            dotIndexB = codeB.lastIndexOf(numberDelim);

            if (
                dotIndexA == -1
                || dotIndexB == -1
                || codeA.substr(0, dotIndexA) != codeB.substr(0, dotIndexB)
                || (numberA = codeA.substr(dotIndexA + 1)) == ''
                || (numberB = codeB.substr(dotIndexB + 1)) == ''
                || !numberRe.test(numberA)
                || !numberRe.test(numberB)
            ) {
                return codeA < codeB ? -1 : 1;
            }

            return numberA - numberB;
        }
    },

    constructor: function(config) {
        this.initConfig(config);
    }
});<|endoftext|>


<commit_before>let __hasProp = {}.hasOwnProperty;

// Binds a function to a context
// :: (->, {}) -> ->
let bind = (fn, context) => {
  return function() {
    return fn.apply(context, arguments);
  };
};

// Aliases
let _console = console;
let log = bind(_console.log, _console);
let error = bind(_console.error, _console);

let _ref = Array.prototype;
let slice = _ref.slice;
let push = _ref.push;

if (typeof TEST !== "undefined" && TEST !== null && TEST) {
  console.log("test mode on");
}

if (typeof DEBUG !== "undefined" && DEBUG !== null && DEBUG) {
  console.log("debug mode on");
}

// Returns true if the given value is of type boolean and is true.
// * -> boolean
let isTrue = (value) => {
  return value === true;
};

// Function that does nothing
// :: ->
let nop = () => {};

// A function to be called on the start of each queue message generated by this type.
// Can e.g. be used for debugging.
// :: ->
let onNext = nop;

// Add the given function to the message queue.
// :: (->) ->
let async = (f) => {
  setTimeout(() => {
    onNext();
    return f();
  }, 0);
};

// Returns `true` if the given value is a function.
// :: * -> boolean
let isFunc = (f) => {
  return typeof f === "function";
};

// Returns a function. A function input is returned directly, Other values will result in a function that does nothing.
// :: * -> ->
let toFunc = (f) => {
  if (isFunc(f)) {
    return f;
  } else {
    return nop;
  }
};

// Convert an argument object to an array.
// :: Arguments -> [*]
let toArray = (args) => {
  return slice.call(args, 0);
};

// Shallow copy an array.
// :: [*] -> [*]
let copyArray = (array) => {
  return array.slice();
};

// Appends content of second array to the end of the first array. Returns the changed first array.
// :: ([*], [*]) -> [*]
let appendArray = (a1, a2) => {
  push.apply(a1, a2);
  return a1;
};

// Returns the number of own key/value pairs in the given object.
// :: {} -> number
let objLength = (obj) => {
  return (Object.keys(obj)).length;
};

// Returns true if the given object has no own key/value pair.
// :: {} -> boolean
let isEmpty = (obj) => {
  return objLength(obj) === 0;
};

// Function composition
let compose = function() {
  let fns;
  fns = toArray(arguments);
  return function() {
    let fn, result, _i;
    result = (fns.pop()).apply(null, arguments);
    for (_i = fns.length - 1; _i >= 0; _i += -1) {
      fn = fns[_i];
      result = fn(result);
    }
    return result;
  };
};

// Returns a sequence generator function.
// :: -> -> number
let createSequence = () => {
  let counter = 0;
  return () => {
    return counter++;
  };
};

// Observable unique id generator function.
// :: -> number
let observableId = createSequence();

// Internal Observable constructor function
// :: (->) -> Obs<T>
let Obs = function() {
  this._id = observableId();
};

// Returns true if the given value is an observable
// :: * -> boolean
let isObservable = (obs) => {
  return obs instanceof Obs;
};

// Call the given function if the last `n` elements of the `args` array are instances of `Obs`, otherwise return a
// function which waits for more arguments to be appended on the current arguments.
// (->, [*], number) -> ->
let waitForObs = (fn, args, n) => {
  return function() {
    let args2, i, len, _i, _j;
    args2 = appendArray(copyArray(args), arguments);
    len = args2.length;
    for (i = _i = n; _i >= 1; i = _i += -1) {
      if (!isObservable(args2[len - i])) {
        return waitForObs(fn, args2, n);
      }
    }
    // The last n arguments are Observables => call function but move Observables from tail to head
    for (i = _j = 0; _j < n; i = _j += 1) {
      args2.unshift(args2.pop());
    }
    return fn.apply(null, args2);
  };
};

// Return a function which calls the given function if its last `n` arguments are instances of Obs, otherwise return a
// function which waits for more arguments to be appended on the given arguments.
let curryObs = (n, fn) => {
  return waitForObs(fn, [], n);
};

// Call the given function if enough arguments are given, otherwise return function which waits for more arguments.
// :: (->, [*]) -> *
let waitForArgs = (fn, args) => {
  if (args.length >= fn.length) {
    return fn.apply(null, args);
  } else {
    return function() {
      return waitForArgs(fn, args.concat(toArray(arguments)));
    };
  }
};

// Function currying
let curry = (fn) => {
  return function() {
    return waitForArgs(fn, toArray(arguments));
  };
};

// Unique id generator function for listeners. It would be possible to have a listener id generator per observable but
// then it would be needed to store the generator in the observable itself (for GC) so we make a global generator here
// instead.
// :: -> number
let listenerId = createSequence();

// All active listeners to all active observables are stored here.
// :: string -> string -> { onValue: ->, onError: ->, onEnd: -> }
let obsListeners = {};

// Add a new listener function for an existing observable.
// Returns a function to deregister the listener.
// :: (Observable, (T ->), (* ->), (->)) ->
let addListener = (obs, onValue, onError, onEnd) => {
  let deregister, lid, listeners;
  // Get listeners of observable
  listeners = obsListeners[obs._id];
  if (listeners == null) {
    obsListeners[obs._id] = listeners = {};
    if (obs._reg != null) {
      deregister = obs._reg.call(obs);
    }
  }
  // Add new listener
  lid = listenerId();
  listeners[lid] = {
    onValue: toFunc(onValue),
    onError: toFunc(onError),
    onEnd: toFunc(onEnd)
  };
  // Return deregister function
  return () => {
    delete listeners[lid];
    if (isEmpty(listeners)) {
      delete obsListeners[obs._id];
      if (deregister != null) {
        deregister.call(obs);
        deregister = null;
      }
    }
  };
};

let removeListener = (obs, lid) => {};

// Iterate key/value pairs of an object
// :: ((string -> *), ((string, *) ->)) ->
let iterate = (obj, callback) => {
  let key, value;
  for (key in obj) {
    if (!__hasProp.call(obj, key)) {
      continue;
    }
    value = obj[key];
    callback(key, value);
  }
};

// Emit the given value on the observable with the given id.
// :: string -> * ->
let trigger = curry((oid, value) => {
  if (value !== void 0) {
    iterate(obsListeners[oid], (_, listener) => {
      listener.onValue(value);
    });
  }
});

// Emit the given error object on the observable with the given id.
// :: string -> * ->
let triggerError = curry((oid, error) => {
  iterate(obsListeners[oid], (_, listener) => {
    listener.onError(error);
  });
});

// End the observable with the given id.
// :: string -> ->
let triggerEnd = (oid) => {
  return () => {
    iterate(obsListeners[oid], (_, listener) => {
      listener.onEnd();
    });
  };
};

// Calls the given function with a push function to emit values on the given observable.
// (Observable, (->)) -> Observable
let pushValues = (obs, create) => {
  let checkEnd, id, msgCount, next, register;
  register = null;
  id = obs._id;
  msgCount = 0; // How much messages are in the queue for this observable
  checkEnd = () => {
    if (register == null && msgCount === 0) { // No more messages in queue => end observable
      async(triggerEnd(id));
    }
  };
  next = (fn) => {
    async(() => {
      let e;
      try {
        fn.call(obs);
      } catch (_error) {
        e = _error;
        triggerError(id)(e);
      }
      msgCount -= 1;
      checkEnd();
    });
    msgCount += 1;
  };
  register = create.call(obs, trigger(id), next);
  checkEnd();
  if (register != null) {
    obs._reg = register;
  }
  return obs;
};

let globals = {
  // ((A -> B), Observable<A>) -> Observable<B>
  map: curryObs(1, (obs, fn, seed) => {
    let deregister, id, ob0, _trigger, _triggerError;
    ob0 = new Obs();
    id = ob0._id;
    _trigger = trigger(id);
    _triggerError = triggerError(id);
    deregister = addListener(obs, (value) => {
      async(() => {
        let e;
        try {
          _trigger(fn(value));
        } catch (_error) {
          e = _error;
          _triggerError(e);
        }
      });
    }, (e) => {
      async(() => {
        _triggerError(e);
      });
    }, () => {
      deregister();
      async(triggerEnd(id));
    });
    return ob0;
  }),
  take: (count, obs) => {
    let deregister, i, id, ob, _trigger, _triggerError;
    ob = new Obs();
    id = ob._id;
    _trigger = trigger(id);
    _triggerError = triggerError(id);
    i = 0; // Number of taken values
    deregister = addListener(obs, (value) => {
      if (i < count) {
        async(() => {
          _trigger(value);
        });
      }
      i += 1;
      if (i >= count) {
        deregister();
        async(triggerEnd(id));
      }
    }, (e) => {
      async(() => {
        _triggerError(e);
      });
    }, () => {
      deregister();
      async(triggerEnd(id));
    });
    return ob;
  },
  filter: curryObs(1, (obs, predicate) => {
    let deregister, elements, id, len, ob0, _trigger, _triggerError;
    ob0 = new Obs();
    id = ob0._id;
    _trigger = trigger(id);
    _triggerError = triggerError(id);
    len = predicate.length;
    elements = [];
    deregister = addListener(obs, (value) => {
      let e;
      try {
        if (isTrue(predicate(value))) {
          async(() => {
            _trigger(value);
          });
        }
      } catch (_error) {
        e = _error;
        async(() => {
          _triggerError(e);
        });
      }
    }, (e) => {
      async(() => {
        _triggerError(e);
      });
    }, () => {
      deregister();
      async(triggerEnd(id));
    });
    return ob0;
  })
};

// Prototype for all Observables.
let obsProto = Obs.prototype = {
  log: function(prefix) {
    addListener(this, (value) => {
      log(prefix, value);
    }, (e) => {
      error(prefix, e);
    }, () => {
      log(prefix, "<end>");
    });
  },
  forEach: function(onValue, onError, onEnd) {
    return addListener(this, onValue, onError, onEnd);
  },
  map: function(fn) {
    return globals.map(fn, this);
  },
  filter: function(predicate) {
    return globals.filter(predicate, this);
  },
  take: function(count) {
    return globals.take(count, this);
  }
};

// Constructor function for a new Observable. It should be used without `new`.
let Observable = (creator) => {
  return pushValues(new Obs(), creator);
};

Observable.prototype = obsProto; // For instanceof

// Will be called at the start of each queue message created by this type
// :: (->) ->
Observable.onNext = (callback) => {
  onNext = toFunc(callback);
};

// Returns an observable which emits all elements of the given array.
// :: [*] -> Observable
Observable.fromArray = (array) => {
  array = array.slice(0); // Shallow copy array
  return Observable((push, next) => {
    let pushIfAvailable, pushNext;
    pushIfAvailable = () => {
      if (array.length > 0) {
        next(pushNext);
      }
    };
    pushNext = () => {
      push(array.shift());
      pushIfAvailable();
    };
    pushIfAvailable();
  });
};

Observable.once = (value) => {
  return Observable.fromArray([value]);
};

// Copy all global functions to the exported object.
iterate(globals, (name, fn) => {
  Observable[name] = fn;
});

this.speck = {
  Observable: Observable
};

// Export internals for white box tests
if (typeof TEST !== "undefined" && TEST !== null && TEST) {
  this.speck._private = {
    toArray: toArray,
    copyArray: copyArray,
    appendArray: appendArray,
    compose: compose,
    curry: curry,
    curryObs: curryObs,
    isFunc: isFunc,
    isObservable: isObservable
  };
}

export default Observable;
<commit_msg>Simplify catch<commit_after>let __hasProp = {}.hasOwnProperty;

// Binds a function to a context
// :: (->, {}) -> ->
let bind = (fn, context) => {
  return function() {
    return fn.apply(context, arguments);
  };
};

// Aliases
let _console = console;
let log = bind(_console.log, _console);
let error = bind(_console.error, _console);

let _ref = Array.prototype;
let slice = _ref.slice;
let push = _ref.push;

if (typeof TEST !== "undefined" && TEST !== null && TEST) {
  console.log("test mode on");
}

if (typeof DEBUG !== "undefined" && DEBUG !== null && DEBUG) {
  console.log("debug mode on");
}

// Returns true if the given value is of type boolean and is true.
// * -> boolean
let isTrue = (value) => {
  return value === true;
};

// Function that does nothing
// :: ->
let nop = () => {};

// A function to be called on the start of each queue message generated by this type.
// Can e.g. be used for debugging.
// :: ->
let onNext = nop;

// Add the given function to the message queue.
// :: (->) ->
let async = (f) => {
  setTimeout(() => {
    onNext();
    return f();
  }, 0);
};

// Returns `true` if the given value is a function.
// :: * -> boolean
let isFunc = (f) => {
  return typeof f === "function";
};

// Returns a function. A function input is returned directly, Other values will result in a function that does nothing.
// :: * -> ->
let toFunc = (f) => {
  if (isFunc(f)) {
    return f;
  } else {
    return nop;
  }
};

// Convert an argument object to an array.
// :: Arguments -> [*]
let toArray = (args) => {
  return slice.call(args, 0);
};

// Shallow copy an array.
// :: [*] -> [*]
let copyArray = (array) => {
  return array.slice();
};

// Appends content of second array to the end of the first array. Returns the changed first array.
// :: ([*], [*]) -> [*]
let appendArray = (a1, a2) => {
  push.apply(a1, a2);
  return a1;
};

// Returns the number of own key/value pairs in the given object.
// :: {} -> number
let objLength = (obj) => {
  return (Object.keys(obj)).length;
};

// Returns true if the given object has no own key/value pair.
// :: {} -> boolean
let isEmpty = (obj) => {
  return objLength(obj) === 0;
};

// Function composition
let compose = function() {
  let fns;
  fns = toArray(arguments);
  return function() {
    let fn, result, _i;
    result = (fns.pop()).apply(null, arguments);
    for (_i = fns.length - 1; _i >= 0; _i += -1) {
      fn = fns[_i];
      result = fn(result);
    }
    return result;
  };
};

// Returns a sequence generator function.
// :: -> -> number
let createSequence = () => {
  let counter = 0;
  return () => {
    return counter++;
  };
};

// Observable unique id generator function.
// :: -> number
let observableId = createSequence();

// Internal Observable constructor function
// :: (->) -> Obs<T>
let Obs = function() {
  this._id = observableId();
};

// Returns true if the given value is an observable
// :: * -> boolean
let isObservable = (obs) => {
  return obs instanceof Obs;
};

// Call the given function if the last `n` elements of the `args` array are instances of `Obs`, otherwise return a
// function which waits for more arguments to be appended on the current arguments.
// (->, [*], number) -> ->
let waitForObs = (fn, args, n) => {
  return function() {
    let args2, i, len, _i, _j;
    args2 = appendArray(copyArray(args), arguments);
    len = args2.length;
    for (i = _i = n; _i >= 1; i = _i += -1) {
      if (!isObservable(args2[len - i])) {
        return waitForObs(fn, args2, n);
      }
    }
    // The last n arguments are Observables => call function but move Observables from tail to head
    for (i = _j = 0; _j < n; i = _j += 1) {
      args2.unshift(args2.pop());
    }
    return fn.apply(null, args2);
  };
};

// Return a function which calls the given function if its last `n` arguments are instances of Obs, otherwise return a
// function which waits for more arguments to be appended on the given arguments.
let curryObs = (n, fn) => {
  return waitForObs(fn, [], n);
};

// Call the given function if enough arguments are given, otherwise return function which waits for more arguments.
// :: (->, [*]) -> *
let waitForArgs = (fn, args) => {
  if (args.length >= fn.length) {
    return fn.apply(null, args);
  } else {
    return function() {
      return waitForArgs(fn, args.concat(toArray(arguments)));
    };
  }
};

// Function currying
let curry = (fn) => {
  return function() {
    return waitForArgs(fn, toArray(arguments));
  };
};

// Unique id generator function for listeners. It would be possible to have a listener id generator per observable but
// then it would be needed to store the generator in the observable itself (for GC) so we make a global generator here
// instead.
// :: -> number
let listenerId = createSequence();

// All active listeners to all active observables are stored here.
// :: string -> string -> { onValue: ->, onError: ->, onEnd: -> }
let obsListeners = {};

// Add a new listener function for an existing observable.
// Returns a function to deregister the listener.
// :: (Observable, (T ->), (* ->), (->)) ->
let addListener = (obs, onValue, onError, onEnd) => {
  let deregister, lid, listeners;
  // Get listeners of observable
  listeners = obsListeners[obs._id];
  if (listeners == null) {
    obsListeners[obs._id] = listeners = {};
    if (obs._reg != null) {
      deregister = obs._reg.call(obs);
    }
  }
  // Add new listener
  lid = listenerId();
  listeners[lid] = {
    onValue: toFunc(onValue),
    onError: toFunc(onError),
    onEnd: toFunc(onEnd)
  };
  // Return deregister function
  return () => {
    delete listeners[lid];
    if (isEmpty(listeners)) {
      delete obsListeners[obs._id];
      if (deregister != null) {
        deregister.call(obs);
        deregister = null;
      }
    }
  };
};

let removeListener = (obs, lid) => {};

// Iterate key/value pairs of an object
// :: ((string -> *), ((string, *) ->)) ->
let iterate = (obj, callback) => {
  let key, value;
  for (key in obj) {
    if (!__hasProp.call(obj, key)) {
      continue;
    }
    value = obj[key];
    callback(key, value);
  }
};

// Emit the given value on the observable with the given id.
// :: string -> * ->
let trigger = curry((oid, value) => {
  if (value !== void 0) {
    iterate(obsListeners[oid], (_, listener) => {
      listener.onValue(value);
    });
  }
});

// Emit the given error object on the observable with the given id.
// :: string -> * ->
let triggerError = curry((oid, error) => {
  iterate(obsListeners[oid], (_, listener) => {
    listener.onError(error);
  });
});

// End the observable with the given id.
// :: string -> ->
let triggerEnd = (oid) => {
  return () => {
    iterate(obsListeners[oid], (_, listener) => {
      listener.onEnd();
    });
  };
};

// Calls the given function with a push function to emit values on the given observable.
// (Observable, (->)) -> Observable
let pushValues = (obs, create) => {
  let checkEnd, id, msgCount, next, register;
  register = null;
  id = obs._id;
  msgCount = 0; // How much messages are in the queue for this observable
  checkEnd = () => {
    if (register == null && msgCount === 0) { // No more messages in queue => end observable
      async(triggerEnd(id));
    }
  };
  next = (fn) => {
    async(() => {
      try {
        fn.call(obs);
      } catch (e) {
        triggerError(id)(e);
      }
      msgCount -= 1;
      checkEnd();
    });
    msgCount += 1;
  };
  register = create.call(obs, trigger(id), next);
  checkEnd();
  if (register != null) {
    obs._reg = register;
  }
  return obs;
};

let globals = {
  // ((A -> B), Observable<A>) -> Observable<B>
  map: curryObs(1, (obs, fn, seed) => {
    let deregister, id, ob0, _trigger, _triggerError;
    ob0 = new Obs();
    id = ob0._id;
    _trigger = trigger(id);
    _triggerError = triggerError(id);
    deregister = addListener(obs, (value) => {
      async(() => {
        try {
          _trigger(fn(value));
        } catch (e) {
          _triggerError(e);
        }
      });
    }, (e) => {
      async(() => {
        _triggerError(e);
      });
    }, () => {
      deregister();
      async(triggerEnd(id));
    });
    return ob0;
  }),
  take: (count, obs) => {
    let deregister, i, id, ob, _trigger, _triggerError;
    ob = new Obs();
    id = ob._id;
    _trigger = trigger(id);
    _triggerError = triggerError(id);
    i = 0; // Number of taken values
    deregister = addListener(obs, (value) => {
      if (i < count) {
        async(() => {
          _trigger(value);
        });
      }
      i += 1;
      if (i >= count) {
        deregister();
        async(triggerEnd(id));
      }
    }, (e) => {
      async(() => {
        _triggerError(e);
      });
    }, () => {
      deregister();
      async(triggerEnd(id));
    });
    return ob;
  },
  filter: curryObs(1, (obs, predicate) => {
    let deregister, elements, id, len, ob0, _trigger, _triggerError;
    ob0 = new Obs();
    id = ob0._id;
    _trigger = trigger(id);
    _triggerError = triggerError(id);
    len = predicate.length;
    elements = [];
    deregister = addListener(obs, (value) => {
      try {
        if (isTrue(predicate(value))) {
          async(() => {
            _trigger(value);
          });
        }
      } catch (e) {
        async(() => {
          _triggerError(e);
        });
      }
    }, (e) => {
      async(() => {
        _triggerError(e);
      });
    }, () => {
      deregister();
      async(triggerEnd(id));
    });
    return ob0;
  })
};

// Prototype for all Observables.
let obsProto = Obs.prototype = {
  log: function(prefix) {
    addListener(this, (value) => {
      log(prefix, value);
    }, (e) => {
      error(prefix, e);
    }, () => {
      log(prefix, "<end>");
    });
  },
  forEach: function(onValue, onError, onEnd) {
    return addListener(this, onValue, onError, onEnd);
  },
  map: function(fn) {
    return globals.map(fn, this);
  },
  filter: function(predicate) {
    return globals.filter(predicate, this);
  },
  take: function(count) {
    return globals.take(count, this);
  }
};

// Constructor function for a new Observable. It should be used without `new`.
let Observable = (creator) => {
  return pushValues(new Obs(), creator);
};

Observable.prototype = obsProto; // For instanceof

// Will be called at the start of each queue message created by this type
// :: (->) ->
Observable.onNext = (callback) => {
  onNext = toFunc(callback);
};

// Returns an observable which emits all elements of the given array.
// :: [*] -> Observable
Observable.fromArray = (array) => {
  array = array.slice(0); // Shallow copy array
  return Observable((push, next) => {
    let pushIfAvailable, pushNext;
    pushIfAvailable = () => {
      if (array.length > 0) {
        next(pushNext);
      }
    };
    pushNext = () => {
      push(array.shift());
      pushIfAvailable();
    };
    pushIfAvailable();
  });
};

Observable.once = (value) => {
  return Observable.fromArray([value]);
};

// Copy all global functions to the exported object.
iterate(globals, (name, fn) => {
  Observable[name] = fn;
});

this.speck = {
  Observable: Observable
};

// Export internals for white box tests
if (typeof TEST !== "undefined" && TEST !== null && TEST) {
  this.speck._private = {
    toArray: toArray,
    copyArray: copyArray,
    appendArray: appendArray,
    compose: compose,
    curry: curry,
    curryObs: curryObs,
    isFunc: isFunc,
    isObservable: isObservable
  };
}

export default Observable;
<|endoftext|>


<commit_before>////////////////////////////////////
//
// SpeechSynth
// A simple wrapper around HTML5 SpeechSynthesis API for modern browsers.
// MIT-style license. Copyright 2016 Nick A. Beers
//
////////////////////////////////////
(function(window, document, undefined) {
    "use strict";
    var closedClass = ' closed';

    var SpeechSynthProto,
        SpeechSynth = function(options) {
            //Unchanging settings and defaults
            // Default values for some properties
            this.speechSynth = window.speechSynthesis;

            // Setup plugin specific options
            this.isVisible = false; // for toggle control
            this.voiceSelector = {};
            this.playButton = {};
            this.pauseButton = {};
            this.events = [];

            // Toggle element
            this.popoutElement = {};
            this.toggleButton = {};

            // Event types
            this.clickEvt = (this.usesTouch) ? "touchstart" : "mousedown";

            // set options and create element
            this.setOptions(options);
            this.init();
        };

    //////////////////////////////////////////////////////////////////////////////////

    (SpeechSynthProto = SpeechSynth.prototype).nothing = function() {};

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.setOptions = function(options) {
        var hasOwnProp = Object.prototype.hasOwnProperty,
            option;

        // setup defaults for all options that we want users to update
        this.options = {
            controlLocation: "right",
            defaultLanguageText: 'Google US English',
            volume: 1,
            rate: 1,
            pitch: 1,
        };

        // set user specified options
        if (options) {
            for (option in this.options) {
                if (hasOwnProp.call(this.options, option) && options[option] !== undefined) {
                    this.options[option] = options[option];
                }
            }
        }
    };

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.speechSynthSpeak = function(text) {
        if (text) {
            var voices = this.speechSynth.getVoices();

            var utterThis = new SpeechSynthesisUtterance(text);
            var selectedOption = this.voiceSelector.selectedOptions[0].getAttribute('data-name');
            for (var i = 0; i < voices.length; i++) {
                if (voices[i].name === selectedOption) {
                    utterThis.voice = voices[i];
                }
            }

            utterThis.volume = this.options.volume;
            utterThis.rate = this.options.rate;
            utterThis.pitch = this.options.pitch;

            this.speechSynth.speak(utterThis);
        }
    }

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.generateAudioControls = function() {
        // TODO: Create HTML components and add them into the DOM
        // Allow user to customize classes or provide their own HTML for the controls
        // and specify buttons for each action

        // controlLocation: top, bottom, left, right
        // Create container
        this.popoutElement = document.createElement('div');
        this.popoutElement.className = 'speechSynth-container ' + this.options.controlLocation.toLowerCase() + closedClass;
        this.popoutElement.id = 'speechSynth-container';

        this.toggleButton = document.createElement('div');
        this.toggleButton.className = 'speechSynth-toggle';
        this.toggleButton.id = 'speechSynth-toggle';
        this.popoutElement.appendChild(this.toggleButton);

        // create other controls
        var controlsContainer = document.createElement('div');
        controlsContainer.className = 'speechSynth-controls';

        // Select
        this.voiceSelector = CreateNewDomElement('select', 'speechSynth-select', 'speechSynth-select');
        controlsContainer.appendChild(this.voiceSelector);

        // Play button
        this.playButton = CreateNewDomElement('button', 'speechsynth-play', 'speechsynth-play speechSynth-button');
        this.playButton.textContent = 'Play';
        controlsContainer.appendChild(this.playButton);

        // Pause button
        this.pauseButton = CreateNewDomElement('button', 'speechsynth-pause', 'speechsynth-pause speechSynth-button');
        this.pauseButton.textContent = 'Pause';
        controlsContainer.appendChild(this.pauseButton);

        // append controls to container
        this.popoutElement.appendChild(controlsContainer);
        // Add controls to the page
        document.body.appendChild(this.popoutElement);
    };

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.populateVoiceList = function(self) {
        var voices = self.speechSynth.getVoices();

        for (var i = 0; i < voices.length; i++) {
            var option = document.createElement('option');
            option.textContent = voices[i].name + ' (' + voices[i].lang + ')';

            if (voices[i].default) {
                option.textContent += ' -- DEFAULT';
            }

            option.setAttribute('data-lang', voices[i].lang);
            option.setAttribute('data-name', voices[i].name);
            self.voiceSelector.appendChild(option);
        }

        // default needs work to run on all browsers properly
        for (var i = 0; i < this.voiceSelector.options.length; i++) {
            if (self.voiceSelector.options[i].getAttribute('data-name') == self.options.defaultLanguageText) {
                self.voiceSelector.selectedIndex = i;
                break;
            }
        }
    };

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.addEvents = function() {
        var self = this;
        // Add the play button
        self.events.push(self.playButton.addEventListener(self.clickEvt, function(event) {
            event.preventDefault();

            var text = self.getSelectionText();
            self.speechSynthSpeak(text);
        }));
        // Add the stop button
        self.events.push(self.pauseButton.addEventListener(self.clickEvt, function(event) {
            event.preventDefault();

            if (self.speechSynth.speaking) {
                self.speechSynth.cancel();
            }
        }));

        self.toggleButton.addEventListener(self.clickEvt, function(event) {
            self.isVisible = !self.isVisible;
            if (!self.isVisible) {
                // Hide the menu
                self.popoutElement.className += closedClass;
            } else {
                // Show the menu
                self.popoutElement.className = self.popoutElement.className.replace(closedClass, '');
            }
        });

        // setup voice list listener
        if (self.speechSynth.onvoiceschanged !== undefined) {
            self.speechSynth.onvoiceschanged = function() {
                self.populateVoiceList(self);
            };
        }
    };

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.init = function() {
        // Set up events and load in everything
        this.generateAudioControls();
        this.addEvents();
    };

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.getSelectionText = function() {
        var text = ""
        if (window.getSelection) {
            text = window.getSelection().toString();
        } else if (document.selection && document.selection.type == "Text") {
            text = document.selection.createRange().text;
        }
        return text;
    }

    //////////////////////////////////////////////////////////////////////////////////
    //
    //                                 Utility
    //
    //////////////////////////////////////////////////////////////////////////////////

    function CreateNewDomElement(elementName, idName, className) {
        if (!elementName) {
            elementName = 'div'; // Default to div
        }
        var element = document.createElement(elementName);
        if (idName) {
            element.id = idName;
        }
        if (className) {
            element.className = className;
        }

        return element;
    }

    // Expose class to user
    window.SpeechSynth = SpeechSynth;

})(this, this.document);
<commit_msg>Updating SpeechSynth to work in FireFox and adding browser support logic - May change overall implementation<commit_after>////////////////////////////////////
//
// SpeechSynth
// A simple wrapper around HTML5 SpeechSynthesis API for modern browsers.
// MIT-style license. Copyright 2016 Nick A. Beers
//
////////////////////////////////////
(function (window, document, undefined) {
	"use strict";
	var closedClass = ' closed';

	var SpeechSynthProto,
        SpeechSynth = function (options) {
            //Unchanging settings and defaults
            // Default values for some properties
            this.speechSynth = window.speechSynthesis;

            // Setup plugin specific options
            this.isVisible = false; // for toggle control
            this.voiceSelector = {};
            this.playButton = {};
            this.pauseButton = {};
            this.events = [];

            // Toggle element
            this.popoutElement = {};
            this.toggleButton = {};

            // Event types
            this.clickEvt = (this.usesTouch) ? "touchstart" : "mousedown";

            // set options and create element
            this.setOptions(options);
            this.init();
	    };

	//////////////////////////////////////////////////////////////////////////////////

    (SpeechSynthProto = SpeechSynth.prototype).nothing = function () {};

	//////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.setOptions = function (options) {
        var hasOwnProp = Object.prototype.hasOwnProperty,
            option;

        // setup defaults for all options that we want users to update
        this.options = {
            controlLocation: "right",
            defaultLanguageText: 'Google US English',
            volume: 1,
            rate: 1,
            pitch: 1,
        };

        // set user specified options
        if (options) {
            for (option in this.options) {
				if (hasOwnProp.call(this.options, option) && options[option] !== undefined) {
					this.options[option] = options[option];
				}
			}
        }
    };

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.speechSynthSpeak = function (text) {
        if (text) {
            var voices = this.speechSynth.getVoices();

            var utterThis = new SpeechSynthesisUtterance(text);
            var selectedOption = this.voiceSelector.selectedOptions[0].getAttribute('data-name');
            for (var i = 0; i < voices.length; i++) {
                if (voices[i].name === selectedOption) {
                    utterThis.voice = voices[i];
                }
            }

            utterThis.volume = this.options.volume;
            utterThis.rate = this.options.rate;
            utterThis.pitch = this.options.pitch;

            this.speechSynth.speak(utterThis);
        }
    }

	//////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.generateAudioControls = function () {
        // TODO: Create HTML components and add them into the DOM
        // Allow user to customize classes or provide their own HTML for the controls
        // and specify buttons for each action

        // controlLocation: top, bottom, left, right
        // Create container
        this.popoutElement = document.createElement('div');
        this.popoutElement.className = 'speechSynth-container ' + this.options.controlLocation.toLowerCase() + closedClass;
        this.popoutElement.id = 'speechSynth-container';

        this.toggleButton = document.createElement('div');
        this.toggleButton.className = 'speechSynth-toggle';
        this.toggleButton.id = 'speechSynth-toggle';
        this.popoutElement.appendChild(this.toggleButton);

        // create other controls
        var controlsContainer = document.createElement('div');
        controlsContainer.className = 'speechSynth-controls';

        // Select
        this.voiceSelector = CreateNewDomElement('select', 'speechSynth-select', 'speechSynth-select');
        controlsContainer.appendChild(this.voiceSelector);

        // Play button
        this.playButton = CreateNewDomElement('button', 'speechsynth-play', 'speechsynth-play speechSynth-button');
        this.playButton.textContent = 'Play';
        controlsContainer.appendChild(this.playButton);

        // Pause button
        this.pauseButton = CreateNewDomElement('button', 'speechsynth-pause', 'speechsynth-pause speechSynth-button');
        this.pauseButton.textContent = 'Pause';
        controlsContainer.appendChild(this.pauseButton);

        // append controls to container
        this.popoutElement.appendChild(controlsContainer);
        // Add controls to the page
        document.body.appendChild(this.popoutElement);
    };

	//////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.populateVoiceList = function (self) {
        var voices = self.speechSynth.getVoices();

        for (var i = 0; i < voices.length; i++) {
            var option = document.createElement('option');
            option.textContent = voices[i].name + ' (' + voices[i].lang + ')';

            if (voices[i].default) {
                option.textContent += ' -- DEFAULT';
            }

            option.setAttribute('data-lang', voices[i].lang);
            option.setAttribute('data-name', voices[i].name);
            self.voiceSelector.appendChild(option);
        }

        // default needs work to run on all browsers properly
        for (var i = 0; i < this.voiceSelector.options.length; i++) {
            if (self.voiceSelector.options[i].getAttribute('data-name') == self.options.defaultLanguageText) {
                self.voiceSelector.selectedIndex = i;
                break;
            }
        }
    };

	//////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.addEvents = function ( ) {
        var self = this;
        // Add the play button
        self.events.push(self.playButton.addEventListener(self.clickEvt, function(event) {
            event.preventDefault();

            var text = self.getSelectionText();
            self.speechSynthSpeak(text);
        }));
        // Add the stop button
        self.events.push(self.pauseButton.addEventListener(self.clickEvt, function(event){
            event.preventDefault();

            if(self.speechSynth.speaking){
                self.speechSynth.cancel();
            }
        }));

        self.toggleButton.addEventListener(self.clickEvt, function (event) {
            self.isVisible = !self.isVisible;
            if (!self.isVisible) {
                // Hide the menu
                self.popoutElement.className += closedClass;
            } else {
                // Show the menu
                self.popoutElement.className = self.popoutElement.className.replace(closedClass, '');
            }
        });

        // setup voice list listener
        self.populateVoiceList(self);

        if (self.speechSynth.onvoiceschanged !== undefined) {
            self.speechSynth.onvoiceschanged = function() { self.populateVoiceList(self); };
        }
    };

	//////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.init = function ( ) {
        if(!isSpeechSynthesisSupported()){
            if (console) {
                console.log('The speechSynthesis API is not currently supported on your browser.')
            }
            return;
        }
        // Set up events and load in everything
        this.generateAudioControls();
		this.addEvents();
	};

    //////////////////////////////////////////////////////////////////////////////////

    SpeechSynthProto.getSelectionText = function ( ) {
        var text = ""
        if (window.getSelection) {
            text = window.getSelection().toString();
        } else if (document.selection && document.selection.type == "Text") {
            text = document.selection.createRange().text;
        }
        return text;
    }

	//////////////////////////////////////////////////////////////////////////////////
    //
    //                                 Utility
    //
	//////////////////////////////////////////////////////////////////////////////////

    function CreateNewDomElement(elementName, idName, className){
        if(!elementName){
            elementName = 'div'; // Default to div
        }
        var element = document.createElement(elementName);
        if (idName) {
            element.id = idName;
        }
        if (className) {
            element.className = className;
        }

        return element;
    }

	//////////////////////////////////////////////////////////////////////////////////

    function get_browser(){
        // Credit goes to Hermann Ingjaldsson on Stackoverflow for their answer
        var ua= navigator.userAgent, tem,
        M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
        if(/trident/i.test(M[1])){
            tem=  /\brv[ :]+(\d+)/g.exec(ua) || [];
            return 'IE '+(tem[1] || '');
        }
        if(M[1]=== 'Chrome'){
            tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
            if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
        }
        M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
        if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]);
        return {
          name: M[0],
          version: M[1]
        };
    }

	//////////////////////////////////////////////////////////////////////////////////

    function isSpeechSynthesisSupported(){
        // the speechSynthesis API is not supported in certain browsers right now and certain versions.
        var browserSpecs = get_browser();
        var isSupported = false;
        switch (browserSpecs.name) {
            case "Chrome":
                isSupported = browserSpecs.version >= 33;
                break;
            case "Edge":
                isSupported = browserSpecs.version >= 14.14393;
                break;
            case "Firefox":
                isSupported = browserSpecs.version >= 49;
                break;
            case "Safari":
                isSupported = browserSpecs.version >= 7;
                break;
            default:
                break;
        }
        return isSupported;
    }

    // Expose class to user
    window.SpeechSynth = SpeechSynth;

})(this, this.document);
<|endoftext|>


<commit_before>/* globals __DEV__ */
import Phaser from 'phaser'

var map;
var base_layer;
var lava_layer;
var non_colide_layer;
var background_layer;
var coinSound;
var teleporters;
var teleported = false;
var score = 0;
var scoreText;
var timer;
var scoreBackgroundSprite;

var darts_left = [];
var darts_right = [];
var fireButton;
var dartTraps;

export default class extends Phaser.State {
  init (){
    // Start the Arcade physics system (for movements and collisions)
    this.physics.startSystem(Phaser.Physics.ARCADE);
    // Add the physics engine to all `game` objects
    this.world.enableBody = true;
  }
  preload () {
    game.load.audio('coinSound', 'assets/coin_sound.wav');

    // Platformer images
    game.load.image('player', 'assets/player.png');
    game.load.image('coin', 'assets/coin.png');
    game.load.image('scoreBackground', 'assets/score_counter.png');
    game.load.image('dart', 'assets/dart.png');
    game.load.image('dart_trap_left', 'assets/dart_trap_left.png');

    game.load.tilemap('level', 'assets/level.json', null, Phaser.Tilemap.TILED_JSON);
    game.load.image('tiles', 'assets/Set_01.png');
    game.load.spritesheet("teleporter_ani", "assets/teleporter_ani.png", 64, 96);
    if (teleported === true) {
      setTimeout(function(){
        teleported = false;
      }, 3000);
    }
  }

  create () {
    score = 0;
    // capture coin sound
    coinSound = game.add.audio('coinSound');

    map = game.add.tilemap('level');

    // Tileset
    map.addTilesetImage('Set_01', 'tiles');

    // Create Layers
    background_layer = map.createLayer('background');
    base_layer = map.createLayer('base');
    non_colide_layer = map.createLayer('non_colide');
    non_colide_layer = map.createLayer('non_colide2');
    lava_layer = map.createLayer('lava');

    // Setup groups
    this.coins = game.add.group();
    this.player = game.add.group();
    teleporters = game.add.group();
    dartTraps = game.add.group();

    // Here we set the physics
    this.coins.enableBody = true;
    teleporters.enableBody = true;
    this.player.enableBody = true;

    base_layer.resizeWorld();

    // Variable to store the arrow key pressed
    this.spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    this.cursor = game.input.keyboard.createCursorKeys();

    // Substtute player
    map.createFromObjects('player', 1, 'player', 0, true, false, this.player);
    // Substtute teleporter
    map.createFromObjects('Object Layer 1', 14766, 'teleporter_ani', 0, true, false, teleporters);
    // Substtute coins
    map.createFromObjects('Object Layer 1', 121, 'coin', 0, true, false, this.coins);

    // Animations
    teleporters.callAll('animations.add', 'animations', 'teleporterAnimation', [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23], 25, false);

    // Renames the player group to be the one sprite that is the child.
    // Reason for this is to keep the code consistant with what was there before.
    this.player = this.player.children[0]

    // Add gravity to make it fall
    this.player.body.gravity.y = 600;

    // This is the base (floor)
    map.setCollision([3,4,5,6,7,8,9,10,11,12,13], true, base_layer);
    // This is the lava_layer
    // TODO: Fix spikes (they should act the same as lava)
    map.setCollision([1,2,3], true, lava_layer);

    // Camera follow player
    game.camera.follow(this.player, Phaser.Camera.FOLLOW_PLATFORMER)
    // Add scoreboard
    // scoreBackgroundSprite = game.add.sprite( 128, 64, 'scoreBackground');
    scoreBackgroundSprite = game.add.sprite(10, 10, 'scoreBackground');

    var style = { font: "20px Arial", wordWrap: true, wordWrapWidth: scoreBackgroundSprite.width, align: "center" };

    scoreText = game.add.text(75, 55, '0', style);
    scoreText.anchor.set(0.5);

    scoreText.fixedToCamera = true;
    scoreBackgroundSprite.fixedToCamera = true;

    //  Create our Timer
    timer = game.time.create(false);

    //  Set a TimerEvent to occur after 2 seconds
    timer.loop(2000, this.updateCounter, this);

    //  Start the timer running - this is important!
    //  It won't start automatically, allowing you to hook it to button events and the like.
    timer.start();
    //  Creates 30 bullets, using the 'bullet' graphic
    map.createFromObjects('Object Layer 1', 14641, 'dart_trap_left', 0, true, false, dartTraps);

    // dartTraps.children.every(this.dartTrapsToTrack);
    dartTraps.children[0]

    for (var i=0, l=dartTraps.children.length; i<l; i++) {
      darts_left[i] = game.add.weapon(40, 'dart');
      darts_left[i].bulletKillType = Phaser.Weapon.KILL_WORLD_BOUNDS;
      darts_left[i].bulletSpeed = 400;
      darts_left[i].fireRate = 2000;
      darts_left[i].trackSprite(dartTraps.children[i], 0, 15, true);
      darts_left[i].trackRotation = false;
      darts_left[i].fireAngle = Phaser.ANGLE_LEFT;
      darts_left[i].autofire = true;
    }

    // TODO: Add right facing turrets and assets
    // TODO: Handle bullet collisions

  }

  update () {

    scoreText.x = Math.floor(scoreBackgroundSprite.x + scoreBackgroundSprite.width / 2);
    scoreText.y = Math.floor(scoreBackgroundSprite.y + scoreBackgroundSprite.height / 2);
    // Make the player and the walls collide
    game.physics.arcade.collide(this.player, base_layer);

    // Call the 'takeCoin' function when the player takes a coin
    game.physics.arcade.overlap(this.player, this.coins, this.takeCoin, null, this);

    game.physics.arcade.overlap(this.player, teleporters, this.teleport, null, this);

    // Call the 'restart' function when the player touches the enemy
    game.physics.arcade.collide(this.player, lava_layer, this.restart, null, this);

    // Move the player when an arrow key is pressed
    if (this.cursor.left.isDown) {
      this.player.body.velocity.x = -200;
    }
    else if (this.cursor.right.isDown) {
      this.player.body.velocity.x = 200;
    }
    else {
      this.player.body.velocity.x = 0;
    }

  if ((this.cursor.up.isDown || this.spaceKey.isDown) && this.player.body.blocked.down) {
      this.player.body.velocity.y = -350;
    }
  // game.input.keyboard.onDownCallback = function() {        console.log(game.input.keyboard.event.keyCode);       };
  }

  render () {}

  asdasd () {
    console.log('potato');
  }
  dartsFire () {
    for (var i=0, l=dartTraps.children.length; i<l; i++) {
      darts_left[i].fire()
    }
  }
  // Function to kill a coin
  takeCoin (player, coin) {
    coinSound.play();
    coin.kill();
    score += 10;
    scoreText.text = score;
  }

  updateCounter () {
    score--
    scoreText.text = score;
  }

  teleport () {
    teleporters.callAll('animations.play', 'animations', 'teleporterAnimation');
    setTimeout(function(){
      if (teleported === false){
        game.state.start('Game');
        teleported = true;
      }
     }, 1000);
  }

  // Function to restart the game
  restart () {
    game.state.start('Game');
  }
}
<commit_msg>Added collisions<commit_after>/* globals __DEV__ */
import Phaser from 'phaser'

var map;
var base_layer;
var lava_layer;
var non_colide_layer;
var background_layer;
var coinSound;
var teleporters;
var teleported = false;
var score = 0;
var scoreText;
var timer;
var scoreBackgroundSprite;

var darts_left = [];
var darts_right = [];
var fireButton;
var dartTraps;

export default class extends Phaser.State {
  init (){
    // Start the Arcade physics system (for movements and collisions)
    this.physics.startSystem(Phaser.Physics.ARCADE);
    // Add the physics engine to all `game` objects
    this.world.enableBody = true;
  }
  preload () {
    game.load.audio('coinSound', 'assets/coin_sound.wav');

    // Platformer images
    game.load.image('player', 'assets/player.png');
    game.load.image('coin', 'assets/coin.png');
    game.load.image('scoreBackground', 'assets/score_counter.png');
    game.load.image('dart', 'assets/dart.png');
    game.load.image('dart_trap_left', 'assets/dart_trap_left.png');

    game.load.tilemap('level', 'assets/level.json', null, Phaser.Tilemap.TILED_JSON);
    game.load.image('tiles', 'assets/Set_01.png');
    game.load.spritesheet("teleporter_ani", "assets/teleporter_ani.png", 64, 96);
    if (teleported === true) {
      setTimeout(function(){
        teleported = false;
      }, 3000);
    }
  }

  create () {
    score = 0;
    // capture coin sound
    coinSound = game.add.audio('coinSound');

    map = game.add.tilemap('level');

    // Tileset
    map.addTilesetImage('Set_01', 'tiles');

    // Create Layers
    background_layer = map.createLayer('background');
    base_layer = map.createLayer('base');
    non_colide_layer = map.createLayer('non_colide');
    non_colide_layer = map.createLayer('non_colide2');
    lava_layer = map.createLayer('lava');

    // Setup groups
    this.coins = game.add.group();
    this.player = game.add.group();
    teleporters = game.add.group();
    dartTraps = game.add.group();

    // Here we set the physics
    this.coins.enableBody = true;
    teleporters.enableBody = true;
    this.player.enableBody = true;

    base_layer.resizeWorld();

    // Variable to store the arrow key pressed
    this.spaceKey = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);
    this.cursor = game.input.keyboard.createCursorKeys();

    // Substtute player
    map.createFromObjects('player', 1, 'player', 0, true, false, this.player);
    // Substtute teleporter
    map.createFromObjects('Object Layer 1', 14766, 'teleporter_ani', 0, true, false, teleporters);
    // Substtute coins
    map.createFromObjects('Object Layer 1', 121, 'coin', 0, true, false, this.coins);

    // Animations
    teleporters.callAll('animations.add', 'animations', 'teleporterAnimation', [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23], 25, false);

    // Renames the player group to be the one sprite that is the child.
    // Reason for this is to keep the code consistant with what was there before.
    this.player = this.player.children[0]

    // Add gravity to make it fall
    this.player.body.gravity.y = 600;

    // This is the base (floor)
    map.setCollision([3,4,5,6,7,8,9,10,11,12,13], true, base_layer);
    // This is the lava_layer
    // TODO: Fix spikes (they should act the same as lava)
    map.setCollision([1,2,3], true, lava_layer);

    // Camera follow player
    game.camera.follow(this.player, Phaser.Camera.FOLLOW_PLATFORMER)
    // Add scoreboard
    // scoreBackgroundSprite = game.add.sprite( 128, 64, 'scoreBackground');
    scoreBackgroundSprite = game.add.sprite(10, 10, 'scoreBackground');

    var style = { font: "20px Arial", wordWrap: true, wordWrapWidth: scoreBackgroundSprite.width, align: "center" };

    scoreText = game.add.text(75, 55, '0', style);
    scoreText.anchor.set(0.5);

    scoreText.fixedToCamera = true;
    scoreBackgroundSprite.fixedToCamera = true;

    //  Create our Timer
    timer = game.time.create(false);

    //  Set a TimerEvent to occur after 2 seconds
    timer.loop(2000, this.updateCounter, this);

    //  Start the timer running - this is important!
    //  It won't start automatically, allowing you to hook it to button events and the like.
    timer.start();
    //  Creates 30 bullets, using the 'bullet' graphic
    map.createFromObjects('Object Layer 1', 14641, 'dart_trap_left', 0, true, false, dartTraps);

    // dartTraps.children.every(this.dartTrapsToTrack);
    dartTraps.children[0]

    for (var i=0, l=dartTraps.children.length; i<l; i++) {
      darts_left[i] = game.add.weapon(40, 'dart');
      darts_left[i].bulletKillType = Phaser.Weapon.KILL_WORLD_BOUNDS;
      darts_left[i].bulletSpeed = 400;
      darts_left[i].fireRate = 2000;
      darts_left[i].trackSprite(dartTraps.children[i], 0, 15, true);
      darts_left[i].trackRotation = false;
      darts_left[i].fireAngle = Phaser.ANGLE_LEFT;
      darts_left[i].autofire = true;
    }

    // TODO: Add right facing turrets and assets

  }

  update () {

    // game.physics.arcade.overlap(this.player, this.darts_left, this.asdasd, null, this);
    for (var i=0, l=dartTraps.children.length; i<l; i++) {
      game.physics.arcade.overlap(this.player, darts_left[i].bullets, this.asdasd, null, this);
    }
    scoreText.x = Math.floor(scoreBackgroundSprite.x + scoreBackgroundSprite.width / 2);
    scoreText.y = Math.floor(scoreBackgroundSprite.y + scoreBackgroundSprite.height / 2);
    // Make the player and the walls collide
    game.physics.arcade.collide(this.player, base_layer);

    // Call the 'takeCoin' function when the player takes a coin
    game.physics.arcade.overlap(this.player, this.coins, this.takeCoin, null, this);

    game.physics.arcade.overlap(this.player, teleporters, this.teleport, null, this);

    // Call the 'restart' function when the player touches the enemy
    game.physics.arcade.collide(this.player, lava_layer, this.restart, null, this);

    // Move the player when an arrow key is pressed
    if (this.cursor.left.isDown) {
      this.player.body.velocity.x = -200;
    }
    else if (this.cursor.right.isDown) {
      this.player.body.velocity.x = 200;
    }
    else {
      this.player.body.velocity.x = 0;
    }

  if ((this.cursor.up.isDown || this.spaceKey.isDown) && this.player.body.blocked.down) {
      this.player.body.velocity.y = -350;
    }
  // game.input.keyboard.onDownCallback = function() {        console.log(game.input.keyboard.event.keyCode);       };
  }

  render () {}

  asdasd () {
    game.state.start('Game');
  }
  dartsFire () {
    for (var i=0, l=dartTraps.children.length; i<l; i++) {
      darts_left[i].fire()
    }
  }
  // Function to kill a coin
  takeCoin (player, coin) {
    coinSound.play();
    coin.kill();
    score += 10;
    scoreText.text = score;
  }

  updateCounter () {
    score--
    scoreText.text = score;
  }

  teleport () {
    teleporters.callAll('animations.play', 'animations', 'teleporterAnimation');
    setTimeout(function(){
      if (teleported === false){
        game.state.start('Game');
        teleported = true;
      }
     }, 1000);
  }

  // Function to restart the game
  restart () {
    game.state.start('Game');
  }
}
<|endoftext|>


<commit_before>import React, { Component } from 'react'

import { getSDK } from '../utils'
import createSinglePlayer from '../singlePlayer'

const IOS = typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
const AUDIO_EXTENSIONS = /\.(m4a|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx)($|\?)/i
const VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)($|\?)/i
const HLS_EXTENSIONS = /\.(m3u8)($|\?)/i
const HLS_SDK_URL = 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.8.9/hls.min.js'
const HLS_GLOBAL = 'Hls'
const DASH_EXTENSIONS = /\.(mpd)($|\?)/i
const DASH_SDK_URL = 'https://cdnjs.cloudflare.com/ajax/libs/dashjs/2.6.5/dash.all.min.js'
const DASH_GLOBAL = 'dashjs'

function canPlay (url) {
  if (url instanceof Array) {
    for (let item of url) {
      if (typeof item === 'string' && canPlay(item)) {
        return true
      }
      if (canPlay(item.src)) {
        return true
      }
    }
    return false
  }
  return (
    AUDIO_EXTENSIONS.test(url) ||
    VIDEO_EXTENSIONS.test(url) ||
    HLS_EXTENSIONS.test(url) ||
    DASH_EXTENSIONS.test(url)
  )
}

export class FilePlayer extends Component {
  static displayName = 'FilePlayer'
  static canPlay = canPlay

  componentDidMount () {
    this.addListeners()
    if (IOS) {
      this.player.load()
    }
  }
  componentWillReceiveProps (nextProps) {
    if (this.shouldUseAudio(this.props) !== this.shouldUseAudio(nextProps)) {
      this.removeListeners()
    }
  }
  componentDidUpdate (prevProps) {
    if (this.shouldUseAudio(this.props) !== this.shouldUseAudio(prevProps)) {
      this.addListeners()
    }
  }
  componentWillUnmount () {
    this.removeListeners()
  }
  addListeners () {
    const { onReady, onPlay, onPause, onEnded, onError, playsinline } = this.props
    this.player.addEventListener('canplay', onReady)
    this.player.addEventListener('play', onPlay)
    this.player.addEventListener('pause', onPause)
    this.player.addEventListener('seeked', this.onSeek)
    this.player.addEventListener('ended', onEnded)
    this.player.addEventListener('error', onError)
    if (playsinline) {
      this.player.setAttribute('playsinline', '')
      this.player.setAttribute('webkit-playsinline', '')
    }
  }
  removeListeners () {
    const { onReady, onPlay, onPause, onEnded, onError } = this.props
    this.player.removeEventListener('canplay', onReady)
    this.player.removeEventListener('play', onPlay)
    this.player.removeEventListener('pause', onPause)
    this.player.removeEventListener('seeked', this.onSeek)
    this.player.removeEventListener('ended', onEnded)
    this.player.removeEventListener('error', onError)
  }
  onSeek = e => {
    this.props.onSeek(e.target.currentTime)
  }
  shouldUseAudio (props) {
    return AUDIO_EXTENSIONS.test(props.url) || props.config.file.forceAudio
  }
  shouldUseHLS (url) {
    return (HLS_EXTENSIONS.test(url) && !IOS) || this.props.config.file.forceHLS
  }
  shouldUseDASH (url) {
    return DASH_EXTENSIONS.test(url) || this.props.config.file.forceDASH
  }
  load (url) {
    if (this.shouldUseHLS(url)) {
      getSDK(HLS_SDK_URL, HLS_GLOBAL).then(Hls => {
        this.hls = new Hls(this.props.config.file.hlsOptions)
        this.hls.on(Hls.Events.ERROR, (e, data) => {
          this.props.onError(e, data, this.hls, Hls)
        })
        this.hls.loadSource(url)
        this.hls.attachMedia(this.player)
      })
    }
    if (this.shouldUseDASH(url)) {
      getSDK(DASH_SDK_URL, DASH_GLOBAL).then(dashjs => {
        this.dash = dashjs.MediaPlayer().create()
        this.dash.initialize(this.player, url, this.props.playing)
        this.dash.getDebug().setLogToBrowserConsole(false)
      })
    }
  }
  play () {
    const promise = this.player.play()
    if (promise) {
      promise.catch(this.props.onError)
    }
  }
  pause () {
    this.player.pause()
  }
  stop () {
    this.player.removeAttribute('src')
    if (this.hls) {
      this.hls.destroy()
    }
    if (this.dash) {
      this.dash.reset()
    }
  }
  seekTo (seconds) {
    this.player.currentTime = seconds
  }
  setVolume (fraction) {
    this.player.volume = fraction
  }
  setPlaybackRate (rate) {
    this.player.playbackRate = rate
  }
  getDuration () {
    return this.player.duration
  }
  getCurrentTime () {
    return this.player.currentTime
  }
  getSecondsLoaded () {
    if (this.player.buffered.length === 0) return 0
    return this.player.buffered.end(0)
  }
  renderSource = (source, index) => {
    if (typeof source === 'string') {
      return <source key={index} src={source} />
    }
    return <source key={index} {...source} />
  }
  renderTrack = (track, index) => {
    return <track key={index} {...track} />
  }
  ref = player => {
    this.player = player
  }
  render () {
    const { url, loop, controls, config, width, height } = this.props
    const useAudio = this.shouldUseAudio(this.props)
    const useHLS = this.shouldUseHLS(url)
    const useDASH = this.shouldUseDASH(url)
    const Element = useAudio ? 'audio' : 'video'
    const src = url instanceof Array || useHLS || useDASH ? undefined : url
    const style = {
      width: !width || width === 'auto' ? width : '100%',
      height: !height || height === 'auto' ? height : '100%'
    }
    return (
      <Element
        ref={this.ref}
        src={src}
        style={style}
        preload='auto'
        controls={controls}
        loop={loop}
        {...config.file.attributes}>
        {url instanceof Array &&
          url.map(this.renderSource)
        }
        {config.file.tracks.map(this.renderTrack)}
      </Element>
    )
  }
}

export default createSinglePlayer(FilePlayer)
<commit_msg>Render video element when poster is set<commit_after>import React, { Component } from 'react'

import { getSDK } from '../utils'
import createSinglePlayer from '../singlePlayer'

const IOS = typeof navigator !== 'undefined' && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream
const AUDIO_EXTENSIONS = /\.(m4a|mp4a|mpga|mp2|mp2a|mp3|m2a|m3a|wav|weba|aac|oga|spx)($|\?)/i
const VIDEO_EXTENSIONS = /\.(mp4|og[gv]|webm|mov|m4v)($|\?)/i
const HLS_EXTENSIONS = /\.(m3u8)($|\?)/i
const HLS_SDK_URL = 'https://cdnjs.cloudflare.com/ajax/libs/hls.js/0.8.9/hls.min.js'
const HLS_GLOBAL = 'Hls'
const DASH_EXTENSIONS = /\.(mpd)($|\?)/i
const DASH_SDK_URL = 'https://cdnjs.cloudflare.com/ajax/libs/dashjs/2.6.5/dash.all.min.js'
const DASH_GLOBAL = 'dashjs'

function canPlay (url) {
  if (url instanceof Array) {
    for (let item of url) {
      if (typeof item === 'string' && canPlay(item)) {
        return true
      }
      if (canPlay(item.src)) {
        return true
      }
    }
    return false
  }
  return (
    AUDIO_EXTENSIONS.test(url) ||
    VIDEO_EXTENSIONS.test(url) ||
    HLS_EXTENSIONS.test(url) ||
    DASH_EXTENSIONS.test(url)
  )
}

export class FilePlayer extends Component {
  static displayName = 'FilePlayer'
  static canPlay = canPlay

  componentDidMount () {
    this.addListeners()
    if (IOS) {
      this.player.load()
    }
  }
  componentWillReceiveProps (nextProps) {
    if (this.shouldUseAudio(this.props) !== this.shouldUseAudio(nextProps)) {
      this.removeListeners()
    }
  }
  componentDidUpdate (prevProps) {
    if (this.shouldUseAudio(this.props) !== this.shouldUseAudio(prevProps)) {
      this.addListeners()
    }
  }
  componentWillUnmount () {
    this.removeListeners()
  }
  addListeners () {
    const { onReady, onPlay, onPause, onEnded, onError, playsinline } = this.props
    this.player.addEventListener('canplay', onReady)
    this.player.addEventListener('play', onPlay)
    this.player.addEventListener('pause', onPause)
    this.player.addEventListener('seeked', this.onSeek)
    this.player.addEventListener('ended', onEnded)
    this.player.addEventListener('error', onError)
    if (playsinline) {
      this.player.setAttribute('playsinline', '')
      this.player.setAttribute('webkit-playsinline', '')
    }
  }
  removeListeners () {
    const { onReady, onPlay, onPause, onEnded, onError } = this.props
    this.player.removeEventListener('canplay', onReady)
    this.player.removeEventListener('play', onPlay)
    this.player.removeEventListener('pause', onPause)
    this.player.removeEventListener('seeked', this.onSeek)
    this.player.removeEventListener('ended', onEnded)
    this.player.removeEventListener('error', onError)
  }
  onSeek = e => {
    this.props.onSeek(e.target.currentTime)
  }
  shouldUseAudio (props) {
    if (this.props.config.file.attributes.poster) {
      return false // Use <video> so that poster is shown
    }
    return AUDIO_EXTENSIONS.test(props.url) || props.config.file.forceAudio
  }
  shouldUseHLS (url) {
    return (HLS_EXTENSIONS.test(url) && !IOS) || this.props.config.file.forceHLS
  }
  shouldUseDASH (url) {
    return DASH_EXTENSIONS.test(url) || this.props.config.file.forceDASH
  }
  load (url) {
    if (this.shouldUseHLS(url)) {
      getSDK(HLS_SDK_URL, HLS_GLOBAL).then(Hls => {
        this.hls = new Hls(this.props.config.file.hlsOptions)
        this.hls.on(Hls.Events.ERROR, (e, data) => {
          this.props.onError(e, data, this.hls, Hls)
        })
        this.hls.loadSource(url)
        this.hls.attachMedia(this.player)
      })
    }
    if (this.shouldUseDASH(url)) {
      getSDK(DASH_SDK_URL, DASH_GLOBAL).then(dashjs => {
        this.dash = dashjs.MediaPlayer().create()
        this.dash.initialize(this.player, url, this.props.playing)
        this.dash.getDebug().setLogToBrowserConsole(false)
      })
    }
  }
  play () {
    const promise = this.player.play()
    if (promise) {
      promise.catch(this.props.onError)
    }
  }
  pause () {
    this.player.pause()
  }
  stop () {
    this.player.removeAttribute('src')
    if (this.hls) {
      this.hls.destroy()
    }
    if (this.dash) {
      this.dash.reset()
    }
  }
  seekTo (seconds) {
    this.player.currentTime = seconds
  }
  setVolume (fraction) {
    this.player.volume = fraction
  }
  setPlaybackRate (rate) {
    this.player.playbackRate = rate
  }
  getDuration () {
    return this.player.duration
  }
  getCurrentTime () {
    return this.player.currentTime
  }
  getSecondsLoaded () {
    if (this.player.buffered.length === 0) return 0
    return this.player.buffered.end(0)
  }
  renderSource = (source, index) => {
    if (typeof source === 'string') {
      return <source key={index} src={source} />
    }
    return <source key={index} {...source} />
  }
  renderTrack = (track, index) => {
    return <track key={index} {...track} />
  }
  ref = player => {
    this.player = player
  }
  render () {
    const { url, loop, controls, config, width, height } = this.props
    const useAudio = this.shouldUseAudio(this.props)
    const useHLS = this.shouldUseHLS(url)
    const useDASH = this.shouldUseDASH(url)
    const Element = useAudio ? 'audio' : 'video'
    const src = url instanceof Array || useHLS || useDASH ? undefined : url
    const style = {
      width: !width || width === 'auto' ? width : '100%',
      height: !height || height === 'auto' ? height : '100%'
    }
    return (
      <Element
        ref={this.ref}
        src={src}
        style={style}
        preload='auto'
        controls={controls}
        loop={loop}
        {...config.file.attributes}>
        {url instanceof Array &&
          url.map(this.renderSource)
        }
        {config.file.tracks.map(this.renderTrack)}
      </Element>
    )
  }
}

export default createSinglePlayer(FilePlayer)
<|endoftext|>