<commit_before>"use strict"

import { fromJS } from "immutable"

const NAMESPACE_DEFAULT = "redux_localstorage_simple"

/**
	Saves specified parts of the Redux state tree into localstorage
	Note: this is Redux middleware. Read this for an explanation:
	http://redux.js.org/docs/advanced/Middleware.html

	PARAMETERS
	----------
	@config (Object) - 	Contains configuration options (leave blank to save entire state tree to localstorage)

						Properties:
							states (Array of Strings, optional) - States to save e.g. ["user", "products"]
							namespace (String, optional) - Namespace to add before your LocalStorage items

	Usage examples:

		// save entire state tree - EASIEST OPTION
		save()

		// save specific parts of the state tree
		save({
			states: ["user", "products"]
		})

		// save the entire state tree under the namespace "my_cool_app". The key "my_cool_app" will appear in LocalStorage
		save({
			namespace: "my_cool_app"
		})

		// save specific parts of the state tree with the namespace "my_cool_app". The keys "my_cool_app_user" and "my_cool_app_products" will appear in LocalStorage
		save({
		    states: ["user", "products"],
		    namespace: "my_cool_app"
		})
*/

export function save({ states = [], namespace = NAMESPACE_DEFAULT } = {}) {

	return store => next => action => {

		next(action)

		if (states.length === 0) {

			localStorage[namespace]
					= JSON.stringify(store.getState())

		} else {

			states.forEach(state => {
				localStorage[namespace + "_" + state]
					= JSON.stringify(store.getState()[state])
			})
		}

	}

}

/**
	Loads specified states from localstorage into the Redux state tree.

	PARAMETERS
	----------
	@config (Object) - 	Contains configuration options (leave blank to load entire state tree, if it was saved previously that is)
						Properties:
							states (Array of Strings, optional) - Parts of state tree to load e.g. ["user", "products"]
							namespace (String, optional) - Namespace required to retrieve your LocalStorage items, if any
							immutablejs (Boolean, optional) - If dealing with Immutable.js data structures, set this to true to load them correctly

	Usage examples:

		// load entire state tree - EASIEST OPTION
		load()

		// load specific parts of the state tree
		load({
			states: ["user", "products"]
		})

		// load the entire state tree which was previously saved with the namespace "my_cool_app"
		load({
			namespace: "my_cool_app"
		})

		// load specific parts of the state tree which use Immutable.js data structures
		load({
		    states: ["user", "products"],
		    immutablejs: true
		})

		// load specific parts of the state tree which was previously saved with the namespace "my_cool_app"
		load({
		    states: ["user", "products"],
		    namespace: "my_cool_app"
		})

*/

export function load({ states = [], immutablejs = false, namespace = NAMESPACE_DEFAULT } = {}) {

	let loadedState = {}

	if (states.length === 0) {

		if (localStorage[namespace]) {
			loadedState = JSON.parse(localStorage[namespace])

			if (immutablejs) {
				for(let key in loadedState) {
					loadedState[key] = fromJS(loadedState[key])
				}
			}

		}

	} else {

		states.forEach(function(state) {

			if (localStorage[namespace + "_" + state]) {

				loadedState[state] = JSON.parse(localStorage[namespace + "_" + state])

			}
		})

		if (immutablejs) {
			for(let key in loadedState) {
				loadedState[key] = fromJS(loadedState[key])
			}
		}

	}

	return loadedState
}

/**
	Combines multiple 'load' method calls to return a single state for use in Redux's createStore method.
	Use this when parts of the loading process need to be handled differently e.g. some parts of your state tree are immutable and some are not

	PARAMETERS
	----------
	@loads - 'load' method calls passed into this method as normal arguments

	Usage example:

		// load both vanilla JavaScript and Immutable.js parts of the state tree from LocalStorage
		combineLoads(
			load({ states: ["user"] }), // loading normal object
			load({ states: ["products"], immutablejs: true ) // this part of the state tree is an Immutable.js structure
		)

		// Load parts of the state tree saved with different namespaces
		combineLoads(
		    load({ states: ["user"], namespace: "account_stuff" }),
		    load({ states: ["products", "categories"], namespace: "site_stuff" )
		)
*/

export function combineLoads(...loads) {

	let combinedLoad = {}

	loads.forEach(load => {
		for (let state in load) {
			combinedLoad[state] = load[state]
		}
	})

	return combinedLoad
}

/**
	Clears all Redux state tree data from LocalStorage
	Remember to provide a namespace if you used one during the save process

	PARAMETERS
	----------
	@config (Object) - 	Contains configuration options (leave blank to clear entire state tree from LocalStorage, if it was saved without a namespace)
						Properties:
							namespace (String, optional) - Namespace that you used during the save process

	Usage example:

		// clear all Redux state tree data saved without a namespace
		clear()

		// clear Redux state tree data saved with a namespace
		clear({
			namespace: "my_cool_app"
		})
*/

export function clear({ namespace = NAMESPACE_DEFAULT } = {}) {

	for (let key in localStorage) {

		// key starts with namespace
		if (key.slice(0, namespace.length) === namespace) {
			localStorage.removeItem(key)
		}
	}

}
<commit_msg>Fix loading of state tree where only one reducer exists and it is an Immutable.js data structure<commit_after>"use strict"

import { fromJS } from "immutable"

const NAMESPACE_DEFAULT = "redux_localstorage_simple"

/**
	Saves specified parts of the Redux state tree into localstorage
	Note: this is Redux middleware. Read this for an explanation:
	http://redux.js.org/docs/advanced/Middleware.html

	PARAMETERS
	----------
	@config (Object) - 	Contains configuration options (leave blank to save entire state tree to localstorage)

						Properties:
							states (Array of Strings, optional) - States to save e.g. ["user", "products"]
							namespace (String, optional) - Namespace to add before your LocalStorage items

	Usage examples:

		// save entire state tree - EASIEST OPTION
		save()

		// save specific parts of the state tree
		save({
			states: ["user", "products"]
		})

		// save the entire state tree under the namespace "my_cool_app". The key "my_cool_app" will appear in LocalStorage
		save({
			namespace: "my_cool_app"
		})

		// save specific parts of the state tree with the namespace "my_cool_app". The keys "my_cool_app_user" and "my_cool_app_products" will appear in LocalStorage
		save({
		    states: ["user", "products"],
		    namespace: "my_cool_app"
		})
*/

export function save({ states = [], namespace = NAMESPACE_DEFAULT } = {}) {

	return store => next => action => {

		next(action)

		if (states.length === 0) {

			localStorage[namespace]
					= JSON.stringify(store.getState())

		} else {

			states.forEach(state => {
				localStorage[namespace + "_" + state]
					= JSON.stringify(store.getState()[state])
			})
		}

	}

}

/**
	Loads specified states from localstorage into the Redux state tree.

	PARAMETERS
	----------
	@config (Object) - 	Contains configuration options (leave blank to load entire state tree, if it was saved previously that is)
						Properties:
							states (Array of Strings, optional) - Parts of state tree to load e.g. ["user", "products"]
							namespace (String, optional) - Namespace required to retrieve your LocalStorage items, if any
							immutablejs (Boolean, optional) - If dealing with Immutable.js data structures, set this to true to load them correctly

	Usage examples:

		// load entire state tree - EASIEST OPTION
		load()

		// load specific parts of the state tree
		load({
			states: ["user", "products"]
		})

		// load the entire state tree which was previously saved with the namespace "my_cool_app"
		load({
			namespace: "my_cool_app"
		})

		// load specific parts of the state tree which use Immutable.js data structures
		load({
		    states: ["user", "products"],
		    immutablejs: true
		})

		// load specific parts of the state tree which was previously saved with the namespace "my_cool_app"
		load({
		    states: ["user", "products"],
		    namespace: "my_cool_app"
		})

*/

export function load({ states = [], immutablejs = false, namespace = NAMESPACE_DEFAULT } = {}) {

	let loadedState = {}

	if (states.length === 0) {

		if (localStorage[namespace]) {
			loadedState = JSON.parse(localStorage[namespace])

			if (immutablejs) {
				loadedState = fromJS(loadedState)
			}

		}

	} else {

		states.forEach(function(state) {

			if (localStorage[namespace + "_" + state]) {

				loadedState[state] = JSON.parse(localStorage[namespace + "_" + state])

			}
		})

		if (immutablejs) {
			for(let key in loadedState) {
				loadedState[key] = fromJS(loadedState[key])
			}
		}

	}

	return loadedState
}

/**
	Combines multiple 'load' method calls to return a single state for use in Redux's createStore method.
	Use this when parts of the loading process need to be handled differently e.g. some parts of your state tree are immutable and some are not

	PARAMETERS
	----------
	@loads - 'load' method calls passed into this method as normal arguments

	Usage example:

		// load both vanilla JavaScript and Immutable.js parts of the state tree from LocalStorage
		combineLoads(
			load({ states: ["user"] }), // loading normal object
			load({ states: ["products"], immutablejs: true ) // this part of the state tree is an Immutable.js structure
		)

		// Load parts of the state tree saved with different namespaces
		combineLoads(
		    load({ states: ["user"], namespace: "account_stuff" }),
		    load({ states: ["products", "categories"], namespace: "site_stuff" )
		)
*/

export function combineLoads(...loads) {

	let combinedLoad = {}

	loads.forEach(load => {
		for (let state in load) {
			combinedLoad[state] = load[state]
		}
	})

	return combinedLoad
}

/**
	Clears all Redux state tree data from LocalStorage
	Remember to provide a namespace if you used one during the save process

	PARAMETERS
	----------
	@config (Object) - 	Contains configuration options (leave blank to clear entire state tree from LocalStorage, if it was saved without a namespace)
						Properties:
							namespace (String, optional) - Namespace that you used during the save process

	Usage example:

		// clear all Redux state tree data saved without a namespace
		clear()

		// clear Redux state tree data saved with a namespace
		clear({
			namespace: "my_cool_app"
		})
*/

export function clear({ namespace = NAMESPACE_DEFAULT } = {}) {

	for (let key in localStorage) {

		// key starts with namespace
		if (key.slice(0, namespace.length) === namespace) {
			localStorage.removeItem(key)
		}
	}

}
<|endoftext|>


<commit_before>'use strict'

import * as gooey from '../dist/core/index'

/**
 * Supported transaction methods in HTTP
 */
export const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD', 'LINK']

/**
 * Meta-information on XHR interface
 */
export const xhr = {
  versions : [2,3,4,5].map(v => `MSXML2.XmlHttp.${v}.0`).concat('Microsoft.XmlHttp'),
  events   : ['onloadstart', 'onprogress', 'onabort', 'onerror', 'onload', 'ontimeout', 'onloadend']
}

/**
 * Chainable interface that integrates HttpRequest and HttpResponse
 *
 * TODO - handle gooey.Service params
 */
export class Http extends gooey.Service {

  /**
   * @param {Object} proxies request / response middlewares
   */
  constructor(proxies?: Object = {}) {
    super() // TODO

    this.proxies = proxies

    methods.forEach(m => {
      const method = m.toLowerCase()

      this.constructor.prototype[method] = () => new HttpRequest(method, ...arguments)
    })
  }

}

/**
 * Deferred and chainable XHR-based HTTP request
 */
export class HttpRequest extends Promise {

  /**
   * Creates a native XHR object and sends request on resolve
   *
   * @param {String} method
   * @param {String} url
   * @param {?Object} body
   * @param {?Object} headers
   * @param {?String} type
   * @param {?String} charset
   * @returns {HttpRequest}
   */
  constructor(method: String, url: String, body?: Object, headers?: Object, type?: String, charset?: String) {
    super((resolve, reject) => {
      if (methods.find(method => method === name)) {
        resolve(this)
      } else {
        reject(`Invalid request, valid method and url required: ${url} | ${method}`)
      }
    })

    this._xhr = this.createXhr()

    this.url(url)
    this.method(method)
    this.headers(headers)
    this.body(body)
    this.type(type)
    this.charset(charset)
  }

  /**
   * Fetches request's URL
   *
   * @returns {String}
   */
  get url(): String {
    return this._url
  }

  /**
   * Modifies request URL
   *
   * @param {String} url
   * @returns {HttpRequest}
   */
  set url(url: String): HttpRequest {
    this._url = url
    return this
  }

  /**
   * Fetches request's method
   *
   * @returns {String}
   */
  get method(): String {
    return this._method
  }

  /**
   * Modifies request method (GET, POST, PUT, etc)
   *
   * @param {String} method
   * @returns {HttpRequest}
   */
  set method(method: String): HttpRequest {
    this._method = method
    return this
  }

  /**
   * Fetches request's body
   *
   * @returns {String}
   */
  get body(): String {
    return this._body
  }

  /**
   * Sets request body
   *
   * @param {Object} data
   * @returns {HttpRequest}
   */
  set body(data): HttpRequest {
    this._body = data
    return this
  }

  /**
   * Modifies a single request header
   *
   * @param {Object} header {field: value}
   * @returns {HttpRequest}
   */
  set header(header: Object): HttpRequest {
    const {field, value} = header
    this._headers[field] = value
    this._xhr.setRequestHeader(field, value)
    return this
  }

  /**
   * Modifies request headers based on object
   *
   * @param {Object} headers {field: value}
   * @returns {HttpRequest}
   */
  set headers(headers: Object): HttpRequest {
    Object.keys(headers).forEach(field => this.header({field, value: obj[key]}))
    return this
  }

  /**
   * Sets request content type
   *
   * @param {String} type
   * @param {String} charset
   * @returns {HttpRequest}
   */
  set type(type: String = 'application/text'): HttpRequest {
    this._body = data
    this.header({field: 'Content-Type', value: `${type}; charset=${this._charset}`})
    return this
  }

  /**
   * Sets request character set
   *
   * @param {String} charset
   * @returns {HttpRequest}
   */
  set charset(charset: String = 'UTF-8'): HttpRequest {
    this._charset = charset
    return this
  }

  /**
   * Creates a native XHR object and sends request on resolve
   *
   * @returns {HttpRequest}
   */
  send(): HttpRequest {
    const xhr = this._xhr

    if (xhr) {
      xhr.open(this.method, url, true)

      // TODO - support remaining xhr.events

      xhr.onload = () => {
        const body = this.mimeify(xhr.responseText, this.type)

        if (xhr.status >= 200 && xhr.status < 400) {
          super.resolve(body)
        } else {
          super.reject(body)
        }
      }

      xhr.onerror = (e) => super.reject(e)

      Object.keys(headers).forEach(key => {
        request.setRequestHeader(key, this.headers[key])
      })

      xhr.send(this.body)
    } else {
      super.reject(`Cannot perform XHR, failed to determine compatible version`)
    }

    return this
  }

  // set attach()

  /**
   * Builds a native XHR object based on the browser version
   *
   * @returns {Object} native XHR object
   */
  createXhr(): Object {
    let xhr = null

    if (!Object.is(XMLHttpRequest, undefined)) {
      xhr = new XMLHttpRequest()
    } else {
      versions.forEach(version => { // TODO - optimize
        try {
          if (!xhr) xhr = new ActiveXObject(version)
        } catch (e) {}
      })
    }

    return xhr
  }

  /**
   * Marshalls data into mimeType
   *
   * @param {Object} data
   * @param {String} mimeType
   * @returns {Object}
   */
  mimeify(data, mimeType: String = 'application/text') {
    return {
      'application/json': JSON.parse(data),
    }[mimeType] || data
  }

}

/**
 * Deferred and chainable XHR-based HTTP response
 */
export class HttpResponse extends Promise {

  constructor(request: HttpRequest) {
    super() // TODO
  }

}
<commit_msg>:sparkles: simple request<commit_after>'use strict'

import * as gooey from '../dist/core/index'

/**
 * Supported transaction methods in HTTP
 */
export const methods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD', 'LINK']

/**
 * Meta-information on XHR interface
 */
export const xhr = {
  versions : [2,3,4,5].map(v => `MSXML2.XmlHttp.${v}.0`).concat('Microsoft.XmlHttp'),
  events   : ['onloadstart', 'onprogress', 'onabort', 'onerror', 'onload', 'ontimeout', 'onloadend']
}

/**
 * Chainable interface that integrates HttpRequest and HttpResponse
 *
 * TODO - handle gooey.Service params
 */
export class Http extends gooey.Service {

  /**
   * @param {Object} proxies request / response middlewares
   */
  constructor(proxies?: Object = {}) {
    super() // TODO

    this.proxies = proxies

    methods.forEach(m => {
      const method = m.toLowerCase()

      this.constructor.prototype[method] = () => new HttpRequest(method, ...arguments)
    })
  }

}

/**
 * Deferred and chainable XHR-based HTTP request
 */
export class HttpRequest extends Promise {

  /**
   * Creates a native XHR object and sends request on resolve
   *
   * @param {String} method
   * @param {String} url
   * @param {?Object} body
   * @param {?Object} headers
   * @param {?String} type
   * @param {?String} charset
   * @returns {HttpRequest}
   */
  constructor(method: String, url: String, body?: Object, headers?: Object, type?: String, charset?: String) {
    super((resolve, reject) => {
      if (methods.find(method => method === name)) {
        resolve(this)
      } else {
        reject(`Invalid request, valid method and url required: ${url} | ${method}`)
      }
    })

    this._xhr = this.createXhr()

    this.url(url)
    this.method(method)
    this.headers(headers)
    this.body(body)
    this.type(type)
    this.charset(charset)
  }

  /**
   * Fetches request's URL
   *
   * @returns {String}
   */
  get url(): String {
    return this._url
  }

  /**
   * Modifies request URL
   *
   * @param {String} url
   * @returns {HttpRequest}
   */
  set url(url: String): HttpRequest {
    this._url = url
    return this
  }

  /**
   * Fetches request's method
   *
   * @returns {String}
   */
  get method(): String {
    return this._method
  }

  /**
   * Modifies request method (GET, POST, PUT, etc)
   *
   * @param {String} method
   * @returns {HttpRequest}
   */
  set method(method: String): HttpRequest {
    this._method = method
    return this
  }

  /**
   * Fetches request's body
   *
   * @returns {String}
   */
  get body(): String {
    return this._body
  }

  /**
   * Sets request body
   *
   * @param {Object} data
   * @returns {HttpRequest}
   */
  set body(data): HttpRequest {
    this._body = data
    return this
  }

  /**
   * Modifies a single request header
   *
   * @param {Object} header {field: value}
   * @returns {HttpRequest}
   */
  set header(header: Object): HttpRequest {
    const {field, value} = header
    this._headers[field] = value
    this._xhr.setRequestHeader(field, value)
    return this
  }

  /**
   * Modifies request headers based on object
   *
   * @param {Object} headers {field: value}
   * @returns {HttpRequest}
   */
  set headers(headers: Object): HttpRequest {
    Object.keys(headers).forEach(field => this.header({field, value: obj[key]}))
    return this
  }

  /**
   * Sets request content type
   *
   * @param {String} type
   * @param {String} charset
   * @returns {HttpRequest}
   */
  set type(type: String = 'application/text'): HttpRequest {
    this._body = data
    this.header({field: 'Content-Type', value: `${type}; charset=${this._charset}`})
    return this
  }

  /**
   * Sets request character set
   *
   * @param {String} charset
   * @returns {HttpRequest}
   */
  set charset(charset: String = 'UTF-8'): HttpRequest {
    this._charset = charset
    return this
  }

  /**
   * Creates a native XHR object and sends request on resolve
   *
   * @returns {HttpRequest}
   */
  send(): HttpRequest {
    const xhr = this._xhr

    if (xhr) {
      xhr.open(this.method, url, true)

      // TODO - support remaining xhr.events

      xhr.onload = () => {
        const body = this.mimeify(xhr.responseText, this.type)

        if (xhr.status >= 200 && xhr.status < 400) {
          super.resolve(body)
        } else {
          super.reject(body)
        }
      }

      xhr.onerror = (e) => super.reject(e)

      Object.keys(headers).forEach(key => {
        request.setRequestHeader(key, this.headers[key])
      })

      xhr.send(this.body)
    } else {
      super.reject(`Cannot perform XHR, failed to determine compatible version`)
    }

    return this
  }

  // set attach()

  /**
   * Builds a native XHR object based on the browser version
   *
   * @returns {Object} native XHR object
   */
  createXhr(): Object {
    let xhr = null

    if (!Object.is(XMLHttpRequest, undefined)) {
      xhr = new XMLHttpRequest()
    } else {
      versions.forEach(version => { // TODO - optimize
        try {
          if (!xhr) xhr = new ActiveXObject(version)
        } catch (e) {}
      })
    }

    return xhr
  }

  /**
   * Marshalls data into mimeType
   *
   * @param {Object} data
   * @param {String} mimeType
   * @returns {Object}
   */
  mimeify(data, mimeType: String = 'application/text') {
    return {
      'application/json': JSON.parse(data),
    }[mimeType] || data
  }

  /**
   * Marshalls request from a promise into a simple POJO
   * of request-specific properties
   *
   * @param {Object} data
   * @param {String} mimeType
   * @returns {Object}
   */
  get simple() {
    return Object.keys(this).find(k => k[0] === '_')
  }

}

/**
 * Deferred and chainable XHR-based HTTP response
 */
export class HttpResponse extends Promise {

  constructor(request: HttpRequest) {
    super() // TODO
  }

}
<|endoftext|>


<commit_before>/* @flow */

import toFastProperties from "to-fast-properties";
import compact from "lodash/array/compact";
import loClone from "lodash/lang/clone";
import each from "lodash/collection/each";
import uniq from "lodash/array/uniq";

let t = exports;

/**
 * Registers `is[Type]` and `assert[Type]` generated functions for a given `type`.
 * Pass `skipAliasCheck` to force it to directly compare `node.type` with `type`.
 */

function registerType(type: string) {
  let is = t[`is${type}`] = function (node, opts) {
    return t.is(type, node, opts);
  };

  t[`assert${type}`] = function (node, opts) {
    opts = opts || {};
    if (!is(node, opts)) {
      throw new Error(`Expected type ${JSON.stringify(type)} with option ${JSON.stringify(opts)}`);
    }
  };
}

export const STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
export const FLATTENABLE_KEYS        = ["body", "expressions"];
export const FOR_INIT_KEYS           = ["left", "init"];
export const COMMENT_KEYS            = ["leadingComments", "trailingComments", "innerComments"];

export const INHERIT_KEYS = {
  optional: ["typeAnnotation", "typeParameters", "returnType"],
  force: ["_scopeInfo", "_paths", "start", "loc", "end"]
};

export const BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="];
export const EQUALITY_BINARY_OPERATORS       = ["==", "===", "!=", "!=="];
export const COMPARISON_BINARY_OPERATORS     = EQUALITY_BINARY_OPERATORS.concat(["in", "instanceof"]);
export const BOOLEAN_BINARY_OPERATORS        = [].concat(COMPARISON_BINARY_OPERATORS, BOOLEAN_NUMBER_BINARY_OPERATORS);
export const NUMBER_BINARY_OPERATORS         = ["-", "/", "*", "**", "&", "|", ">>", ">>>", "<<", "^"];

export const BOOLEAN_UNARY_OPERATORS = ["delete", "!"];
export const NUMBER_UNARY_OPERATORS  = ["+", "-", "++", "--", "~"];
export const STRING_UNARY_OPERATORS  = ["typeof"];

import "./definitions/init";
import { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS, DEPRECATED_KEYS } from "./definitions";
export { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS, DEPRECATED_KEYS };

import * as _react from "./react";
export { _react as react };

/**
 * Registers `is[Type]` and `assert[Type]` for all types.
 */

for (let type in t.VISITOR_KEYS) {
  registerType(type);
}

/**
 * Flip `ALIAS_KEYS` for faster access in the reverse direction.
 */

t.FLIPPED_ALIAS_KEYS = {};

each(t.ALIAS_KEYS, function (aliases, type) {
  each(aliases, function (alias) {
    let types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || [];
    types.push(type);
  });
});

/**
 * Registers `is[Alias]` and `assert[Alias]` functions for all aliases.
 */

each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
  t[type.toUpperCase() + "_TYPES"] = types;
  registerType(type);
});

export const TYPES = Object.keys(t.VISITOR_KEYS)
  .concat(Object.keys(t.FLIPPED_ALIAS_KEYS))
  .concat(Object.keys(t.DEPRECATED_KEYS));

/**
 * Returns whether `node` is of given `type`.
 *
 * For better performance, use this instead of `is[Type]` when `type` is unknown.
 * Optionally, pass `skipAliasCheck` to directly compare `node.type` with `type`.
 */

export function is(type: string, node: Object, opts?: Object): boolean {
  if (!node) return false;

  let matches = isType(node.type, type);
  if (!matches) return false;

  if (typeof opts === "undefined") {
    return true;
  } else {
    return t.shallowEqual(node, opts);
  }
}

/**
 * Test if a `nodeType` is a `targetType` or if `targetType` is an alias of `nodeType`.
 */

export function isType(nodeType: string, targetType: string): boolean {
  if (nodeType === targetType) return true;

  let aliases: ?Array<string> = t.FLIPPED_ALIAS_KEYS[targetType];
  if (aliases) {
    if (aliases[0] === nodeType) return true;

    for (let alias of aliases) {
      if (nodeType === alias) return true;
    }
  }

  return false;
}

/**
 * Description
 */

each(t.BUILDER_KEYS, function (keys, type) {
  function builder() {
    if (arguments.length > keys.length) {
      // todo: error
    }

    let node = {};
    node.type = type;

    let i = 0;

    for (let key of (keys: Array<string>)) {
      let field = t.NODE_FIELDS[type][key];

      let arg = arguments[i++];
      if (arg === undefined) arg = loClone(field.default);

      node[key] = arg;
    }

    for (let key in node) {
      validate(node, key, node[key]);
    }

    return node;
  }

  t[type] = builder;
  t[type[0].toLowerCase() + type.slice(1)] = builder;
});

/**
 * Description
 */

 for (let type in t.DEPRECATED_KEYS) {
   let newType = t.DEPRECATED_KEYS[type];

   function proxy(fn) {
     return function () {
       console.trace(`The node type ${type} has been renamed to ${newType}`);
       return fn.apply(this, arguments);
     };
   }

   t[type] = t[type[0].toLowerCase() + type.slice(1)] = proxy(t[newType]);
   t[`is${type}`] = proxy(t[`is${newType}`]);
   t[`assert${type}`] = proxy(t[`assert${newType}`]);
 }

/**
 * Description
 */

export function validate(node?: Object, key: string, val: any) {
  if (!node) return;

  let fields = t.NODE_FIELDS[node.type];
  if (!fields) return;

  let field = fields[key];
  if (!field || !field.validate) return;
  if (field.optional && val == null) return;

  field.validate(node, key, val);
}

/**
 * Test if an object is shallowly equal.
 */

export function shallowEqual(actual: Object, expected: Object): boolean {
  let keys = Object.keys(expected);

  for (let key of (keys: Array<string>)) {
    if (actual[key] !== expected[key]) {
      return false;
    }
  }

  return true;
}

/**
 * Append a node to a member expression.
 */

export function appendToMemberExpression(member: Object, append: Object, computed?: boolean): Object {
  member.object   = t.memberExpression(member.object, member.property, member.computed);
  member.property = append;
  member.computed = !!computed;
  return member;
}

/**
 * Prepend a node to a member expression.
 */

export function prependToMemberExpression(member: Object, prepend: Object): Object {
  member.object = t.memberExpression(prepend, member.object);
  return member;
}

/**
 * Ensure the `key` (defaults to "body") of a `node` is a block.
 * Casting it to a block if it is not.
 */

export function ensureBlock(node: Object, key: string = "body"): Object {
  return node[key] = t.toBlock(node[key], node);
}

/**
 * Create a shallow clone of a `node` excluding `_private` properties.
 */

export function clone(node: Object): Object {
  let newNode = {};
  for (let key in node) {
    if (key[0] === "_") continue;
    newNode[key] = node[key];
  }
  return newNode;
}

/**
 * Create a deep clone of a `node` and all of it's child nodes
 * exluding `_private` properties.
 */

export function cloneDeep(node: Object): Object {
  let newNode = {};

  for (let key in node) {
    if (key[0] === "_") continue;

    let val = node[key];

    if (val) {
      if (val.type) {
        val = t.cloneDeep(val);
      } else if (Array.isArray(val)) {
        val = val.map(t.cloneDeep);
      }
    }

    newNode[key] = val;
  }

  return newNode;
}

/**
 * Build a function that when called will return whether or not the
 * input `node` `MemberExpression` matches the input `match`.
 *
 * For example, given the match `React.createClass` it would match the
 * parsed nodes of `React.createClass` and `React["createClass"]`.
 */

export function buildMatchMemberExpression(match:string, allowPartial?: boolean): Function {
  let parts = match.split(".");

  return function (member) {
    // not a member expression
    if (!t.isMemberExpression(member)) return false;

    let search = [member];
    let i = 0;

    while (search.length) {
      let node = search.shift();

      if (allowPartial && i === parts.length) {
        return true;
      }

      if (t.isIdentifier(node)) {
        // this part doesn't match
        if (parts[i] !== node.name) return false;
      } else if (t.isStringLiteral(node)) {
        // this part doesn't match
        if (parts[i] !== node.value) return false;
      } else if (t.isMemberExpression(node)) {
        if (node.computed && !t.isStringLiteral(node.property)) {
          // we can't deal with this
          return false;
        } else {
          search.push(node.object);
          search.push(node.property);
          continue;
        }
      } else {
        // we can't deal with this
        return false;
      }

      // too many parts
      if (++i > parts.length) {
        return false;
      }
    }

    return true;
  };
}

/**
 * Remove comment properties from a node.
 */

export function removeComments(node: Object): Object {
  for (let key of COMMENT_KEYS) {
    delete node[key];
  }
  return node;
}

/**
 * Inherit all unique comments from `parent` node to `child` node.
 */

export function inheritsComments(child: Object, parent: Object): Object {
  inheritTrailingComments(child, parent);
  inheritLeadingComments(child, parent);
  inheritInnerComments(child, parent);
  return child;
}

export function inheritTrailingComments(child: Object, parent: Object) {
  _inheritComments("trailingComments", child, parent);
}

export function inheritLeadingComments(child: Object, parent: Object) {
  _inheritComments("leadingComments", child, parent);
}

export function inheritInnerComments(child: Object, parent: Object) {
  _inheritComments("innerComments", child, parent);
}

function _inheritComments(key, child, parent) {
  if (child && parent) {
    child[key] = uniq(compact([].concat(child[key], parent[key])));
  }
}

/**
 * Inherit all contextual properties from `parent` node to `child` node.
 */

export function inherits(child: Object, parent: Object): Object {
  if (!child || !parent) return child;

  for (let key of (t.INHERIT_KEYS.optional: Array<string>)) {
    if (child[key] == null) {
      child[key] = parent[key];
    }
  }

  for (let key of (t.INHERIT_KEYS.force: Array<string>)) {
    child[key] = parent[key];
  }

  t.inheritsComments(child, parent);

  return child;
}

/**
 * TODO
 */

export function assertNode(node?) {
  if (!isNode(node)) {
    throw new TypeError("Not a valid node " + (node && node.type));
  }
}

/**
 * TODO
 */

export function isNode(node?): boolean {
  return !!(node && VISITOR_KEYS[node.type]);
}

// Optimize property access.
toFastProperties(t);
toFastProperties(t.VISITOR_KEYS);

//
export * from "./retrievers";
export * from "./validators";
export * from "./converters";
export * from "./flow";
<commit_msg>add error for too many builder method args<commit_after>/* @flow */

import toFastProperties from "to-fast-properties";
import compact from "lodash/array/compact";
import loClone from "lodash/lang/clone";
import each from "lodash/collection/each";
import uniq from "lodash/array/uniq";

let t = exports;

/**
 * Registers `is[Type]` and `assert[Type]` generated functions for a given `type`.
 * Pass `skipAliasCheck` to force it to directly compare `node.type` with `type`.
 */

function registerType(type: string) {
  let is = t[`is${type}`] = function (node, opts) {
    return t.is(type, node, opts);
  };

  t[`assert${type}`] = function (node, opts) {
    opts = opts || {};
    if (!is(node, opts)) {
      throw new Error(`Expected type ${JSON.stringify(type)} with option ${JSON.stringify(opts)}`);
    }
  };
}

export const STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
export const FLATTENABLE_KEYS        = ["body", "expressions"];
export const FOR_INIT_KEYS           = ["left", "init"];
export const COMMENT_KEYS            = ["leadingComments", "trailingComments", "innerComments"];

export const INHERIT_KEYS = {
  optional: ["typeAnnotation", "typeParameters", "returnType"],
  force: ["_scopeInfo", "_paths", "start", "loc", "end"]
};

export const BOOLEAN_NUMBER_BINARY_OPERATORS = [">", "<", ">=", "<="];
export const EQUALITY_BINARY_OPERATORS       = ["==", "===", "!=", "!=="];
export const COMPARISON_BINARY_OPERATORS     = EQUALITY_BINARY_OPERATORS.concat(["in", "instanceof"]);
export const BOOLEAN_BINARY_OPERATORS        = [].concat(COMPARISON_BINARY_OPERATORS, BOOLEAN_NUMBER_BINARY_OPERATORS);
export const NUMBER_BINARY_OPERATORS         = ["-", "/", "*", "**", "&", "|", ">>", ">>>", "<<", "^"];

export const BOOLEAN_UNARY_OPERATORS = ["delete", "!"];
export const NUMBER_UNARY_OPERATORS  = ["+", "-", "++", "--", "~"];
export const STRING_UNARY_OPERATORS  = ["typeof"];

import "./definitions/init";
import { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS, DEPRECATED_KEYS } from "./definitions";
export { VISITOR_KEYS, ALIAS_KEYS, NODE_FIELDS, BUILDER_KEYS, DEPRECATED_KEYS };

import * as _react from "./react";
export { _react as react };

/**
 * Registers `is[Type]` and `assert[Type]` for all types.
 */

for (let type in t.VISITOR_KEYS) {
  registerType(type);
}

/**
 * Flip `ALIAS_KEYS` for faster access in the reverse direction.
 */

t.FLIPPED_ALIAS_KEYS = {};

each(t.ALIAS_KEYS, function (aliases, type) {
  each(aliases, function (alias) {
    let types = t.FLIPPED_ALIAS_KEYS[alias] = t.FLIPPED_ALIAS_KEYS[alias] || [];
    types.push(type);
  });
});

/**
 * Registers `is[Alias]` and `assert[Alias]` functions for all aliases.
 */

each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
  t[type.toUpperCase() + "_TYPES"] = types;
  registerType(type);
});

export const TYPES = Object.keys(t.VISITOR_KEYS)
  .concat(Object.keys(t.FLIPPED_ALIAS_KEYS))
  .concat(Object.keys(t.DEPRECATED_KEYS));

/**
 * Returns whether `node` is of given `type`.
 *
 * For better performance, use this instead of `is[Type]` when `type` is unknown.
 * Optionally, pass `skipAliasCheck` to directly compare `node.type` with `type`.
 */

export function is(type: string, node: Object, opts?: Object): boolean {
  if (!node) return false;

  let matches = isType(node.type, type);
  if (!matches) return false;

  if (typeof opts === "undefined") {
    return true;
  } else {
    return t.shallowEqual(node, opts);
  }
}

/**
 * Test if a `nodeType` is a `targetType` or if `targetType` is an alias of `nodeType`.
 */

export function isType(nodeType: string, targetType: string): boolean {
  if (nodeType === targetType) return true;

  let aliases: ?Array<string> = t.FLIPPED_ALIAS_KEYS[targetType];
  if (aliases) {
    if (aliases[0] === nodeType) return true;

    for (let alias of aliases) {
      if (nodeType === alias) return true;
    }
  }

  return false;
}

/**
 * Description
 */

each(t.BUILDER_KEYS, function (keys, type) {
  function builder() {
    if (arguments.length > keys.length) {
      throw new Error(`t.${type}: Too many arguments passed. Received ${arguments.length} but can receive no more than ${keys.length}`);
    }

    let node = {};
    node.type = type;

    let i = 0;

    for (let key of (keys: Array<string>)) {
      let field = t.NODE_FIELDS[type][key];

      let arg = arguments[i++];
      if (arg === undefined) arg = loClone(field.default);

      node[key] = arg;
    }

    for (let key in node) {
      validate(node, key, node[key]);
    }

    return node;
  }

  t[type] = builder;
  t[type[0].toLowerCase() + type.slice(1)] = builder;
});

/**
 * Description
 */

 for (let type in t.DEPRECATED_KEYS) {
   let newType = t.DEPRECATED_KEYS[type];

   function proxy(fn) {
     return function () {
       console.trace(`The node type ${type} has been renamed to ${newType}`);
       return fn.apply(this, arguments);
     };
   }

   t[type] = t[type[0].toLowerCase() + type.slice(1)] = proxy(t[newType]);
   t[`is${type}`] = proxy(t[`is${newType}`]);
   t[`assert${type}`] = proxy(t[`assert${newType}`]);
 }

/**
 * Description
 */

export function validate(node?: Object, key: string, val: any) {
  if (!node) return;

  let fields = t.NODE_FIELDS[node.type];
  if (!fields) return;

  let field = fields[key];
  if (!field || !field.validate) return;
  if (field.optional && val == null) return;

  field.validate(node, key, val);
}

/**
 * Test if an object is shallowly equal.
 */

export function shallowEqual(actual: Object, expected: Object): boolean {
  let keys = Object.keys(expected);

  for (let key of (keys: Array<string>)) {
    if (actual[key] !== expected[key]) {
      return false;
    }
  }

  return true;
}

/**
 * Append a node to a member expression.
 */

export function appendToMemberExpression(member: Object, append: Object, computed?: boolean): Object {
  member.object   = t.memberExpression(member.object, member.property, member.computed);
  member.property = append;
  member.computed = !!computed;
  return member;
}

/**
 * Prepend a node to a member expression.
 */

export function prependToMemberExpression(member: Object, prepend: Object): Object {
  member.object = t.memberExpression(prepend, member.object);
  return member;
}

/**
 * Ensure the `key` (defaults to "body") of a `node` is a block.
 * Casting it to a block if it is not.
 */

export function ensureBlock(node: Object, key: string = "body"): Object {
  return node[key] = t.toBlock(node[key], node);
}

/**
 * Create a shallow clone of a `node` excluding `_private` properties.
 */

export function clone(node: Object): Object {
  let newNode = {};
  for (let key in node) {
    if (key[0] === "_") continue;
    newNode[key] = node[key];
  }
  return newNode;
}

/**
 * Create a deep clone of a `node` and all of it's child nodes
 * exluding `_private` properties.
 */

export function cloneDeep(node: Object): Object {
  let newNode = {};

  for (let key in node) {
    if (key[0] === "_") continue;

    let val = node[key];

    if (val) {
      if (val.type) {
        val = t.cloneDeep(val);
      } else if (Array.isArray(val)) {
        val = val.map(t.cloneDeep);
      }
    }

    newNode[key] = val;
  }

  return newNode;
}

/**
 * Build a function that when called will return whether or not the
 * input `node` `MemberExpression` matches the input `match`.
 *
 * For example, given the match `React.createClass` it would match the
 * parsed nodes of `React.createClass` and `React["createClass"]`.
 */

export function buildMatchMemberExpression(match:string, allowPartial?: boolean): Function {
  let parts = match.split(".");

  return function (member) {
    // not a member expression
    if (!t.isMemberExpression(member)) return false;

    let search = [member];
    let i = 0;

    while (search.length) {
      let node = search.shift();

      if (allowPartial && i === parts.length) {
        return true;
      }

      if (t.isIdentifier(node)) {
        // this part doesn't match
        if (parts[i] !== node.name) return false;
      } else if (t.isStringLiteral(node)) {
        // this part doesn't match
        if (parts[i] !== node.value) return false;
      } else if (t.isMemberExpression(node)) {
        if (node.computed && !t.isStringLiteral(node.property)) {
          // we can't deal with this
          return false;
        } else {
          search.push(node.object);
          search.push(node.property);
          continue;
        }
      } else {
        // we can't deal with this
        return false;
      }

      // too many parts
      if (++i > parts.length) {
        return false;
      }
    }

    return true;
  };
}

/**
 * Remove comment properties from a node.
 */

export function removeComments(node: Object): Object {
  for (let key of COMMENT_KEYS) {
    delete node[key];
  }
  return node;
}

/**
 * Inherit all unique comments from `parent` node to `child` node.
 */

export function inheritsComments(child: Object, parent: Object): Object {
  inheritTrailingComments(child, parent);
  inheritLeadingComments(child, parent);
  inheritInnerComments(child, parent);
  return child;
}

export function inheritTrailingComments(child: Object, parent: Object) {
  _inheritComments("trailingComments", child, parent);
}

export function inheritLeadingComments(child: Object, parent: Object) {
  _inheritComments("leadingComments", child, parent);
}

export function inheritInnerComments(child: Object, parent: Object) {
  _inheritComments("innerComments", child, parent);
}

function _inheritComments(key, child, parent) {
  if (child && parent) {
    child[key] = uniq(compact([].concat(child[key], parent[key])));
  }
}

/**
 * Inherit all contextual properties from `parent` node to `child` node.
 */

export function inherits(child: Object, parent: Object): Object {
  if (!child || !parent) return child;

  for (let key of (t.INHERIT_KEYS.optional: Array<string>)) {
    if (child[key] == null) {
      child[key] = parent[key];
    }
  }

  for (let key of (t.INHERIT_KEYS.force: Array<string>)) {
    child[key] = parent[key];
  }

  t.inheritsComments(child, parent);

  return child;
}

/**
 * TODO
 */

export function assertNode(node?) {
  if (!isNode(node)) {
    throw new TypeError("Not a valid node " + (node && node.type));
  }
}

/**
 * TODO
 */

export function isNode(node?): boolean {
  return !!(node && VISITOR_KEYS[node.type]);
}

// Optimize property access.
toFastProperties(t);
toFastProperties(t.VISITOR_KEYS);

//
export * from "./retrievers";
export * from "./validators";
export * from "./converters";
export * from "./flow";
<|endoftext|>


<commit_before>// debug output
let __DEBUG__
function debug (...args) {
  if (__DEBUG__) {
    if (!console.group) {
      args.unshift('%credux-undo', 'font-style: italic')
    }
    console.log(...args)
  }
}
function debugStart (action, state) {
  if (__DEBUG__) {
    const args = ['action', action.type]
    if (console.group) {
      args.unshift('%credux-undo', 'font-style: italic')
      console.groupCollapsed(...args)
      console.log('received', {state, action})
    } else {
      debug(...args)
    }
  }
}
function debugEnd () {
  if (__DEBUG__) {
    return console.groupEnd && console.groupEnd()
  }
}
// /debug output

// action types
export const ActionTypes = {
  UNDO: '@@redux-undo/UNDO',
  REDO: '@@redux-undo/REDO',
  JUMP_TO_FUTURE: '@@redux-undo/JUMP_TO_FUTURE',
  JUMP_TO_PAST: '@@redux-undo/JUMP_TO_PAST',
  CLEAR_HISTORY: '@@redux-undo/CLEAR_HISTORY'
}
// /action types

// action creators to change the state
export const ActionCreators = {
  undo () {
    return { type: ActionTypes.UNDO }
  },
  redo () {
    return { type: ActionTypes.REDO }
  },
  jumpToFuture (index) {
    return { type: ActionTypes.JUMP_TO_FUTURE, index }
  },
  jumpToPast (index) {
    return { type: ActionTypes.JUMP_TO_PAST, index }
  },
  clearHistory () {
    return { type: ActionTypes.CLEAR_HISTORY }
  }
}
// /action creators

// length: get length of history
function length (history) {
  const { past, future } = history
  return past.length + 1 + future.length
}
// /length

// insert: insert `state` into history, which means adding the current state
//         into `past`, setting the new `state` as `present` and erasing
//         the `future`.
function insert (history, state, limit) {
  debug('insert', {state, history, free: limit - length(history)})

  const { past, present } = history
  const historyOverflow = limit && length(history) >= limit

  if (present === undefined) {
    // init history
    return {
      past: [],
      present: state,
      future: []
    }
  }

  return {
    past: [
      ...past.slice(historyOverflow ? 1 : 0),
      present
    ],
    present: state,
    future: []
  }
}
// /insert

// undo: go back to the previous point in history
function undo (history) {
  debug('undo', {history})

  const { past, present, future } = history

  if (past.length <= 0) return history

  return {
    past: past.slice(0, past.length - 1), // remove last element from past
    present: past[past.length - 1], // set element as new present
    future: [
      present, // old present state is in the future now
      ...future
    ]
  }
}
// /undo

// redo: go to the next point in history
function redo (history) {
  debug('redo', {history})

  const { past, present, future } = history

  if (future.length <= 0) return history

  return {
    future: future.slice(1, future.length), // remove element from future
    present: future[0], // set element as new present
    past: [
      ...past,
      present // old present state is in the past now
    ]
  }
}
// /redo

// jumpToFuture: jump to requested index in future history
function jumpToFuture (history, index) {
  if (index === 0) return redo(history)
  if (index < 0 || index >= history.future.length) return history

  const { past, present, future } = history

  return {
    future: future.slice(index + 1),
    present: future[index],
    past: past.concat([present])
              .concat(future.slice(0, index))
  }
}
// /jumpToFuture

// jumpToPast: jump to requested index in past history
function jumpToPast (history, index) {
  if (index === history.past.length - 1) return undo(history)
  if (index < 0 || index >= history.past.length) return history

  const { past, present, future } = history

  return {
    future: past.slice(index + 1)
                .concat([present])
                .concat(future),
    present: past[index],
    past: past.slice(0, index)
  }
}
// /jumpToPast

// createHistory
function createHistory (state) {
  return {
    past: [],
    present: state,
    future: []
  }
}
// /createHistory

// parseActions
export function parseActions (rawActions, defaultValue = []) {
  if (Array.isArray(rawActions)) {
    return rawActions
  } else if (typeof rawActions === 'string') {
    return [rawActions]
  }
  return defaultValue
}
// /parseActions

// redux-undo higher order reducer
export default function undoable (reducer, rawConfig = {}) {
  __DEBUG__ = rawConfig.debug

  const config = {
    initialState: rawConfig.initialState,
    initTypes: parseActions(rawConfig.initTypes, ['@@redux-undo/INIT']),
    limit: rawConfig.limit,
    filter: rawConfig.filter || (() => true),
    undoType: rawConfig.undoType || ActionTypes.UNDO,
    redoType: rawConfig.redoType || ActionTypes.REDO,
    jumpToPastType: rawConfig.jumpToPastType || ActionTypes.JUMP_TO_PAST,
    jumpToFutureType: rawConfig.jumpToFutureType || ActionTypes.JUMP_TO_FUTURE,
    clearHistoryType: rawConfig.clearHistoryType || ActionTypes.CLEAR_HISTORY
  }
  config.history = rawConfig.initialHistory || createHistory(config.initialState || reducer(undefined, {}))

  if (config.initTypes.length === 0) {
    console.warn('redux-undo: supply at least one action type in initTypes to ensure initial state')
  }

  return (state = config.history, action = {}) => {
    debugStart(action, state)
    let res
    switch (action.type) {
      case undefined:
      case '@@redux/INIT':
        return state

      case config.undoType:
        res = undo(state)
        debug('after undo', res)
        debugEnd()
        return res

      case config.redoType:
        res = redo(state)
        debug('after redo', res)
        debugEnd()
        return res

      case config.jumpToPastType:
        res = jumpToPast(state, action.index)
        debug('after jumpToPast', res)
        debugEnd()
        return res

      case config.jumpToFutureType:
        res = jumpToFuture(state, action.index)
        debug('after jumpToFuture', res)
        debugEnd()
        return res

      case config.clearHistoryType:
        res = createHistory(state.present)
        debug('cleared history', res)
        debugEnd()
        return res

      default:
        res = reducer(state && state.present, action)

        if (config.initTypes.some((actionType) => actionType === action.type)) {
          debug('reset history due to init action')
          debugEnd()
          return config.history
        }

        if (config.filter && typeof config.filter === 'function') {
          if (!config.filter(action, res, state && state.present)) {
            debug('filter prevented action, not storing it')
            debugEnd()
            return {
              ...state,
              present: res
            }
          }
        }

        const updatedHistory = insert(state, res, config.limit)
        debug('after insert', {history: updatedHistory, free: config.limit - length(updatedHistory)})
        debugEnd()
        return updatedHistory
    }
  }
}
// /redux-undo

// distinctState helper
export function distinctState () {
  return (action, currentState, previousState) => currentState !== previousState
}
// /distinctState

// includeAction helper
export function includeAction (rawActions) {
  const actions = parseActions(rawActions)
  return (action) => actions.indexOf(action.type) >= 0
}
// /includeAction

// excludeAction helper
export function excludeAction (rawActions = []) {
  const actions = parseActions(rawActions)
  return (action) => actions.indexOf(action.type) < 0
}
// /excludeAction
<commit_msg>The reducer no longer requires an `initType` to ensure an `initialState`. `state.present` should never become undefined.<commit_after>// debug output
let __DEBUG__
function debug (...args) {
  if (__DEBUG__) {
    if (!console.group) {
      args.unshift('%credux-undo', 'font-style: italic')
    }
    console.log(...args)
  }
}
function debugStart (action, state) {
  if (__DEBUG__) {
    const args = ['action', action.type]
    if (console.group) {
      args.unshift('%credux-undo', 'font-style: italic')
      console.groupCollapsed(...args)
      console.log('received', {state, action})
    } else {
      debug(...args)
    }
  }
}
function debugEnd () {
  if (__DEBUG__) {
    return console.groupEnd && console.groupEnd()
  }
}
// /debug output

// action types
export const ActionTypes = {
  UNDO: '@@redux-undo/UNDO',
  REDO: '@@redux-undo/REDO',
  JUMP_TO_FUTURE: '@@redux-undo/JUMP_TO_FUTURE',
  JUMP_TO_PAST: '@@redux-undo/JUMP_TO_PAST',
  CLEAR_HISTORY: '@@redux-undo/CLEAR_HISTORY'
}
// /action types

// action creators to change the state
export const ActionCreators = {
  undo () {
    return { type: ActionTypes.UNDO }
  },
  redo () {
    return { type: ActionTypes.REDO }
  },
  jumpToFuture (index) {
    return { type: ActionTypes.JUMP_TO_FUTURE, index }
  },
  jumpToPast (index) {
    return { type: ActionTypes.JUMP_TO_PAST, index }
  },
  clearHistory () {
    return { type: ActionTypes.CLEAR_HISTORY }
  }
}
// /action creators

// length: get length of history
function length (history) {
  const { past, future } = history
  return past.length + 1 + future.length
}
// /length

// insert: insert `state` into history, which means adding the current state
//         into `past`, setting the new `state` as `present` and erasing
//         the `future`.
function insert (history, state, limit) {
  debug('insert', {state, history, free: limit - length(history)})

  const { past, present } = history
  const historyOverflow = limit && length(history) >= limit

  return {
    past: [
      ...past.slice(historyOverflow ? 1 : 0),
      present
    ],
    present: state,
    future: []
  }
}
// /insert

// undo: go back to the previous point in history
function undo (history) {
  debug('undo', {history})

  const { past, present, future } = history

  if (past.length <= 0) return history

  return {
    past: past.slice(0, past.length - 1), // remove last element from past
    present: past[past.length - 1], // set element as new present
    future: [
      present, // old present state is in the future now
      ...future
    ]
  }
}
// /undo

// redo: go to the next point in history
function redo (history) {
  debug('redo', {history})

  const { past, present, future } = history

  if (future.length <= 0) return history

  return {
    future: future.slice(1, future.length), // remove element from future
    present: future[0], // set element as new present
    past: [
      ...past,
      present // old present state is in the past now
    ]
  }
}
// /redo

// jumpToFuture: jump to requested index in future history
function jumpToFuture (history, index) {
  if (index === 0) return redo(history)
  if (index < 0 || index >= history.future.length) return history

  const { past, present, future } = history

  return {
    future: future.slice(index + 1),
    present: future[index],
    past: past.concat([present])
              .concat(future.slice(0, index))
  }
}
// /jumpToFuture

// jumpToPast: jump to requested index in past history
function jumpToPast (history, index) {
  if (index === history.past.length - 1) return undo(history)
  if (index < 0 || index >= history.past.length) return history

  const { past, present, future } = history

  return {
    future: past.slice(index + 1)
                .concat([present])
                .concat(future),
    present: past[index],
    past: past.slice(0, index)
  }
}
// /jumpToPast

// createHistory
function createHistory (state) {
  return {
    past: [],
    present: state,
    future: []
  }
}
// /createHistory

// parseActions
export function parseActions (rawActions, defaultValue = []) {
  if (Array.isArray(rawActions)) {
    return rawActions
  } else if (typeof rawActions === 'string') {
    return [rawActions]
  }
  return defaultValue
}
// /parseActions

// redux-undo higher order reducer
export default function undoable (reducer, rawConfig = {}) {
  __DEBUG__ = rawConfig.debug

  const config = {
    initialState: rawConfig.initialState,
    initTypes: parseActions(rawConfig.initTypes, ['@@redux-undo/INIT']),
    limit: rawConfig.limit,
    filter: rawConfig.filter || (() => true),
    undoType: rawConfig.undoType || ActionTypes.UNDO,
    redoType: rawConfig.redoType || ActionTypes.REDO,
    jumpToPastType: rawConfig.jumpToPastType || ActionTypes.JUMP_TO_PAST,
    jumpToFutureType: rawConfig.jumpToFutureType || ActionTypes.JUMP_TO_FUTURE,
    clearHistoryType: rawConfig.clearHistoryType || ActionTypes.CLEAR_HISTORY
  }
  config.history = rawConfig.initialHistory || createHistory(config.initialState || reducer(undefined, {}))

  return (state = config.history, action = {}) => {
    debugStart(action, state)
    let res
    switch (action.type) {
      case undefined:
      case '@@redux/INIT':
        return state

      case config.undoType:
        res = undo(state)
        debug('after undo', res)
        debugEnd()
        return res

      case config.redoType:
        res = redo(state)
        debug('after redo', res)
        debugEnd()
        return res

      case config.jumpToPastType:
        res = jumpToPast(state, action.index)
        debug('after jumpToPast', res)
        debugEnd()
        return res

      case config.jumpToFutureType:
        res = jumpToFuture(state, action.index)
        debug('after jumpToFuture', res)
        debugEnd()
        return res

      case config.clearHistoryType:
        res = createHistory(state.present)
        debug('cleared history', res)
        debugEnd()
        return res

      default:
        res = reducer(state && state.present, action)

        if (config.initTypes.some((actionType) => actionType === action.type)) {
          debug('reset history due to init action')
          debugEnd()
          return config.history
        }

        if (config.filter && typeof config.filter === 'function') {
          if (!config.filter(action, res, state && state.present)) {
            debug('filter prevented action, not storing it')
            debugEnd()
            return {
              ...state,
              present: res
            }
          }
        }

        const updatedHistory = insert(state, res, config.limit)
        debug('after insert', {history: updatedHistory, free: config.limit - length(updatedHistory)})
        debugEnd()
        return updatedHistory
    }
  }
}
// /redux-undo

// distinctState helper
export function distinctState () {
  return (action, currentState, previousState) => currentState !== previousState
}
// /distinctState

// includeAction helper
export function includeAction (rawActions) {
  const actions = parseActions(rawActions)
  return (action) => actions.indexOf(action.type) >= 0
}
// /includeAction

// excludeAction helper
export function excludeAction (rawActions = []) {
  const actions = parseActions(rawActions)
  return (action) => actions.indexOf(action.type) < 0
}
// /excludeAction
<|endoftext|>


<commit_before> /**
 * Creates an instance of Error that aggregates and preserves an innerError.
 */
export function AggregateError(message: string, innerError?: Error, skipIfAlreadyAggregate?: boolean): Error {
  if (innerError) {
    if (innerError.innerError && skipIfAlreadyAggregate) {
      return innerError;
    }

    if (innerError.stack) {
      message += `\n------------------------------------------------\ninner error: ${innerError.stack}`;
    }
  }

  let e = new Error(message);
  if (innerError) {
    e.innerError = innerError;
  }

  return e;
}

 /**
 * Enum specifying the levels of the logger
 *
 * @property logLevel
 * @type Enum
 * @for export
 */
export const logLevel = {
  none: 0,
  error: 1,
  warn: 2,
  info: 3,
  debug: 4
};

let loggers = {};
let currentLevel = logLevel.none;
let appenders = [];
let slice = Array.prototype.slice;
let loggerConstructionKey = {};

function log(logger, level, args) {
  let i = appenders.length;
  let current;

  args = slice.call(args);
  args.unshift(logger);

  while (i--) {
    current = appenders[i];
    current[level].apply(current, args);
  }
}

function debug() {
  if (currentLevel < 4) {
    return;
  }

  log(this, 'debug', arguments);
}

function info() {
  if (currentLevel < 3) {
    return;
  }

  log(this, 'info', arguments);
}

function warn() {
  if (currentLevel < 2) {
    return;
  }

  log(this, 'warn', arguments);
}

function error() {
  if (currentLevel < 1) {
    return;
  }

  log(this, 'error', arguments);
}

function connectLogger(logger) {
  logger.debug = debug;
  logger.info = info;
  logger.warn = warn;
  logger.error = error;
}

function createLogger(id) {
  let logger = new Logger(id, loggerConstructionKey);

  if (appenders.length) {
    connectLogger(logger);
  }

  return logger;
}

/**
 * Gets an instance of a logger by the Id used when creating.
 *
 * @method getLogger
 * @param {string} id The id of the logger you wish to get an instance of.
 * @return {Logger} The instance of the logger, or creates a new logger if none exists for that Id.
 */
export function getLogger(id: string): Logger {
  return loggers[id] || (loggers[id] = createLogger(id));
}

interface Appender {
  debug(logger: Logger, ...rest: any[]): void;
  info(logger: Logger, ...rest: any[]): void;
  warn(logger: Logger, ...rest: any[]): void;
  error(logger: Logger, ...rest: any[]): void;
}

/**
 * Adds an appender capable of processing logs and channeling them to an output.
 *
 * @method addAppender
 * @param {Object} appender An appender instance to begin processing logs with.
 * @for export
 */
export function addAppender(appender: Appender): void {
  appenders.push(appender);

  if (appenders.length === 1) {
    for (let key in loggers) {
      connectLogger(loggers[key]);
    }
  }
}

/**
 * Sets the level of the logging for the application loggers
 *
 * @method setLevel
 * @param {Number} level Matches an enum specifying the level of logging.
 * @for export
 */
export function setLevel(level : number): void {
  currentLevel = level;
}

/**
* The logger is essentially responsible for having log statements that appear during debugging but are squelched
* when using the build tools, depending on the log level that is set.  The available levels are -
* 1. none
* 2. error
* 3. warn
* 4. info
* 5. debug
*
* You cannot instantiate the logger directly - you must use the getLogger method instead.
*
* @class Logger
* @constructor
*/
export class Logger {
  constructor(id: string, key: Object) {
    if (key !== loggerConstructionKey) {
      throw new Error('You cannot instantiate "Logger". Use the "getLogger" API instead.');
    }

    this.id = id;
  }

  /**
   * Logs a debug message.
   *
   * @method debug
   * @param {string} message The message to log
   */
  debug(message: string): void {}

  /**
   * Logs info.
   *
   * @method info
   * @param {string} message The message to log
   */
  info(message: string): void {}

  /**
   * Logs a warning.
   *
   * @method warn
   * @param {string} message The message to log
   */
  warn(message: string): void {}

  /**
   * Logs an error.
   *
   * @method error
   * @param {string} message The message to log
   */
  error(message: string): void {}
}
<commit_msg>fix(all): address more lint issues<commit_after> /**
 * Creates an instance of Error that aggregates and preserves an innerError.
 */
export function AggregateError(message: string, innerError?: Error, skipIfAlreadyAggregate?: boolean): Error {
  if (innerError) {
    if (innerError.innerError && skipIfAlreadyAggregate) {
      return innerError;
    }

    if (innerError.stack) {
      message += `\n------------------------------------------------\ninner error: ${innerError.stack}`;
    }
  }

  let e = new Error(message);
  if (innerError) {
    e.innerError = innerError;
  }

  return e;
}

 /**
 * Enum specifying the levels of the logger
 *
 * @property logLevel
 * @type Enum
 * @for export
 */
export const logLevel = {
  none: 0,
  error: 1,
  warn: 2,
  info: 3,
  debug: 4
};

let loggers = {};
let currentLevel = logLevel.none;
let appenders = [];
let slice = Array.prototype.slice;
let loggerConstructionKey = {};

function log(logger, level, args) {
  let i = appenders.length;
  let current;

  args = slice.call(args);
  args.unshift(logger);

  while (i--) {
    current = appenders[i];
    current[level].apply(current, args);
  }
}

function debug() {
  if (currentLevel < 4) {
    return;
  }

  log(this, 'debug', arguments);
}

function info() {
  if (currentLevel < 3) {
    return;
  }

  log(this, 'info', arguments);
}

function warn() {
  if (currentLevel < 2) {
    return;
  }

  log(this, 'warn', arguments);
}

function error() {
  if (currentLevel < 1) {
    return;
  }

  log(this, 'error', arguments);
}

function connectLogger(logger) {
  logger.debug = debug;
  logger.info = info;
  logger.warn = warn;
  logger.error = error;
}

function createLogger(id) {
  let logger = new Logger(id, loggerConstructionKey);

  if (appenders.length) {
    connectLogger(logger);
  }

  return logger;
}

/**
* Gets an instance of a logger by the Id used when creating.
*
* @method getLogger
* @param {string} id The id of the logger you wish to get an instance of.
* @return {Logger} The instance of the logger, or creates a new logger if none exists for that Id.
*/
export function getLogger(id: string): Logger {
  return loggers[id] || (loggers[id] = createLogger(id));
}

interface Appender {
  debug(logger: Logger, ...rest: any[]): void;
  info(logger: Logger, ...rest: any[]): void;
  warn(logger: Logger, ...rest: any[]): void;
  error(logger: Logger, ...rest: any[]): void;
}

/**
* Adds an appender capable of processing logs and channeling them to an output.
*
* @method addAppender
* @param {Object} appender An appender instance to begin processing logs with.
*/
export function addAppender(appender: Appender): void {
  appenders.push(appender);

  if (appenders.length === 1) {
    for (let key in loggers) {
      connectLogger(loggers[key]);
    }
  }
}

/**
* Sets the level of the logging for the application loggers
*
* @method setLevel
* @param {Number} level Matches an enum specifying the level of logging.
*/
export function setLevel(level : number): void {
  currentLevel = level;
}

/**
* The logger is essentially responsible for having log statements that appear during debugging but are squelched
* when using the build tools, depending on the log level that is set.  The available levels are -
* 1. none
* 2. error
* 3. warn
* 4. info
* 5. debug
*
* You cannot instantiate the logger directly - you must use the getLogger method instead.
*
* @class Logger
* @constructor
*/
export class Logger {
  constructor(id: string, key: Object) {
    if (key !== loggerConstructionKey) {
      throw new Error('You cannot instantiate "Logger". Use the "getLogger" API instead.');
    }

    this.id = id;
  }

  /**
   * Logs a debug message.
   *
   * @method debug
   * @param {string} message The message to log
   */
  debug(message: string): void {}

  /**
   * Logs info.
   *
   * @method info
   * @param {string} message The message to log
   */
  info(message: string): void {}

  /**
   * Logs a warning.
   *
   * @method warn
   * @param {string} message The message to log
   */
  warn(message: string): void {}

  /**
   * Logs an error.
   *
   * @method error
   * @param {string} message The message to log
   */
  error(message: string): void {}
}
<|endoftext|>


<commit_before>const Sequelize = require('sequelize');

function capitalize(string){
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function cloneAttrs(model, attrs, excludeAttrs){

    let clone = {};

    const attributes = model.attributes.filter(attr => !excludeAttrs.some(excludeAttr => excludeAttr === attr));

    for (let p in attributes){

        let nestedClone = {};

        const attribute = attributes[p];

        for (let np in attribute){
            if (attrs.indexOf(np) > -1){
                nestedClone[np] = attribute[np];
            }
        }

        clone[p] = nestedClone;

    }

    return clone;

}

const VersionType = {
    CREATED: 1,
    UPDATED: 2,
    DELETED: 3
}

const defaults = {
    prefix: 'version',
    suffix: '',
    namespace: null,
    sequelize: null,
    exclude: [],
}

const hooks = ['afterCreate', 'afterUpdate', 'afterDestroy'];
const attrsToClone = ['type', 'field'];

function getVersionType(hook){
    if (hook === 'afterCreate') return VersionType.CREATED;
    if (hook === 'afterUpdate') return VersionType.UPDATED;
    if (hook === 'afterDestroy') return VersionType.DELETED;
}

function Version(model, customOptions) {

    const options = Object.assign({}, defaults, Version.defaults, customOptions);

    const prefix = options.prefix;
    const suffix = options.suffix;
    const sequelize = options.sequelize || sequelize;
    const namespace = options.namespace;
    const excludeAttrs = options.exclude;

    const versionModelName = `${capitalize(prefix)}${capitalize(model.name)}`;

    const versionFieldId = `${prefix}_id`;
    const versionFieldType = `${prefix}_type`;
    const versionFieldTimestamp = `${prefix}_timestamp`

    const versionAttrs = {
        [versionFieldId]: {
            type: Sequelize.BIGINT,
            primaryKey: true,
            autoIncrement: true,
        },
        [versionFieldType]: {
            type: Sequelize.INTEGER,
            allowNull: false,
        },
        [versionFieldTimestamp]: {
            type: Sequelize.DATE,
            allowNull: false,
        },
    }

    const cloneModelAttrs = cloneAttrs(model, attrsToClone, excludeAttrs);
    const versionModelAttrs = Object.assign({}, cloneModelAttrs, versionAttrs);
    const tableName = `${prefix.toLowerCase()}_${model.options.tableName || model.name.toLowerCase()}${suffix ? `_${suffix}`:''}`;

    const versionModelOptions = {
        schema: options.schema || model.options.schema,
        tableName,
        timestamps: false,
    }

    const versionModel = model.sequelize.define(versionModelName, versionModelAttrs, versionModelOptions)

    hooks.forEach(hook => {

        model.addHook(hook, (instanceData, {transaction}) => {

            let versionType = getVersionType(hook);

            const data = JSON.parse(JSON.stringify(instanceData));

            const versionData = Object.assign({}, data, {[versionFieldType]: versionType, [versionFieldTimestamp]: new Date()});

            const versionTransaction = namespace ? namespace.get('transaction') : transaction;

            return versionModel.build(versionData).save({transaction : versionTransaction});

        })

    });

    versionModel.addScope('created', {where: {[versionFieldType]: VersionType.CREATED}});
    versionModel.addScope('updated', {where: {[versionFieldType]: VersionType.UPDATED}});
    versionModel.addScope('deleted', {where: {[versionFieldType]: VersionType.DELETED}});

    return versionModel;

}

Version.defaults = defaults;
Version.VersionType = VersionType;

module.exports = Version;
<commit_msg>travis test<commit_after>const Sequelize = require('sequelize');

function capitalize(string){
    return string.charAt(0).toUpperCase() + string.slice(1);
}

function cloneAttrs(model, attrs, excludeAttrs){

    let clone = {};

    const attributes = model.attributes;

    for (let p in attributes){

        if (excludeAttrs.some(p)) continue;

        let nestedClone = {};

        const attribute = attributes[p];

        for (let np in attribute){
            if (attrs.indexOf(np) > -1){
                nestedClone[np] = attribute[np];
            }
        }

        clone[p] = nestedClone;

    }

    return clone;

}

const VersionType = {
    CREATED: 1,
    UPDATED: 2,
    DELETED: 3
}

const defaults = {
    prefix: 'version',
    suffix: '',
    namespace: null,
    sequelize: null,
    exclude: [],
}

const hooks = ['afterCreate', 'afterUpdate', 'afterDestroy'];
const attrsToClone = ['type', 'field'];

function getVersionType(hook){
    if (hook === 'afterCreate') return VersionType.CREATED;
    if (hook === 'afterUpdate') return VersionType.UPDATED;
    if (hook === 'afterDestroy') return VersionType.DELETED;
}

function Version(model, customOptions) {

    const options = Object.assign({}, defaults, Version.defaults, customOptions);

    const prefix = options.prefix;
    const suffix = options.suffix;
    const sequelize = options.sequelize || sequelize;
    const namespace = options.namespace;
    const excludeAttrs = options.exclude;

    const versionModelName = `${capitalize(prefix)}${capitalize(model.name)}`;

    const versionFieldId = `${prefix}_id`;
    const versionFieldType = `${prefix}_type`;
    const versionFieldTimestamp = `${prefix}_timestamp`

    const versionAttrs = {
        [versionFieldId]: {
            type: Sequelize.BIGINT,
            primaryKey: true,
            autoIncrement: true,
        },
        [versionFieldType]: {
            type: Sequelize.INTEGER,
            allowNull: false,
        },
        [versionFieldTimestamp]: {
            type: Sequelize.DATE,
            allowNull: false,
        },
    }

    const cloneModelAttrs = cloneAttrs(model, attrsToClone, excludeAttrs);
    const versionModelAttrs = Object.assign({}, cloneModelAttrs, versionAttrs);
    const tableName = `${prefix.toLowerCase()}_${model.options.tableName || model.name.toLowerCase()}${suffix ? `_${suffix}`:''}`;

    const versionModelOptions = {
        schema: options.schema || model.options.schema,
        tableName,
        timestamps: false,
    }

    const versionModel = model.sequelize.define(versionModelName, versionModelAttrs, versionModelOptions)

    hooks.forEach(hook => {

        model.addHook(hook, (instanceData, {transaction}) => {

            let versionType = getVersionType(hook);

            const data = JSON.parse(JSON.stringify(instanceData));

            const versionData = Object.assign({}, data, {[versionFieldType]: versionType, [versionFieldTimestamp]: new Date()});

            const versionTransaction = namespace ? namespace.get('transaction') : transaction;

            return versionModel.build(versionData).save({transaction : versionTransaction});

        })

    });

    versionModel.addScope('created', {where: {[versionFieldType]: VersionType.CREATED}});
    versionModel.addScope('updated', {where: {[versionFieldType]: VersionType.UPDATED}});
    versionModel.addScope('deleted', {where: {[versionFieldType]: VersionType.DELETED}});

    return versionModel;

}

Version.defaults = defaults;
Version.VersionType = VersionType;

module.exports = Version;
<|endoftext|>


<commit_before>var Hash = require("hash"),
    utils = require("utils");

var SPLITER = /[ ,]+/,
    REPLACER = /.*[\.\/\\]/,
    TYPE_REPLACER = /;[(.*)\S\s]+/;

function MimeType(type, exts) {
    this.type = type;
    this.exts = utils.isArray(exts) ? exts : (utils.isString(exts) ? exts.split(SPLITER) : []);
    this.ext = this.exts[0];
}

MimeType.prototype.toJSON = function(json) {
    json || (json = {});
    var exts = this.exts,
        jsonExts = json.exts || (json.exts = []),
        i, il;

    if (jsonExts.length) jsonExts.length = 0;
    for (i = 0, il = exts.length; i < il; i++) jsonExts.push(exts[i]);

    json.type = this.type;
    json.ext = this.ext;

    return json;
};

MimeType.prototype.fromJSON = function(json) {
    var exts = this.exts,
        jsonExts = json.exts || (json.exts = []),
        i, il;

    if (exts.length) exts.length = 0;
    for (i = 0, il = jsonExts.length; i < il; i++) exts.push(jsonExts[i]);

    this.type = json.type;
    this.ext = json.ext;

    return this;
};

function Mime() {

    this.types = new Hash("ext");
    this.extensions = new Hash("type");

    this.defaultType = "text/plain";
    this.defaultExtension = "txt";

    this.defaults();
}

Mime.prototype.defaults = function() {
    if (this.types.length > 0 || this.extensions.length > 0) this.clear();

    this.register("*/*", "*");

    this.register("text/html", "html htm xhtml");
    this.register("text/plain", "txt");
    this.register("application/javascript", "js");
    this.register("text/css", "css");

    this.register("image/svg+xml", "svg");
    this.register("application/x-font-ttf", "ttf");
    this.register("application/x-font-opentype", "otf");
    this.register("application/font-woff", "woff");
    this.register("application/vnd.ms-fontobject", "eot");

    this.register("image/png", "png");
    this.register("image/jpeg", "jpeg jpg");
    this.register("image/gif", "gif");
    this.register("image/bmp", "bmp");
    this.register("image/x-icon", "ico");
    this.register("image/tiff", "tiff tif");

    this.register("video/mpeg", "mpg mpeg mpe");
    this.register("video/ogg", "ogv ogg");
    this.register("video/mp4", "mp4");

    this.register("audio/wav", "wav");
    this.register("audio/mpeg", "mpg mpeg");
    this.register("audio/ogg", "oga ogg");
    this.register("audio/mp3", "mp3");
    this.register("audio/mp4", "mp4");

    this.register("application/xml", "xml");

    this.register("application/json", "json");
    this.register("application/pdf", "pdf");
    this.register("application/zip", "zip");

    return this;
};

Mime.prototype.clear = function() {

    this.defaultType = "text/plain";
    this.defaultExtension = "txt";

    this.types.clear();
    this.extensions.clear();

    return this;
};

Mime.prototype.register = function(type, exts) {
    var mimeType = new MimeType(type, exts);

    this.types.push(mimeType);
    this.extensions.push(mimeType);

    return this;
};

Mime.prototype.unregister = function(exts) {
    exts = exts.split(SPLITER);
    var extensions = this.extensions,
        i = exts.length;

    while (i--) {
        if ((mimeType = extensions.get(exts[i]))) break;
    }

    if (mimeType) {
        extensions.remove(mimeType);
        this.types.remove(mimeType);
    }

    return this;
};

Mime.prototype.unregisterType = function(type) {
    var types = this.types,
        mimeType = types.get(type);

    if (mimeType) {
        types.remove(mimeType);
        this.extensions.remove(mimeType);
    }

    return this;
};

Mime.prototype.lookUp = function(path, fallback) {
    var ext = path.replace(REPLACER, "").toLowerCase(),
        types = this.types,
        mimeType = this.types.get(ext),
        testMimeType, exts, i, j, jl;

    if (!mimeType) {
        for (i = types.length; i--;) {
            testMimeType = types[i];
            exts = testMimeType.exts;

            for (j = 0, jl = exts.length; j < jl; j++) {
                if (ext === exts[j]) {
                    mimeType = testMimeType;
                    break;
                }
            }
            if (mimeType) break;
        }
    }
    if (!mimeType && fallback === false) return null;

    return (mimeType && mimeType.type) || fallback || this.defaultType;
};

Mime.prototype.lookUpType = function(ext, fallback) {
    ext = ext.replace(REPLACER, "").toLowerCase();
    var mimeType = this.types.get(ext);
    if (!mimeType && fallback === false) return null;

    return (mimeType && mimeType.type) || fallback || this.defaultType;
};

Mime.prototype.lookUpExt = function(type, fallback) {
    type = type.replace(TYPE_REPLACER, "").toLowerCase();
    var mimeType = this.extensions.get(type);
    if (!mimeType && fallback === false) return null;

    return (mimeType && mimeType.ext) || fallback || this.defaultType;
};

Mime.prototype.toJSON = function(json) {
    json || (json = {});
    var types = this.types,
        extensions = this.extensions,
        jsonTypes = json.types || (json.types = []),
        jsonExtensions = json.extensions || (json.extensions = []),
        i, il;

    for (i = 0, il = types.length; i < il; i++) jsonTypes[i] = types[i].toJSON(jsonTypes[i]);
    for (i = 0, il = extensions.length; i < il; i++) jsonExtensions[i] = extensions[i].toJSON(jsonExtensions[i]);

    json.defaultType = this.defaultType;
    json.defaultExtension = this.defaultExtension;

    return json;
};

Mime.prototype.fromJSON = function(json) {
    var types = this.types,
        extensions = this.extensions,
        jsonTypes = json.types,
        jsonExtensions = json.extensions,
        type, extension,
        i, il;

    for (i = 0, il = jsonTypes.length; i < il; i++) {
        if ((type = types[i])) {
            type.fromJSON(jsonTypes[i]);
        } else {
            types.push(new MimeType().fromJSON(jsonTypes[i]));
        }
    }
    for (i = 0, il = jsonExtensions.length; i < il; i++) {
        if ((extension = extensions[i])) {
            extension.fromJSON(jsonExtensions[i]);
        } else {
            extensions.push(new MimeType().fromJSON(jsonExtensions[i]));
        }
    }

    this.defaultType = json.defaultType;
    this.defaultExtension = json.defaultExtension;

    return this;
};

module.exports = new Mime();
<commit_msg>add setLength method<commit_after>var Hash = require("hash"),
    utils = require("utils");

var SPLITER = /[ ,]+/,
    REPLACER = /.*[\.\/\\]/,
    TYPE_REPLACER = /;[(.*)\S\s]+/;

function MimeType(types, exts) {
    this.types = utils.isArray(types) ? types : (utils.isString(types) ? types.split(SPLITER) : []);
    this.type = this.types[0];
    this.exts = utils.isArray(exts) ? exts : (utils.isString(exts) ? exts.split(SPLITER) : []);
    this.ext = this.exts[0];

    console.log(this);
}

MimeType.prototype.toJSON = function(json) {
    json || (json = {});
    var exts = this.exts,
        types = this.types,
        jsonExts = json.exts || (json.exts = []),
        jsonTypes = json.types || (json.types = []),
        i, il;

    if (jsonExts.length) jsonExts.length = 0;
    for (i = 0, il = exts.length; i < il; i++) jsonExts.push(exts[i]);

    if (jsonTypes.length) jsonTypes.length = 0;
    for (i = 0, il = types.length; i < il; i++) jsonTypess.push(types[i]);

    json.type = this.type;
    json.ext = this.ext;

    return json;
};

MimeType.prototype.fromJSON = function(json) {
    var exts = this.exts,
        types = this.types,
        jsonExts = json.exts || (json.exts = []),
        jsonTypes = json.types || (json.types = []),
        i, il;

    if (types.length) types.length = 0;
    for (i = 0, il = jsonTypes.length; i < il; i++) types.push(jsonTypes[i]);

    if (exts.length) exts.length = 0;
    for (i = 0, il = jsonExts.length; i < il; i++) exts.push(jsonExts[i]);

    this.type = json.type;
    this.ext = json.ext;

    return this;
};

function Mime() {

    this.types = new Hash("ext");
    this.extensions = new Hash("type");

    this.defaultType = "text/plain";
    this.defaultExtension = "txt";

    this.defaults();
}

Mime.prototype.defaults = function() {
    if (this.types.length > 0 || this.extensions.length > 0) this.clear();

    this.register("*/*", "*");

    this.register("text/html", "html htm xhtml");
    this.register("text/plain", "txt");
    this.register("application/javascript", "js");
    this.register("text/css", "css");

    this.register("image/svg+xml", "svg");
    this.register("application/x-font-ttf application/x-font-truetype", "ttf");
    this.register("application/x-font-opentype", "otf");
    this.register("application/font-woff", "woff wof");
    this.register("application/vnd.ms-fontobject", "eot");

    this.register("image/png", "png");
    this.register("image/jpeg", "jpeg jpg");
    this.register("image/gif", "gif");
    this.register("image/bmp", "bmp");
    this.register("image/x-icon", "ico");
    this.register("image/tiff", "tiff tif");

    this.register("video/mpeg", "mpg mpeg mpe");
    this.register("video/ogg", "ogv ogg");
    this.register("video/mp4", "mp4");

    this.register("audio/wav", "wav");
    this.register("audio/mpeg", "mpg mpeg");
    this.register("audio/ogg", "oga ogg");
    this.register("audio/mp3", "mp3");
    this.register("audio/mp4", "mp4");

    this.register("application/xml", "xml");

    this.register("application/json", "json");
    this.register("application/pdf", "pdf");
    this.register("application/zip", "zip");

    return this;
};

Mime.prototype.clear = function() {

    this.defaultType = "text/plain";
    this.defaultExtension = "txt";

    this.types.clear();
    this.extensions.clear();

    return this;
};

Mime.prototype.register = function(types, exts) {
    var mimeType = new MimeType(types, exts);

    this.types.push(mimeType);
    this.extensions.push(mimeType);

    return this;
};

Mime.prototype.unregister = function(exts) {
    exts = utils.isArray(exts) ? exts : (utils.isString(exts) ? exts.split(SPLITER) : []);
    var extensions = this.extensions,
        i = exts.length;

    while (i--) {
        if ((mimeType = extensions.get(exts[i]))) break;
    }

    if (mimeType) {
        extensions.remove(mimeType);
        this.types.remove(mimeType);
    }

    return this;
};

Mime.prototype.unregisterType = function(types) {
    types = utils.isArray(types) ? types : (utils.isString(types) ? types.split(SPLITER) : []);
    var thisTypes = this.thisTypes,
        i = types.length;

    while (i--) {
        if ((mimeType = thisTypes.get(types[i]))) break;
    }

    if (mimeType) {
        thisTypes.remove(mimeType);
        this.extensions.remove(mimeType);
    }

    return this;
};

Mime.prototype.lookUp = function(path, fallback) {
    var ext = path.replace(REPLACER, "").toLowerCase(),
        types = this.types,
        mimeType = this.types.get(ext),
        testMimeType, exts, i, j, jl;

    if (!mimeType) {
        for (i = types.length; i--;) {
            testMimeType = types[i];
            exts = testMimeType.exts;

            for (j = 0, jl = exts.length; j < jl; j++) {
                if (ext === exts[j]) {
                    mimeType = testMimeType;
                    break;
                }
            }
            if (mimeType) break;
        }
    }
    if (!mimeType && fallback === false) return null;

    return (mimeType && mimeType.type) || fallback || this.defaultType;
};

Mime.prototype.lookUpType = function(ext, fallback) {
    ext = ext.replace(REPLACER, "").toLowerCase();
    var mimeType = this.types.get(ext);
    if (!mimeType && fallback === false) return null;

    return (mimeType && mimeType.type) || fallback || this.defaultType;
};

Mime.prototype.lookUpExt = function(type, fallback) {
    type = type.replace(TYPE_REPLACER, "").toLowerCase();
    var mimeType = this.extensions.get(type);
    if (!mimeType && fallback === false) return null;

    return (mimeType && mimeType.ext) || fallback || this.defaultType;
};

Mime.prototype.toJSON = function(json) {
    json || (json = {});
    var types = this.types,
        extensions = this.extensions,
        jsonTypes = json.types || (json.types = []),
        jsonExtensions = json.extensions || (json.extensions = []),
        i, il;

    for (i = 0, il = types.length; i < il; i++) jsonTypes[i] = types[i].toJSON(jsonTypes[i]);
    for (i = 0, il = extensions.length; i < il; i++) jsonExtensions[i] = extensions[i].toJSON(jsonExtensions[i]);

    json.defaultType = this.defaultType;
    json.defaultExtension = this.defaultExtension;

    return json;
};

Mime.prototype.fromJSON = function(json) {
    var types = this.types,
        extensions = this.extensions,
        jsonTypes = json.types,
        jsonExtensions = json.extensions,
        type, extension,
        i, il;

    types.setLength(jsonTypes.length);
    for (i = 0, il = jsonTypes.length; i < il; i++) {
        if ((type = types[i])) {
            type.fromJSON(jsonTypes[i]);
        } else {
            types.push(new MimeType().fromJSON(jsonTypes[i]));
        }
    }
    extension.setLength(jsonExtensions.length);
    for (i = 0, il = jsonExtensions.length; i < il; i++) {
        if ((extension = extensions[i])) {
            extension.fromJSON(jsonExtensions[i]);
        } else {
            extensions.push(new MimeType().fromJSON(jsonExtensions[i]));
        }
    }

    this.defaultType = json.defaultType;
    this.defaultExtension = json.defaultExtension;

    return this;
};

module.exports = new Mime();
<|endoftext|>


<commit_before>'use strict'

const path      = require('path')
const fs        = require('fs')
const denodeify = require('denodeify')
const rename    = require('rename-extension')
const ejs       = require('./ejs')

/*
 * Load EJS and transform to HTML.
 * @public
 * @param {string} filePath - Absolute path to the requested file.
 * @param {string} srcPath - Absolute path to the source folder.
 * @param {string} distPath - Absolute path to the export folder.
 * @param {Object} route - The route which matched the request URL.
 * @param {function} next - The callback that handles the response. Receives the following properties: err, result, savePath.
 */
module.exports = function(filePath, srcPath, distPath, route, next) {

	let savePath     = null
	let dataPath     = null
	let relativePath = null

	let data = null

	Promise.resolve()

	// Prepare file paths
	.then(() => {

		filePath     = rename(filePath, 'ejs')
		savePath     = rename(filePath.replace(srcPath, distPath), 'html')
		dataPath     = path.resolve(process.cwd(), './data.json')
		relativePath = path.relative(srcPath, filePath)

	})

	// Get the contents of the ejs data
	.then(() => denodeify(fs.readFile)(dataPath, 'utf8'))

	// Process ejs data
	.then((dataStr) => {

		const current     = path.parse(relativePath)
		const environment = process.env

		const dataJSON    = JSON.parse(dataStr)
		const globalData  = dataJSON['*'] || {}
		const pageData    = dataJSON[current.name] || {}

		data = Object.assign({}, globalData, pageData, {
			current,
			environment
		})

	})

	// Get the contents of the file
	.then(() => denodeify(fs.readFile)(filePath, 'utf8'))

	// Process file
	.then((str) => ejs(filePath, str, data))

	// Return processed data and catch errors
	// Avoid .catch as we don't want to catch errors of the callback
	.then(
		(str) => next(null, str, savePath),
		(err) => next(err, null, null)
	)

}

/**
 * Attach an array to the function, which contains a list of
 * extensions used by the handler. The array will be used by Rosid for caching purposes.
 */
module.exports.cache = [
	'.ejs',
	'.json'
]<commit_msg>Code syntax adjustments<commit_after>'use strict'

const path      = require('path')
const fs        = require('fs')
const denodeify = require('denodeify')
const rename    = require('rename-extension')
const ejs       = require('./ejs')

/*
 * Load EJS and transform to HTML.
 * @public
 * @param {string} filePath - Absolute path to the requested file.
 * @param {string} srcPath - Absolute path to the source folder.
 * @param {string} distPath - Absolute path to the export folder.
 * @param {Object} route - The route which matched the request URL.
 * @param {function} next - The callback that handles the response. Receives the following properties: err, result, savePath.
 */
module.exports = function(filePath, srcPath, distPath, route, next) {

	let savePath     = null
	let dataPath     = null
	let relativePath = null

	let data = null

	Promise.resolve().then(() => {

		// Prepare file paths

		filePath     = rename(filePath, 'ejs')
		savePath     = rename(filePath.replace(srcPath, distPath), 'html')
		dataPath     = path.resolve(process.cwd(), './data.json')
		relativePath = path.relative(srcPath, filePath)

	}).then(() => {

		// Get the contents of the ejs data

		return denodeify(fs.readFile)(dataPath, 'utf8')

	}).then((dataStr) => {

		// Process ejs data

		const current     = path.parse(relativePath)
		const environment = process.env

		const dataJSON    = JSON.parse(dataStr)
		const globalData  = dataJSON['*'] || {}
		const pageData    = dataJSON[current.name] || {}

		data = Object.assign({}, globalData, pageData, {
			current,
			environment
		})

	}).then(() => {

		// Get the contents of the file

		return denodeify(fs.readFile)(filePath, 'utf8')

	}).then((str) => {

		// Process file

		return ejs(filePath, str, data)

	}).then(

		// Return processed data and catch errors
		// Avoid .catch as we don't want to catch errors of the callback

		(str) => next(null, str, savePath),
		(err) => next(err, null, null)

	)

}

/**
 * Attach an array to the function, which contains a list of
 * extensions used by the handler. The array will be used by Rosid for caching purposes.
 */
module.exports.cache = [
	'.ejs',
	'.json'
]<|endoftext|>