<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
   * @param {Config} [config] Config data to be used throughout all API
   * interaction
   */
  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}).then(products => {
   *   console.log(products); // An array of products in collection 123
   * });
   * ```
   * @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.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
   * @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.myShopifyDomain}.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.myShopifyDomain}.recent-cart`;

        this.create('references', refAttrs);

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

export default ShopClient;
<commit_msg>Adding handle and updated_at_min to the docs (#146)<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
   * @param {Config} [config] Config data to be used throughout all API
   * interaction
   */
  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}).then(products => {
   *   console.log(products); // An array of products in collection 123
   * });
   * ```
   * @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.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.myShopifyDomain}.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.myShopifyDomain}.recent-cart`;

        this.create('references', refAttrs);

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

export default ShopClient;
<|endoftext|>


<commit_before>import styles, {COLORS} from './styles';

import Component from 'react-pure-render/component';
import React, {
  BackAndroid,
  Navigator,
  PropTypes,
} from 'react-native';

import {
  Actions,
  Route,
  Router,
  Schema,
} from 'react-native-router-flux';

import Agenda from '../log/agenda/Page.react';
import All from '../log/all/Page.react';
import Drawer from './Drawer.react';
import Header from './Header.react';
import Help from '../help/Page.react';
import LogEntry from '../log/entry/Page.react';
import LogEntryHeader from '../log/entry/Header.react';
import Medication from '../medication/Page.react';
import Month from '../log/month/Page.react';
import Settings from '../settings/Page.react';
import Support from '../support/Page.react';
import Week from '../log/week/Page.react';

const components = {
  Agenda,
  All,
  Drawer,
  Header,
  Help,
  Medication,
  LogEntry,
  LogEntryHeader,
  Month,
  Router,
  Settings,
  Support,
  Week,
};

import {
  compose,
  map,
} from 'ramda';

import appSelector from '../../common/app/selector';
import mapDispatchToProps from '../../common/app/mapDispatchToProps';
import mapStateToProps from '../../common/app/mapStateToProps';
import {connect} from 'react-redux';

// connect selectors, state, and action creatores to component
const connectComponent = compose(
  connect(mapStateToProps, mapDispatchToProps)
);
const Connected = map(connectComponent, components);

class App extends Component {

  static propTypes = {
    actions: PropTypes.object.isRequired,
  };

  componentWillMount() {
    // Navigate back and exit if we have nowhere further to navigate to
    BackAndroid.addEventListener('hardwareBackPress', Actions.pop);
  }

  componentDidMount() {
    // dispatch action to set the initial route
    this.props.actions.ui.setRoute();
  }

  render() {
    const {actions} = this.props;

    return (
      <Connected.Router
        hideNavBar
        style={styles.container}
      >

        <Schema
          name="default"
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
        />

        <Schema
          headerColor={COLORS.PRIMARY}
          leftIcon="menu"
          leftIconPress={actions.ui.openDrawer}
          name="primary"
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
          type="replace"
        />

        <Schema
          header={Connected.Header}
          headerColor={COLORS.TERTIARY}
          hideNavBar
          leftIcon="arrow-back"
          leftIconPress={Actions.pop}
          name="secondary"
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
          wrapRouter
        />

        <Schema
          header={Connected.Header}
          headerColor={COLORS.TERTIARY}
          hideNavBar
          leftIcon="close"
          leftIconPress={Actions.pop}
          name="modal"
          sceneConfig={Navigator.SceneConfigs.FloatFromBottomAndroid}
          wrapRouter
        />

        <Route
          name="main"
          initial
          schema="primary"
        >
          <Connected.Drawer>
            <Connected.Router
              hideNavBar
              header={Connected.Header}
            >

              <Route
                name="logAgenda"
                component={Connected.Agenda}
                initial
                schema="primary"
                title="{date}"
              />

              <Route
                name="logWeek"
                component={Connected.Week}
                schema="primary"
                title="{date}"
              />

              <Route
                name="logMonth"
                component={Connected.Month}
                schema="primary"
                title="{date}"
              />

              <Route
                name="logAll"
                component={Connected.All}
                schema="primary"
                title="{date}"
              />

              <Route
                name="medication"
                component={Connected.Medication}
                schema="primary"
                title="Medication"
              />

              <Route
                name="support"
                component={Connected.Support}
                schema="primary"
                title="Support"
              />

            </Connected.Router>
          </Connected.Drawer>
        </Route>

        <Route
          name="settings"
          component={Connected.Settings}
          schema="secondary"
          title="Settings"
        />

        <Route
          name="help"
          component={Connected.Help}
          schema="secondary"
          title="Help & Feedback"
        />

        <Route
          name="logEntry"
          component={Connected.LogEntry}
          header={Connected.LogEntryHeader}
          schema="modal"
        />

      </Connected.Router>
    );
  }

}

export default connectComponent(App);
<commit_msg>cleanup app<commit_after>import styles, {COLORS} from './styles';

import Component from 'react-pure-render/component';
import React, {
  BackAndroid,
  Navigator,
  PropTypes,
} from 'react-native';

import {
  Actions,
  Route,
  Router,
  Schema,
} from 'react-native-router-flux';

import Agenda from '../log/agenda/Page.react';
import All from '../log/all/Page.react';
import Drawer from './Drawer.react';
import Header from './Header.react';
import Help from '../help/Page.react';
import LogEntry from '../log/entry/Page.react';
import LogEntryHeader from '../log/entry/Header.react';
import Medication from '../medication/Page.react';
import Month from '../log/month/Page.react';
import Settings from '../settings/Page.react';
import Support from '../support/Page.react';
import Week from '../log/week/Page.react';

const components = {
  Agenda,
  All,
  Drawer,
  Header,
  Help,
  Medication,
  LogEntry,
  LogEntryHeader,
  Month,
  Router,
  Settings,
  Support,
  Week,
};

import mapDispatchToProps from '../../common/app/mapDispatchToProps';
import mapStateToProps from '../../common/app/mapStateToProps';
import {connect} from 'react-redux';

import {map} from 'ramda';

const connectComponent = connect(mapStateToProps, mapDispatchToProps);
const Connected = map(connectComponent, components);

class App extends Component {

  static propTypes = {
    actions: PropTypes.object.isRequired,
  };

  componentWillMount() {
    // Navigate back and exit if we have nowhere further to navigate to
    BackAndroid.addEventListener('hardwareBackPress', Actions.pop);
  }

  componentDidMount() {
    // dispatch action to set the initial route
    this.props.actions.ui.setRoute();
  }

  render() {
    const {actions} = this.props;

    return (
      <Connected.Router
        hideNavBar
        style={styles.container}
      >

        <Schema
          name="default"
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
        />

        <Schema
          headerColor={COLORS.PRIMARY}
          leftIcon="menu"
          leftIconPress={actions.ui.openDrawer}
          name="primary"
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
          type="replace"
        />

        <Schema
          header={Connected.Header}
          headerColor={COLORS.TERTIARY}
          hideNavBar
          leftIcon="arrow-back"
          leftIconPress={Actions.pop}
          name="secondary"
          sceneConfig={Navigator.SceneConfigs.FadeAndroid}
          wrapRouter
        />

        <Schema
          header={Connected.Header}
          headerColor={COLORS.TERTIARY}
          hideNavBar
          leftIcon="close"
          leftIconPress={Actions.pop}
          name="modal"
          sceneConfig={Navigator.SceneConfigs.FloatFromBottomAndroid}
          wrapRouter
        />

        <Route
          name="main"
          initial
          schema="primary"
        >
          <Connected.Drawer>
            <Connected.Router
              hideNavBar
              header={Connected.Header}
            >

              <Route
                name="logAgenda"
                component={Connected.Agenda}
                initial
                schema="primary"
                title="{date}"
              />

              <Route
                name="logWeek"
                component={Connected.Week}
                schema="primary"
                title="{date}"
              />

              <Route
                name="logMonth"
                component={Connected.Month}
                schema="primary"
                title="{date}"
              />

              <Route
                name="logAll"
                component={Connected.All}
                schema="primary"
                title="{date}"
              />

              <Route
                name="medication"
                component={Connected.Medication}
                schema="primary"
                title="Medication"
              />

              <Route
                name="support"
                component={Connected.Support}
                schema="primary"
                title="Support"
              />

            </Connected.Router>
          </Connected.Drawer>
        </Route>

        <Route
          name="settings"
          component={Connected.Settings}
          schema="secondary"
          title="Settings"
        />

        <Route
          name="help"
          component={Connected.Help}
          schema="secondary"
          title="Help & Feedback"
        />

        <Route
          name="logEntry"
          component={Connected.LogEntry}
          header={Connected.LogEntryHeader}
          schema="modal"
        />

      </Connected.Router>
    );
  }

}

export default connectComponent(App);
<|endoftext|>


<commit_before>"use strict";

var newrelic = require("newrelic");

const GOOGLE_PLUS_DAILY_REQUESTS_LIMIT = 50000;

/**
 * The amount of distinct feeds expected to be requested every day.
 *
 * An approximation can be obtained by calculating the amount of unique feeds
 * requested on the previous day:
 *
 *   $ cat /var/log/nginx/plusfeed-access.log.1 | awk '{ print $7 }' | sort -u | wc -l
 */
const EXPECTED_DAILY_UNIQUE_REQUESTED_FEEDS = 21164;

var Items = (module.exports = function({ googlePlus, repository }) {
  this._googlePlus = googlePlus;
  this._repository = repository;
});

Items.prototype.get = function(userId) {
  userId = userId.toLowerCase(); // Normalize it
  return this._repository.get(userId).then(cache => {
    cache =
      cache &&
      Object.assign({}, cache, {
        expired: cache.date < this._getExpirationDate()
      });
    this._logUserCacheStatus(userId, cache);
    if (cache && !cache.expired) return cache.items;
    return this._googlePlus
      .getUserItems(userId)
      .then(userItems =>
        this._repository.set(userId, userItems).then(() => userItems)
      )
      .catch(error => {
        // Try to use the cached items (even if it has expired) before failing
        if (!cache) throw error;
        // eslint-disable-next-line no-console
        console.error(error);
        return cache.items;
      });
  });
};

Items.prototype._logUserCacheStatus = function(userId, cache) {
  var status = { ID: userId, Status: this._getCacheStatus(cache) };
  // eslint-disable-next-line no-console
  console.log(JSON.stringify(status));
  newrelic.recordCustomEvent("User Feed Cache", status);
};

Items.prototype._getCacheStatus = function(cache) {
  if (!cache) return "Missing";
  if (cache.expired) return "Expired";
  return "Hit";
};

Items.prototype._getExpirationDate = function() {
  return new Date(Date.now() - this._getFeedCacheAge());
};

Items.prototype._getFeedCacheAge = function(
  { dailyRequestsLimit, maxDailyFeeds } = {}
) {
  dailyRequestsLimit = dailyRequestsLimit || GOOGLE_PLUS_DAILY_REQUESTS_LIMIT;
  maxDailyFeeds = maxDailyFeeds || EXPECTED_DAILY_UNIQUE_REQUESTED_FEEDS;
  const dailyRequestsLimitPerFeed = dailyRequestsLimit / maxDailyFeeds;
  return 1 /* day */ * 24 * 60 * 60 * 1000 / dailyRequestsLimitPerFeed;
};
<commit_msg>CachedFeeds function -> class<commit_after>"use strict";

var newrelic = require("newrelic");

const GOOGLE_PLUS_DAILY_REQUESTS_LIMIT = 50000;

/**
 * The amount of distinct feeds expected to be requested every day.
 *
 * An approximation can be obtained by calculating the amount of unique feeds
 * requested on the previous day:
 *
 *   $ cat /var/log/nginx/plusfeed-access.log.1 | awk '{ print $7 }' | sort -u | wc -l
 */
const EXPECTED_DAILY_UNIQUE_REQUESTED_FEEDS = 21164;

module.exports = class Items {
  constructor({ googlePlus, repository }) {
    this._googlePlus = googlePlus;
    this._repository = repository;
  }

  get(userId) {
    userId = userId.toLowerCase(); // Normalize it
    return this._repository.get(userId).then(cache => {
      cache =
        cache &&
        Object.assign({}, cache, {
          expired: cache.date < this._getExpirationDate()
        });
      this._logUserCacheStatus(userId, cache);
      if (cache && !cache.expired) return cache.items;
      return this._googlePlus
        .getUserItems(userId)
        .then(userItems =>
          this._repository.set(userId, userItems).then(() => userItems)
        )
        .catch(error => {
          // Try to use the cached items (even if it has expired) before failing
          if (!cache) throw error;
          // eslint-disable-next-line no-console
          console.error(error);
          return cache.items;
        });
    });
  }

  _logUserCacheStatus(userId, cache) {
    var status = { ID: userId, Status: this._getCacheStatus(cache) };
    // eslint-disable-next-line no-console
    console.log(JSON.stringify(status));
    newrelic.recordCustomEvent("User Feed Cache", status);
  }

  _getCacheStatus(cache) {
    if (!cache) return "Missing";
    if (cache.expired) return "Expired";
    return "Hit";
  }

  _getExpirationDate() {
    return new Date(Date.now() - this._getFeedCacheAge());
  }

  _getFeedCacheAge({ dailyRequestsLimit, maxDailyFeeds } = {}) {
    dailyRequestsLimit = dailyRequestsLimit || GOOGLE_PLUS_DAILY_REQUESTS_LIMIT;
    maxDailyFeeds = maxDailyFeeds || EXPECTED_DAILY_UNIQUE_REQUESTED_FEEDS;
    const dailyRequestsLimitPerFeed = dailyRequestsLimit / maxDailyFeeds;
    return 1 /* day */ * 24 * 60 * 60 * 1000 / dailyRequestsLimitPerFeed;
  }
};
<|endoftext|>


<commit_before>import {Inspector, Runtime} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";

if (window.self !== window.top) {
    const observableNotebookJS = JSON.parse(document.getElementById("observableNotebookJS").textContent);
    const selectedCells = JSON.parse(document.getElementById("observableCells").textContent);
    const evaluations = JSON.parse(document.getElementById("evaluations").textContent);

    function setErrorMessage(msg) {
        const alert = document.getElementById("observableAlert");
        alert.textContent = msg
        alert.classList.remove("d-none");

        const cells = document.getElementsByClassName("observableCell");
        while (cells.length > 0) cells[0].remove();
    }

    import(observableNotebookJS).then(
        module => {
            const runtime = new Runtime()
            let main

            if (selectedCells.length === 1 && selectedCells.every((c) => c === "*")) {
                const cell = document.querySelector("#observableCell");
                cell.textContent = "";
                main = runtime.module(module.default, Inspector.into(cell));
            } else {
                main = runtime.module(module.default, (name) => {
                    let selected = selectedCells.indexOf(name);
                    if (selected > -1) {
                        const id = selectedCells[selected].replace(/[\s*]/g, "");  // remove spaces and * from cell names
                        let cell = document.querySelector("#observableCell" + id);
                        cell.classList.remove("text-center");
                        return new Inspector(cell);
                    }
                });
            }

            try {
                main.redefine("parse_results", evaluations);
            } catch (error) {
                setErrorMessage("The variable 'parse_results' has not been defined in the provided notebook.");
            }
        }
    ).catch(() => {
            setErrorMessage("Could not load notebook.");
        }
    );
}
<commit_msg>Update notebook API<commit_after>import {Inspector, Runtime} from "https://cdn.jsdelivr.net/npm/@observablehq/runtime@4/dist/runtime.js";

if (window.self !== window.top) {
    const observableNotebookJS = JSON.parse(document.getElementById("observableNotebookJS").textContent);
    const selectedCells = JSON.parse(document.getElementById("observableCells").textContent);
    const evaluations = JSON.parse(document.getElementById("evaluations").textContent);

    function setErrorMessage(msg) {
        const alert = document.getElementById("observableAlert");
        alert.textContent = msg
        alert.classList.remove("d-none");

        const cells = document.getElementsByClassName("observableCell");
        while (cells.length > 0) cells[0].remove();
    }

    import(observableNotebookJS).then(
        module => {
            const runtime = new Runtime()
            let main

            if (selectedCells.length === 1 && selectedCells.every((c) => c === "*")) {
                const cell = document.querySelector("#observableCell");
                cell.textContent = "";
                main = runtime.module(module.default, Inspector.into(cell));
            } else {
                main = runtime.module(module.default, (name) => {
                    let selected = selectedCells.indexOf(name);
                    if (selected > -1) {
                        const id = selectedCells[selected].replace(/[\s*]/g, "");  // remove spaces and * from cell names
                        let cell = document.querySelector("#observableCell" + id);
                        cell.classList.remove("text-center");
                        return new Inspector(cell);
                    }
                });
            }

            try {
                main.redefine("evaluations", evaluations);
            } catch (error) {
                setErrorMessage("The variable 'evaluations' has not been defined in the provided notebook.");
            }
        }
    ).catch(() => {
            setErrorMessage("Could not load notebook.");
        }
    );
}
<|endoftext|>


<commit_before>import Promise from 'bluebird';
import R from 'ramda';
import _request from 'request';
import nconf from 'nconf';

import { getNSFWChannel } from '../../redis';

const apikey = nconf.get('DERPIBOORU_KEY');
const request = Promise.promisify(_request);

// Setup and makes request to e621 API
function _makeRequest(options) {
  let default_options = {
    json: true,
    headers: {
      'User-Agent' : 'FurBot/1.0 (Phun @ e621)'
    }
  };

  if (options.qs) options.qs = R.merge(default_options.qs, options.qs);
  return request(R.merge(default_options, options, true))
    .tap(res => {
      if (res.statusCode === 521) throw new ApiDown();
    })
    .then(R.prop('body'))
    .tap(body => {
      //if (body.error) throw new Error(body.error);
    });
}

function tags(client, evt, suffix) {
  const channel_id = evt.message.channel_id;
  return getNSFWChannel(channel_id).then(value => {
    if (evt.message.channel.isPrivate) value = true;
    if (value === 'false') return Promise.resolve(`\u26A0  |  Please use this command in a NSFW channel. Admins can enable NSFW with the command \`!setnsfw\``);
    let query;
    let array = suffix.split(' ');
    let lastTag = Number.parseInt(array[array.length-1]);
    let count = 1;
    if (suffix && Number.isInteger(lastTag)) {
      count = lastTag;
      if (count > 5) count = 5;
      if (count < 0) count = 1;
      let removeCount = R.slice(0, -1, array);
      let cleanSuffix = R.join(' ', removeCount);
      query = cleanSuffix.toLowerCase().replace('tags ', '');
    } else {
      query = suffix.toLowerCase().replace('tags ', '');
    }
    query = query.replace(' ', ',');
    query = query.replace('_', '+');

    if (query == '') return Promise.resolve(`\u26A0  |  No tags were supplied`);
    let checkLength = query.split(' ');
    if (checkLength.length > 6) return Promise.resolve(`\u26A0  |  You can only search up to 6 tags`);

    const options = {
      url: `https://derpibooru.org/search.json?q=${query}`,
      qs: {
        key: `${apikey}`
      }
    };

    return Promise.resolve(R.repeat('tags', count))
    .map(() => {
      return _makeRequest(options)
      .then(body => {
        if (!body || typeof body === 'undefined' || body.search.length == 0) return Promise.resolve(`\u26A0  |  No results for: \`${query}\``);
        // Do some math
        let randomid = Math.floor(Math.random() * body.search.length);
        // Grab the data
        let id = body.search[randomid].id;
        let file = body.search[randomid].image;
        let fileurl = `https:${file}`;
        let height = body.search[randomid].height;
        let width = body.search[randomid].width;
        let score = body.search[randomid].score;
        let embed = {
          color: 4035280,
          author: {
            name: 'Searched: ' + suffix,
            icon_url: evt.message.author.avatarURL
          },
          url: 'https://derpibooru.org/' + id,
          description: `**Score:** ${score} | **Resolution: ** ${width} x ${height}`,
          image: { url: fileurl }
        }
        return evt.message.channel.sendMessage('', false, embed);
      })
    })
  })
}

export default {
  dp: (client, evt, suffix, lang) => {
    const command = suffix.toLowerCase().split(' ')[0];
    /*if (command === 'latest') return latest(client, evt, suffix);*/
    if (command === 'tags') return tags(client, evt, suffix);
    if (command !== 'tags' || command !== 'latest') return tags(client, evt, suffix);
  }
};

export const help = {
  dp: {parameters: ['tags', 'number']}
};
<commit_msg>Make derpibooru search all pages<commit_after>import Promise from 'bluebird';
import R from 'ramda';
import _request from 'request';
import nconf from 'nconf';

import { getNSFWChannel } from '../../redis';

const apikey = nconf.get('DERPIBOORU_KEY');
const request = Promise.promisify(_request);

// Setup and makes request to e621 API
function _makeRequest(options) {
  let default_options = {
    json: true,
    headers: {
      'User-Agent' : 'FurBot/1.0 (Phun @ e621)'
    }
  };

  if (options.qs) options.qs = R.merge(default_options.qs, options.qs);
  return request(R.merge(default_options, options, true))
    .tap(res => {
      if (res.statusCode === 521) throw new ApiDown();
    })
    .then(R.prop('body'))
    .tap(body => {
      //if (body.error) throw new Error(body.error);
    });
}

function tags(client, evt, suffix) {
  const channel_id = evt.message.channel_id;
  return getNSFWChannel(channel_id).then(value => {
    if (evt.message.channel.isPrivate) value = true;
    if (value === 'false') return Promise.resolve(`\u26A0  |  Please use this command in a NSFW channel. Admins can enable NSFW with the command \`!setnsfw\``);
    let query;
    let array = suffix.split(' ');
    let lastTag = Number.parseInt(array[array.length-1]);
    let count = 1;
    if (suffix && Number.isInteger(lastTag)) {
      count = lastTag;
      if (count > 5) count = 5;
      if (count < 0) count = 1;
      let removeCount = R.slice(0, -1, array);
      let cleanSuffix = R.join(' ', removeCount);
      query = cleanSuffix.toLowerCase().replace('tags ', '');
    } else {
      query = suffix.toLowerCase().replace('tags ', '');
    }
    query = query.replace(/ /gi, ',');
    query = query.replace(/_/gi, '+');

    if (query == '') return Promise.resolve(`\u26A0  |  No tags were supplied`);
    let checkLength = query.split(' ');
    if (checkLength.length > 6) return Promise.resolve(`\u26A0  |  You can only search up to 6 tags`);

    const options = {
      url: `https://derpibooru.org/search.json?q=${query}`,
      qs: {
        key: `${apikey}`
      }
    };

    return Promise.resolve(R.repeat('tags', count))
    .map(() => {
      return _makeRequest(options)
      .then(count => {
        let pages = Math.ceil(count.total / count.search.length);
        return _makeRequest({
          url: `https://derpibooru.org/search.json?q=${query}`,
          qs: {
            key: `${apikey}`,
            page: `${pages}`
          }
        })
      })
      .then(body => {
        if (!body || typeof body === 'undefined' || body.search.length == 0) return Promise.resolve(`\u26A0  |  No results for: \`${query}\``);
        // Do some math
        let randomid = Math.floor(Math.random() * body.search.length);
        // Grab the data
        let id = body.search[randomid].id;
        let file = body.search[randomid].image;
        let fileurl = `https:${file}`;
        let height = body.search[randomid].height;
        let width = body.search[randomid].width;
        let score = body.search[randomid].score;
        let embed = {
          color: 4035280,
          author: {
            name: 'Searched: ' + suffix,
            icon_url: evt.message.author.avatarURL
          },
          url: 'https://derpibooru.org/' + id,
          description: `**Score:** ${score} | **Resolution: ** ${width} x ${height}`,
          image: { url: fileurl }
        }
        return evt.message.channel.sendMessage('', false, embed);
      })
    })
  })
}

export default {
  dp: (client, evt, suffix, lang) => {
    const command = suffix.toLowerCase().split(' ')[0];
    /*if (command === 'latest') return latest(client, evt, suffix);*/
    if (command === 'tags') return tags(client, evt, suffix);
    if (command !== 'tags' || command !== 'latest') return tags(client, evt, suffix);
  }
};

export const help = {
  dp: {parameters: ['tags', 'number']}
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * https://raw.github.com/facebook/regenerator/master/LICENSE file. An
 * additional grant of patent rights can be found in the PATENTS file in
 * the same directory.
 */

(function(
  // Reliable reference to the global object (i.e. window in browsers).
  global,

  // Dummy constructor that we use as the .constructor property for
  // functions that return Generator objects.
  GeneratorFunction
) {
  var hasOwn = Object.prototype.hasOwnProperty;

  if (global.wrapGenerator) {
    return;
  }

  function wrapGenerator(innerFn, self) {
    return new Generator(innerFn, self || null);
  }

  global.wrapGenerator = wrapGenerator;
  if (typeof exports !== "undefined") {
    exports.wrapGenerator = wrapGenerator;
  }

  var GenStateSuspendedStart = "suspendedStart";
  var GenStateSuspendedYield = "suspendedYield";
  var GenStateExecuting = "executing";
  var GenStateCompleted = "completed";

  // Returning this object from the innerFn has the same effect as
  // breaking out of the dispatch switch statement.
  var ContinueSentinel = {};

  wrapGenerator.mark = function(genFun) {
    genFun.constructor = GeneratorFunction;
    return genFun;
  };

  // Ensure isGeneratorFunction works when Function#name not supported.
  if (GeneratorFunction.name !== "GeneratorFunction") {
    GeneratorFunction.name = "GeneratorFunction";
  }

  wrapGenerator.isGeneratorFunction = function(genFun) {
    var ctor = genFun && genFun.constructor;
    return ctor ? GeneratorFunction.name === ctor.name : false;
  };

  function Generator(innerFn, self) {
    var generator = this;
    var context = new Context();
    var state = GenStateSuspendedStart;

    function invoke(method, arg) {
      if (state === GenStateExecuting) {
        throw new Error("Generator is already running");
      }

      if (state === GenStateCompleted) {
        throw new Error("Generator has already finished");
      }

      while (true) {
        var delegate = context.delegate;
        if (delegate) {
          try {
            var info = delegate.generator[method](arg);

            // Delegate generator ran and handled its own exceptions so
            // regardless of what the method was, we continue as if it is
            // "next" with an undefined arg.
            method = "next";
            arg = void 0;

          } catch (uncaught) {
            context.delegate = null;

            // Like returning generator.throw(uncaught), but without the
            // overhead of an extra function call.
            method = "throw";
            arg = uncaught;

            continue;
          }

          if (info.done) {
            context[delegate.resultName] = info.value;
            context.next = delegate.nextLoc;
          } else {
            state = GenStateSuspendedYield;
            return info;
          }

          context.delegate = null;
        }

        if (method === "next") {
          if (state === GenStateSuspendedStart &&
              typeof arg !== "undefined") {
            // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
            throw new TypeError(
              "attempt to send " + JSON.stringify(arg) + " to newborn generator"
            );
          }

          if (state === GenStateSuspendedYield) {
            context.sent = arg;
          } else {
            delete context.sent;
          }

        } else if (method === "throw") {
          if (state === GenStateSuspendedStart) {
            state = GenStateCompleted;
            throw arg;
          }

          if (context.dispatchException(arg)) {
            // If the dispatched exception was caught by a catch block,
            // then let that catch block handle the exception normally.
            method = "next";
            arg = void 0;
          }
        }

        state = GenStateExecuting;

        try {
          var value = innerFn.call(self, context);

          // If an exception is thrown from innerFn, we leave state ===
          // GenStateExecuting and loop back for another invocation.
          state = context.done
            ? GenStateCompleted
            : GenStateSuspendedYield;

          var info = {
            value: value,
            done: context.done
          };

          if (value === ContinueSentinel) {
            if (context.delegate && method === "next") {
              // Deliberately forget the last sent value so that we don't
              // accidentally pass it on to the delegate.
              arg = void 0;
            }
          } else {
            return info;
          }

        } catch (thrown) {
          if (method === "next") {
            context.dispatchException(thrown);
          } else {
            arg = thrown;
          }
        }
      }
    }

    generator.next = invoke.bind(generator, "next");
    generator.throw = invoke.bind(generator, "throw");
  }

  Generator.prototype.toString = function() {
    return "[object Generator]";
  };

  function Context() {
    this.reset();
  }

  Context.prototype = {
    constructor: Context,

    reset: function() {
      this.next = 0;
      this.sent = void 0;
      this.tryStack = [];
      this.done = false;
      this.delegate = null;

      // Pre-initialize at least 20 temporary variables to enable hidden
      // class optimizations for simple generators.
      for (var tempIndex = 0, tempName;
           hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20;
           ++tempIndex) {
        this[tempName] = null;
      }
    },

    stop: function() {
      this.done = true;

      if (hasOwn.call(this, "thrown")) {
        var thrown = this.thrown;
        delete this.thrown;
        throw thrown;
      }

      return this.rval;
    },

    keys: function(object) {
      return Object.keys(object).reverse();
    },

    pushTry: function(catchLoc, finallyLoc, finallyTempVar) {
      if (finallyLoc) {
        this.tryStack.push({
          finallyLoc: finallyLoc,
          finallyTempVar: finallyTempVar
        });
      }

      if (catchLoc) {
        this.tryStack.push({
          catchLoc: catchLoc
        });
      }
    },

    popCatch: function(catchLoc) {
      var lastIndex = this.tryStack.length - 1;
      var entry = this.tryStack[lastIndex];

      if (entry && entry.catchLoc === catchLoc) {
        this.tryStack.length = lastIndex;
      }
    },

    popFinally: function(finallyLoc) {
      var lastIndex = this.tryStack.length - 1;
      var entry = this.tryStack[lastIndex];

      if (!entry || !hasOwn.call(entry, "finallyLoc")) {
        entry = this.tryStack[--lastIndex];
      }

      if (entry && entry.finallyLoc === finallyLoc) {
        this.tryStack.length = lastIndex;
      }
    },

    dispatchException: function(exception) {
      var finallyEntries = [];
      var caught = false;

      if (this.done) {
        throw exception;
      }

      // Dispatch the exception to the "end" location by default.
      this.thrown = exception;
      this.next = "end";

      for (var i = this.tryStack.length - 1; i >= 0; --i) {
        var entry = this.tryStack[i];
        if (entry.catchLoc) {
          this.next = entry.catchLoc;
          caught = true;
          break;
        } else if (entry.finallyLoc) {
          finallyEntries.push(entry);
        }
      }

      while ((entry = finallyEntries.pop())) {
        this[entry.finallyTempVar] = this.next;
        this.next = entry.finallyLoc;
      }

      return caught;
    },

    delegateYield: function(generator, resultName, nextLoc) {
      this.delegate = {
        generator: generator,
        resultName: resultName,
        nextLoc: nextLoc
      };

      return ContinueSentinel;
    }
  };
}).apply(this, Function("return [this, function GeneratorFunction(){}]")());
<commit_msg>Copy latest runtime/dev.js from the master branch.<commit_after>/**
 * Copyright (c) 2013, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * https://raw.github.com/facebook/regenerator/master/LICENSE file. An
 * additional grant of patent rights can be found in the PATENTS file in
 * the same directory.
 */

(function(
  // Reliable reference to the global object (i.e. window in browsers).
  global,

  // Dummy constructor that we use as the .constructor property for
  // functions that return Generator objects.
  GeneratorFunction
) {
  var hasOwn = Object.prototype.hasOwnProperty;

  if (global.wrapGenerator) {
    return;
  }

  function wrapGenerator(innerFn, self, tryList) {
    return new Generator(innerFn, self || null, tryList || []);
  }

  global.wrapGenerator = wrapGenerator;
  if (typeof exports !== "undefined") {
    exports.wrapGenerator = wrapGenerator;
  }

  var GenStateSuspendedStart = "suspendedStart";
  var GenStateSuspendedYield = "suspendedYield";
  var GenStateExecuting = "executing";
  var GenStateCompleted = "completed";

  // Returning this object from the innerFn has the same effect as
  // breaking out of the dispatch switch statement.
  var ContinueSentinel = {};

  wrapGenerator.mark = function(genFun) {
    genFun.constructor = GeneratorFunction;
    return genFun;
  };

  // Ensure isGeneratorFunction works when Function#name not supported.
  if (GeneratorFunction.name !== "GeneratorFunction") {
    GeneratorFunction.name = "GeneratorFunction";
  }

  wrapGenerator.isGeneratorFunction = function(genFun) {
    var ctor = genFun && genFun.constructor;
    return ctor ? GeneratorFunction.name === ctor.name : false;
  };

  function Generator(innerFn, self, tryList) {
    var generator = this;
    var context = new Context(tryList);
    var state = GenStateSuspendedStart;

    function invoke(method, arg) {
      if (state === GenStateExecuting) {
        throw new Error("Generator is already running");
      }

      if (state === GenStateCompleted) {
        throw new Error("Generator has already finished");
      }

      while (true) {
        var delegate = context.delegate;
        if (delegate) {
          try {
            var info = delegate.generator[method](arg);

            // Delegate generator ran and handled its own exceptions so
            // regardless of what the method was, we continue as if it is
            // "next" with an undefined arg.
            method = "next";
            arg = void 0;

          } catch (uncaught) {
            context.delegate = null;

            // Like returning generator.throw(uncaught), but without the
            // overhead of an extra function call.
            method = "throw";
            arg = uncaught;

            continue;
          }

          if (info.done) {
            context[delegate.resultName] = info.value;
            context.next = delegate.nextLoc;
          } else {
            state = GenStateSuspendedYield;
            return info;
          }

          context.delegate = null;
        }

        if (method === "next") {
          if (state === GenStateSuspendedStart &&
              typeof arg !== "undefined") {
            // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
            throw new TypeError(
              "attempt to send " + JSON.stringify(arg) + " to newborn generator"
            );
          }

          if (state === GenStateSuspendedYield) {
            context.sent = arg;
          } else {
            delete context.sent;
          }

        } else if (method === "throw") {
          if (state === GenStateSuspendedStart) {
            state = GenStateCompleted;
            throw arg;
          }

          if (context.dispatchException(arg)) {
            // If the dispatched exception was caught by a catch block,
            // then let that catch block handle the exception normally.
            method = "next";
            arg = void 0;
          }
        }

        state = GenStateExecuting;

        try {
          var value = innerFn.call(self, context);

          // If an exception is thrown from innerFn, we leave state ===
          // GenStateExecuting and loop back for another invocation.
          state = context.done
            ? GenStateCompleted
            : GenStateSuspendedYield;

          var info = {
            value: value,
            done: context.done
          };

          if (value === ContinueSentinel) {
            if (context.delegate && method === "next") {
              // Deliberately forget the last sent value so that we don't
              // accidentally pass it on to the delegate.
              arg = void 0;
            }
          } else {
            return info;
          }

        } catch (thrown) {
          if (method === "next") {
            context.dispatchException(thrown);
          } else {
            arg = thrown;
          }
        }
      }
    }

    generator.next = invoke.bind(generator, "next");
    generator.throw = invoke.bind(generator, "throw");
  }

  Generator.prototype.toString = function() {
    return "[object Generator]";
  };

  function tryTripleToObj(triple) {
    var entry = { tryLoc: triple[0] };

    if (triple[1]) {
      entry.catchLoc = triple[1];
    }

    var fin = triple[2];
    if (fin) {
      entry.finallyLoc = fin[0];
      entry.finallyTempVar = fin[1];
    }

    this.tryEntries.push(entry);
  }

  function Context(tryList) {
    this.reset();

    this.tryEntries = [{
      // This implicit root object (effectively a try statement without a
      // real catch or a finally block) gives us a place to store values
      // thrown when there is no enclosing try statement.
      tryLoc: 0,
      catchLoc: "end"
    }];

    tryList.forEach(tryTripleToObj, this);
  }

  Context.prototype = {
    constructor: Context,

    reset: function() {
      this.prev = 0;
      this.next = 0;
      this.sent = void 0;
      this.done = false;
      this.delegate = null;

      if (this.tryEntries) {
        this.tryEntries.forEach(function(entry) {
          delete entry.thrown;
        });
      }

      // Pre-initialize at least 20 temporary variables to enable hidden
      // class optimizations for simple generators.
      for (var tempIndex = 0, tempName;
           hasOwn.call(this, tempName = "t" + tempIndex) || tempIndex < 20;
           ++tempIndex) {
        this[tempName] = null;
      }
    },

    stop: function() {
      this.done = true;

      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
        var entry = this.tryEntries[i];
        if (hasOwn.call(entry, "thrown")) {
          throw entry.thrown;
        }
      }

      return this.rval;
    },

    keys: function(object) {
      return Object.keys(object).reverse();
    },

    dispatchException: function(exception) {
      var finallyEntries = [];
      var caught = false;

      if (this.done) {
        throw exception;
      }

      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
        var entry = this.tryEntries[i];

        if (entry.catchLoc) {
          if (entry.catchLoc === "end") {
            // If the lookup reaches the implicit root try statement, skip
            // on down to the if (!caught) block below.
            break;
          }

          if (entry.tryLoc <= this.prev &&
              this.prev < entry.catchLoc) {
            entry.thrown = exception;
            this.next = entry.catchLoc;
            caught = true;
            break;
          }
        }

        if (entry.finallyLoc &&
            entry.tryLoc <= this.prev &&
            this.prev < entry.finallyLoc) {
          finallyEntries.push(entry);
        }
      }

      if (!caught) {
        // Dispatch uncaught exceptions to the implicit root object.
        this.tryEntries[0].thrown = exception;
        this.next = "end";
      }

      while ((entry = finallyEntries.pop())) {
        this[entry.finallyTempVar] = this.next;
        this.next = entry.finallyLoc;
      }

      return caught;
    },

    "catch": function(catchLoc) {
      for (var i = this.tryEntries.length - 1; i >= 0; --i) {
        var entry = this.tryEntries[i];
        if (entry.catchLoc === catchLoc) {
          var thrown = entry.thrown;
          delete entry.thrown;
          return thrown;
        }
      }

      // The context.catch method must only be called with a location
      // argument that corresponds to a known catch block.
      throw new Error("illegal catch attempt");
    },

    delegateYield: function(generator, resultName, nextLoc) {
      this.delegate = {
        generator: generator,
        resultName: resultName,
        nextLoc: nextLoc
      };

      return ContinueSentinel;
    }
  };
}).apply(this, Function("return [this, function GeneratorFunction(){}]")());
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '../server/pages';
import { FormattedMessage, defineMessages } from 'react-intl';
import withIntl from '../lib/withIntl';
import { formatCurrency, capitalize } from '../lib/utils';
import { Badge } from 'react-bootstrap';
import { get, uniqBy } from 'lodash';
import styled from 'styled-components';
import {
  backgroundImage,
  bgColor,
  border,
  borderRadius,
  display,
  size,
  space,
  style,
} from 'styled-system';
import { Box, Flex } from 'grid-styled';
import Hide from './Hide';
import { P } from './Text';
import Caret from './Caret';
import StyledLink from './StyledLink';
import ListItem from './ListItem';
import Container from './Container';

const cursor = style({
  prop: 'cursor',
});

const Avatar = styled.div`
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  overflow: hidden;

  ${bgColor}
  ${backgroundImage}
  ${border}
  ${borderRadius}
  ${display}
  ${size}
  ${space}

  ${cursor}
`;

class TopBarProfileMenu extends React.Component {
  static propTypes = {
    LoggedInUser: PropTypes.object,
    intl: PropTypes.object.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = { showProfileMenu: false, loading: true };
    this.messages = defineMessages({
      'tooltip.balance': {
        id: 'profilemenu.memberships.tooltip.balance',
        defaultMessage: 'Balance {balance}',
      },
      'tooltip.pendingExpenses': {
        id: 'profilemenu.memberships.tooltip.pendingExpenses',
        defaultMessage: '{n} pending expenses',
      },
      'menu.transactions': {
        id: 'menu.transactions',
        defaultMessage: 'transactions',
      },
      'menu.applications': {
        id: 'menu.applications',
        defaultMessage: 'applications',
      },
    });
  }

  componentDidMount() {
    document.addEventListener('click', this.onClickOutside);
    if (typeof window !== 'undefined') {
      this.redirectAfterSignin = window.location.href.replace(
        /^https?:\/\/[^/]+/,
        '',
      );
      if (!window.localStorage.accessToken) {
        this.setState({ loading: false });
      }
    }
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.onClickOutside);
  }

  logout = () => {
    this.setState({ showProfileMenu: false, status: 'loggingout' });
    window.localStorage.removeItem('accessToken');
    window.localStorage.removeItem('LoggedInUser');
    window.location.reload();
  };

  onClickOutside = () => {
    this.setState({ showProfileMenu: false });
  };

  toggleProfileMenu = e => {
    this.setState({ showProfileMenu: !this.state.showProfileMenu });
    // don't propagate to onClickOutside
    e.nativeEvent.stopImmediatePropagation();
  };

  tooltip(membership) {
    const { intl } = this.props;
    const { collective } = membership;
    const balance = get(collective, 'stats.balance');
    let str = intl.formatMessage(this.messages['tooltip.balance'], {
      balance: formatCurrency(balance, collective.currency),
    });
    const pendingExpenses = get(collective, 'stats.expenses.pending');
    if (pendingExpenses > 0) {
      str += ` - ${intl.formatMessage(
        this.messages['tooltip.pendingExpenses'],
        { n: pendingExpenses },
      )}`;
    }
    return str;
  }

  renderProfileMenu() {
    const { LoggedInUser, intl } = this.props;
    const score = c => {
      switch (c.role) {
        case 'HOST':
          return 0;
        case 'ADMIN':
          return 1;
        case 'MEMBER':
          return 2;
        case 'BACKER':
          return 3;
      }
    };
    const collectives = uniqBy(
      [
        ...LoggedInUser.memberOf.filter(
          m => m.collective.type === 'COLLECTIVE',
        ),
      ],
      m => m.collective.id,
    ).sort((a, b) => {
      return `${score(a)}-${a.collective.slug}` >
        `${score(b)}-${b.collective.slug}`
        ? 1
        : -1;
    }); // order by role then az

    const orgs = uniqBy(
      [
        ...LoggedInUser.memberOf.filter(
          m => m.collective.type === 'ORGANIZATION',
        ),
      ],
      m => m.collective.id,
    ).sort((a, b) => {
      return `${score(a)}-${a.collective.slug}` >
        `${score(b)}-${b.collective.slug}`
        ? 1
        : -1;
    }); // order by role then az

    return (
      <Container
        bg="white"
        border="1px solid rgba(18,19,20,0.12)"
        borderRadius="12px"
        boxShadow="0 4px 8px 0 rgba(61,82,102,0.08)"
        minWidth="170px"
        maxWidth="300px"
        position="absolute"
        right={16}
        top={40}
        width="max-content"
        zIndex={3000}
      >
        <Container p={3}>
          <Flex alignItems="center">

              <FormattedMessage
                id="collective"
                defaultMessage="my collectives"
              />

            <Container height="0.1rem" bg="#E6E6E6" width={1} minWidth={50} />
            <Link route="/create" passHref>

            </Link>
          </Flex>
          <Box is="ul" p={0} my={2}>
            {collectives.map(membership => (
              <ListItem
                py={1}
                key={`LoggedInMenu-Collective-${get(
                  membership,
                  'collective.slug',
                )}`}
              >
                <Link route={`/${get(membership, 'collective.slug')}`} passHref>

                </Link>
                {get(membership, 'collective.stats.expenses.pending') > 0 && (
                  <Badge>
                    {get(membership, 'collective.stats.expenses.pending')}
                  </Badge>
                )}
              </ListItem>
            ))}
          </Box>

          <Flex alignItems="center" mt={3}>

              <FormattedMessage
                id="organization"
                defaultMessage="my organizations"
              />

            <Container height="0.1rem" bg="#E6E6E6" width={1} minWidth={50} />
            <Link route="/organizations/new" passHref>

            </Link>
          </Flex>
          <Box is="ul" p={0} my={2}>
            {orgs.map(membership => (
              <ListItem
                py={1}
                key={`LoggedInMenu-Collective-${get(
                  membership,
                  'collective.slug',
                )}`}
              >
                <Link route={`/${get(membership, 'collective.slug')}`} passHref>

                </Link>
                {get(membership, 'collective.stats.expenses.pending') > 0 && (
                  <Badge>
                    {get(membership, 'collective.stats.expenses.pending')}
                  </Badge>
                )}
              </ListItem>
            ))}
          </Box>
          {orgs.length === 0 && (
            <Box my={2}>

                No organizations yet

            </Box>
          )}
        </Container>
        <Container height="0.1rem" bg="#E6E6E6" width={1} />
        <Container p={[3]}>

            <FormattedMessage id="menu.myAccount" defaultMessage="My account" />

          <Box is="ul" p={0} my={2}>
            <ListItem py={1}>
              <Link
                route="collective"
                params={{ slug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>
              <Link
                route="subscriptions"
                params={{ collectiveSlug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>
              <Link
                route="transactions"
                params={{ collectiveSlug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>
              <Link
                route="applications"
                params={{ collectiveSlug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>

            </ListItem>
          </Box>
        </Container>
      </Container>
    );
  }

  renderLoggedInUser() {
    const { showProfileMenu } = this.state;
    const { LoggedInUser } = this.props;

    return (
      <Flex alignItems="center" onClick={this.toggleProfileMenu}>
        {LoggedInUser.image && (
          <Avatar
            backgroundColor="#FDFDFD"
            backgroundImage={`url(${LoggedInUser.image})`}
            borderRadius="100%"
            display="inline-block"
            size="2.6rem"
            mr={2}
            cursor="pointer"
          />
        )}
        <Hide xs sm>

            {LoggedInUser.username}

        </Hide>
        <Caret
          color="#46b0ed"
          display="inline-block"
          height="0.6rem"
          strokeWidth="0.2rem"
          width="1.4rem"
          cursor="pointer"
        />
        {showProfileMenu && this.renderProfileMenu()}
      </Flex>
    );
  }

  render() {
    const { loading } = this.state;
    const { LoggedInUser } = this.props;

    let status;
    if (this.state.status) {
      status = this.state.status;
    } else if (loading && typeof LoggedInUser === 'undefined') {
      status = 'loading';
    } else if (!LoggedInUser) {
      status = 'loggedout';
    } else {
      status = 'loggedin';
    }

    return (

        {status === 'loading' && (

            <FormattedMessage id="loading" defaultMessage="loading" />
            …

        )}

        {status === 'loggingout' && (

            <FormattedMessage id="loggingout" defaultMessage="logging out" />
            …

        )}

        {status === 'loggedout' && (
          <Link
            route="signin"
            params={{ next: this.redirectAfterSignin }}
            passHref
          >

          </Link>
        )}

        {status === 'loggedin' && this.renderLoggedInUser()}

    );
  }
}

export default withIntl(TopBarProfileMenu);
<commit_msg>Change "logout" to "Log out"<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import { Link } from '../server/pages';
import { FormattedMessage, defineMessages } from 'react-intl';
import withIntl from '../lib/withIntl';
import { formatCurrency, capitalize } from '../lib/utils';
import { Badge } from 'react-bootstrap';
import { get, uniqBy } from 'lodash';
import styled from 'styled-components';
import {
  backgroundImage,
  bgColor,
  border,
  borderRadius,
  display,
  size,
  space,
  style,
} from 'styled-system';
import { Box, Flex } from 'grid-styled';
import Hide from './Hide';
import { P } from './Text';
import Caret from './Caret';
import StyledLink from './StyledLink';
import ListItem from './ListItem';
import Container from './Container';

const cursor = style({
  prop: 'cursor',
});

const Avatar = styled.div`
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  overflow: hidden;

  ${bgColor}
  ${backgroundImage}
  ${border}
  ${borderRadius}
  ${display}
  ${size}
  ${space}

  ${cursor}
`;

class TopBarProfileMenu extends React.Component {
  static propTypes = {
    LoggedInUser: PropTypes.object,
    intl: PropTypes.object.isRequired,
  };

  constructor(props) {
    super(props);
    this.state = { showProfileMenu: false, loading: true };
    this.messages = defineMessages({
      'tooltip.balance': {
        id: 'profilemenu.memberships.tooltip.balance',
        defaultMessage: 'Balance {balance}',
      },
      'tooltip.pendingExpenses': {
        id: 'profilemenu.memberships.tooltip.pendingExpenses',
        defaultMessage: '{n} pending expenses',
      },
      'menu.transactions': {
        id: 'menu.transactions',
        defaultMessage: 'transactions',
      },
      'menu.applications': {
        id: 'menu.applications',
        defaultMessage: 'applications',
      },
    });
  }

  componentDidMount() {
    document.addEventListener('click', this.onClickOutside);
    if (typeof window !== 'undefined') {
      this.redirectAfterSignin = window.location.href.replace(
        /^https?:\/\/[^/]+/,
        '',
      );
      if (!window.localStorage.accessToken) {
        this.setState({ loading: false });
      }
    }
  }

  componentWillUnmount() {
    document.removeEventListener('click', this.onClickOutside);
  }

  logout = () => {
    this.setState({ showProfileMenu: false, status: 'loggingout' });
    window.localStorage.removeItem('accessToken');
    window.localStorage.removeItem('LoggedInUser');
    window.location.reload();
  };

  onClickOutside = () => {
    this.setState({ showProfileMenu: false });
  };

  toggleProfileMenu = e => {
    this.setState({ showProfileMenu: !this.state.showProfileMenu });
    // don't propagate to onClickOutside
    e.nativeEvent.stopImmediatePropagation();
  };

  tooltip(membership) {
    const { intl } = this.props;
    const { collective } = membership;
    const balance = get(collective, 'stats.balance');
    let str = intl.formatMessage(this.messages['tooltip.balance'], {
      balance: formatCurrency(balance, collective.currency),
    });
    const pendingExpenses = get(collective, 'stats.expenses.pending');
    if (pendingExpenses > 0) {
      str += ` - ${intl.formatMessage(
        this.messages['tooltip.pendingExpenses'],
        { n: pendingExpenses },
      )}`;
    }
    return str;
  }

  renderProfileMenu() {
    const { LoggedInUser, intl } = this.props;
    const score = c => {
      switch (c.role) {
        case 'HOST':
          return 0;
        case 'ADMIN':
          return 1;
        case 'MEMBER':
          return 2;
        case 'BACKER':
          return 3;
      }
    };
    const collectives = uniqBy(
      [
        ...LoggedInUser.memberOf.filter(
          m => m.collective.type === 'COLLECTIVE',
        ),
      ],
      m => m.collective.id,
    ).sort((a, b) => {
      return `${score(a)}-${a.collective.slug}` >
        `${score(b)}-${b.collective.slug}`
        ? 1
        : -1;
    }); // order by role then az

    const orgs = uniqBy(
      [
        ...LoggedInUser.memberOf.filter(
          m => m.collective.type === 'ORGANIZATION',
        ),
      ],
      m => m.collective.id,
    ).sort((a, b) => {
      return `${score(a)}-${a.collective.slug}` >
        `${score(b)}-${b.collective.slug}`
        ? 1
        : -1;
    }); // order by role then az

    return (
      <Container
        bg="white"
        border="1px solid rgba(18,19,20,0.12)"
        borderRadius="12px"
        boxShadow="0 4px 8px 0 rgba(61,82,102,0.08)"
        minWidth="170px"
        maxWidth="300px"
        position="absolute"
        right={16}
        top={40}
        width="max-content"
        zIndex={3000}
      >
        <Container p={3}>
          <Flex alignItems="center">

              <FormattedMessage
                id="collective"
                defaultMessage="my collectives"
              />

            <Container height="0.1rem" bg="#E6E6E6" width={1} minWidth={50} />
            <Link route="/create" passHref>

            </Link>
          </Flex>
          <Box is="ul" p={0} my={2}>
            {collectives.map(membership => (
              <ListItem
                py={1}
                key={`LoggedInMenu-Collective-${get(
                  membership,
                  'collective.slug',
                )}`}
              >
                <Link route={`/${get(membership, 'collective.slug')}`} passHref>

                </Link>
                {get(membership, 'collective.stats.expenses.pending') > 0 && (
                  <Badge>
                    {get(membership, 'collective.stats.expenses.pending')}
                  </Badge>
                )}
              </ListItem>
            ))}
          </Box>

          <Flex alignItems="center" mt={3}>

              <FormattedMessage
                id="organization"
                defaultMessage="my organizations"
              />

            <Container height="0.1rem" bg="#E6E6E6" width={1} minWidth={50} />
            <Link route="/organizations/new" passHref>

            </Link>
          </Flex>
          <Box is="ul" p={0} my={2}>
            {orgs.map(membership => (
              <ListItem
                py={1}
                key={`LoggedInMenu-Collective-${get(
                  membership,
                  'collective.slug',
                )}`}
              >
                <Link route={`/${get(membership, 'collective.slug')}`} passHref>

                </Link>
                {get(membership, 'collective.stats.expenses.pending') > 0 && (
                  <Badge>
                    {get(membership, 'collective.stats.expenses.pending')}
                  </Badge>
                )}
              </ListItem>
            ))}
          </Box>
          {orgs.length === 0 && (
            <Box my={2}>

                No organizations yet

            </Box>
          )}
        </Container>
        <Container height="0.1rem" bg="#E6E6E6" width={1} />
        <Container p={[3]}>

            <FormattedMessage id="menu.myAccount" defaultMessage="My account" />

          <Box is="ul" p={0} my={2}>
            <ListItem py={1}>
              <Link
                route="collective"
                params={{ slug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>
              <Link
                route="subscriptions"
                params={{ collectiveSlug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>
              <Link
                route="transactions"
                params={{ collectiveSlug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>
              <Link
                route="applications"
                params={{ collectiveSlug: LoggedInUser.username }}
                passHref
              >

              </Link>
            </ListItem>
            <ListItem py={1}>

            </ListItem>
          </Box>
        </Container>
      </Container>
    );
  }

  renderLoggedInUser() {
    const { showProfileMenu } = this.state;
    const { LoggedInUser } = this.props;

    return (
      <Flex alignItems="center" onClick={this.toggleProfileMenu}>
        {LoggedInUser.image && (
          <Avatar
            backgroundColor="#FDFDFD"
            backgroundImage={`url(${LoggedInUser.image})`}
            borderRadius="100%"
            display="inline-block"
            size="2.6rem"
            mr={2}
            cursor="pointer"
          />
        )}
        <Hide xs sm>

            {LoggedInUser.username}

        </Hide>
        <Caret
          color="#46b0ed"
          display="inline-block"
          height="0.6rem"
          strokeWidth="0.2rem"
          width="1.4rem"
          cursor="pointer"
        />
        {showProfileMenu && this.renderProfileMenu()}
      </Flex>
    );
  }

  render() {
    const { loading } = this.state;
    const { LoggedInUser } = this.props;

    let status;
    if (this.state.status) {
      status = this.state.status;
    } else if (loading && typeof LoggedInUser === 'undefined') {
      status = 'loading';
    } else if (!LoggedInUser) {
      status = 'loggedout';
    } else {
      status = 'loggedin';
    }

    return (

        {status === 'loading' && (

            <FormattedMessage id="loading" defaultMessage="loading" />
            …

        )}

        {status === 'loggingout' && (

            <FormattedMessage id="loggingout" defaultMessage="logging out" />
            …

        )}

        {status === 'loggedout' && (
          <Link
            route="signin"
            params={{ next: this.redirectAfterSignin }}
            passHref
          >

          </Link>
        )}

        {status === 'loggedin' && this.renderLoggedInUser()}

    );
  }
}

export default withIntl(TopBarProfileMenu);
<|endoftext|>


<commit_before>// param can be either length (number) or buffer
function createTypedArray(type, param){
  switch(type){
    case 'Uint8Array':
      return new Uint8Array(param);
    case 'Uint8ClampedArray':
      return new Uint8ClampedArray(param);
    case 'Uint16Array':
      return new Uint16Array(param);
    case 'Uint32Array':
      return new Uint32Array(param);
    case 'Int8Array':
      return new Int8Array(param);
    case 'Int16Array':
      return new Int16Array(param);
    case 'Int32Array':
      return new Int32Array(param);
    case 'Float32Array':
      return new Float32Array(param);
    case 'Float64Array':
      return new Float64Array(param);
  }
}

module.exports = function(event){
  var pack = event.data;
  var arg = pack.arg;
  var code = pack.code;
  /*jslint evil: true */
  var f = new Function(arg, code);
  var array = createTypedArray(pack.elementsType, pack.buffer);

  var i = array.length;
  for ( ; i--; ){
    array[i] = f(array[i]);
  }
  return {
    message: {
      index: pack.index,
      value: array.buffer
    },
    transferables: [ array.buffer ]
  };
};
<commit_msg>Added function cache<commit_after>// param can be either length (number) or buffer
function createTypedArray(type, param){
  switch(type){
    case 'Uint8Array':
      return new Uint8Array(param);
    case 'Uint8ClampedArray':
      return new Uint8ClampedArray(param);
    case 'Uint16Array':
      return new Uint16Array(param);
    case 'Uint32Array':
      return new Uint32Array(param);
    case 'Int8Array':
      return new Int8Array(param);
    case 'Int16Array':
      return new Int16Array(param);
    case 'Int32Array':
      return new Int32Array(param);
    case 'Float32Array':
      return new Float32Array(param);
    case 'Float64Array':
      return new Float64Array(param);
  }
}

var functionCache = new Map();

module.exports = function(event){
  var pack = event.data;
  var arg = pack.arg;
  var code = pack.code;
  var cacheKey = arg + code;
  var f = functionCache[cacheKey];
  if (!f){
    /*jslint evil: true */
    f = new Function(arg, code);
    functionCache[cacheKey] = f;
  }

  var array = createTypedArray(pack.elementsType, pack.buffer);

  var i = array.length;
  for ( ; i--; ){
    array[i] = f(array[i]);
  }
  return {
    message: {
      index: pack.index,
      value: array.buffer
    },
    transferables: [ array.buffer ]
  };
};
<|endoftext|>


<commit_before>'use strict';

export { default as cainiao } from './cainiao';
<commit_msg>Fixed typo exporting providers<commit_after>export { default as cainiao } from './cainiao'
export { default as dhl } from './dhl'
<|endoftext|>


<commit_before>/** Assignment model. */
Ext.define('devilry_subjectadmin.model.Assignment', {
    extend: 'devilry.apps.administrator.simplified.SimplifiedAssignment',

    proxy: Ext.create('devilry.extjshelpers.RestProxy', {
        url: DevilrySettings.DEVILRY_URLPATH_PREFIX + '/administrator/restfulsimplifiedassignment/',
        orderby: ['-publishing_time'],
        result_fieldgroups: ["subject", "period"]
    }),
});
<commit_msg>devilry_subjectadmin...model.Assignment: Specify X_DEVILRY_USE_EXTJS header.<commit_after>/** Assignment model. */
Ext.define('devilry_subjectadmin.model.Assignment', {
    extend: 'devilry.apps.administrator.simplified.SimplifiedAssignment',

    proxy: Ext.create('devilry.extjshelpers.RestProxy', {
        url: DevilrySettings.DEVILRY_URLPATH_PREFIX + '/administrator/restfulsimplifiedassignment/',
        headers: {X_DEVILRY_USE_EXTJS: true},
        orderby: ['-publishing_time'],
        result_fieldgroups: ["subject", "period"]
    }),
});
<|endoftext|>