<commit_before>'use strict';

var JSON5 = require('json5');

/**
 * Helper function. Search for and replace substrings in a greater string without using RegExp.
 *
 * @private
 * @param {string} string - the greater string.
 * @param {string} substring - the substring to be searched for.
 * @param {string} replacement - the search replacement.
 * @returns {string} JS object.
 */
function replaceSubstringGlobally(string, substring, replacement) {
  var i = 0;
  var l = string.length;
  var stringNew = '';

  while (i < l) {
    var substringLength = substring.length;

    if (string.substr(i, substringLength) === substring) {
      stringNew += replacement;
      i += substringLength;
    }
    else {
      stringNew += string[i];
      i++;
    }
  }

  return stringNew;
}

/**
 * Main function for export.
 * The recommended syntax for the submitted string is that it be valid JSON5.
 * However, this function was expressly created to parse the quoting and special character escaping scheme employed by
 * Pattern Lab for PHP.
 *
 * The steps on a high-level are as follows:
 *   * First, try to parse the string with JSON5.
 *   * If that fails, traverse the submitted string, modifying the data in the following manner:
 *   * Further escape all escaped quotes and colons. Use the string representation of their unicodes for this. This has
 *     the added bonus of being interpreted correctly by JSON5.parse() without further modification. This will be useful
 *     later in the function.
 *   * Once escaped quotes are out of the way, we know the remaining quotes are either key/value wrappers or wrapped
 *     within those wrappers. We know that remaining commas and colons are either delimiters, or wrapped within quotes
 *     to not be recognized as such.
 *   * A do-while loop traverses processString to write keys to a keys array and values to a values array.
 *   * Start by parsing the first key. Determine the type of wrapping quote, if any.
 *   * By knowing the open wrapper, we know that the next quote of that kind (if the key is wrapped in quotes) HAS to
 *     be the close wrapper. Similarly, if the key is unwrapped, we know the next colon HAS to be the delimiter between
 *     key and value.
 *   * Save the key to the keys array.
 *   * Next, search for a value. It will either be the next block wrapped in quotes, or a string of alphanumerics,
 *     decimal points, or minus signs.
 *   * Save the value to the values array.
 *   * The do-while loop truncates the processString value while parsing. Its condition for completion is when the
 *     processString is whittled down to an empty string.
 *   * After the keys and values arrays are built, a for loop iterates through them to build the final jsonString.
 *   * No quote substitution had been done prior to this loop. In this loop, all keys are ensured to be wrapped in
 *     double-quotes. String values are also ensured to be wrapped in double-quotes.
 *   * Unescape escaped unicodes except for double-quotes. Everything beside double-quotes will be wrapped in
 *     double-quotes without need for escape.
 *   * Return jsonString.
 *
 * @param {string} jsonLikeString - a JSON-like string.
 * @returns {object} JS object.
 */
function jsonEval(jsonLikeString) {
  var colonPos = -1;
  var i;
  var keys = [];
  var l;
  var processString = jsonLikeString.trim();
  var jsObject;
  var jsonString;
  var quote = null;
  var quotePos = -1;
  var values = [];
  var wrapper;

  // First, try parsing valid JSON5.
  try {
    jsObject = JSON5.parse(processString);
  }

  // Else, traverse string to massage it into valid JSON5.
  catch (err) {
    // Replace all escaped double-quotes with escaped unicode.
    processString = replaceSubstringGlobally(processString, '\\"', '\\u0022');

    // Replace all escaped single-quotes with escaped unicode.
    processString = replaceSubstringGlobally(processString, '\\\'', '\\u0027');

    // Replace all escaped colons with escaped unicode.
    processString = replaceSubstringGlobally(processString, '\\:', '\\u0058');

    // With escaped chars out of the way, traverse processString looking for keys and values.
    do {
      // Check if searching for a key.
      if (processString[0] === '{' || processString[0] === ',') {
        processString = processString.substring(1, processString.length).trim();

        // Search for an end quote if wrapped in quotes. Else search for a colon. Everything up to that position will be
        // saved in the keys array.
        switch (processString[0]) {
          // Determine the quote type of the opening quote.
          case '"':
          case '\'':
            wrapper = processString[0];
            // Find the position of the closing quote.
            quotePos = processString.indexOf(wrapper, 1);

            break;

          default:
            colonPos = processString.indexOf(':');
        }

        if (quotePos > -1) {
          keys.push(processString.substring(0, quotePos + 1).trim());

          // Truncate the beginning from processString and look for a value.
          processString = processString.substring(quotePos + 1, processString.length).trim();

          // Unset quotePos.
          quotePos = -1;
        }
        else if (colonPos > -1) {
          keys.push(processString.substring(0, colonPos).trim());

          // Truncate the beginning from processString and look for a value.
          processString = processString.substring(colonPos, processString.length);

          // Unset colonPos.
          colonPos = -1;

        // If there are no more colons, and we're looking for a key, probably a problem. Stop further processing.
        }
        else {
          /* istanbul ignore next */
          processString = '';
          /* istanbul ignore next */
          break;
        }
      }

      // Now, search for a value.
      if (processString[0] === ':') {
        processString = processString.substring(1, processString.length).trim();

        switch (processString[0]) {
          // Since a quote of the same type as its wrappers would be escaped, and we escaped those even further with
          // their unicodes, it is safe to look for wrapper pairs and conclude that their contents are values.
          case '"':
            quote = '"';

            break;
          case '\'':
            quote = '\'';

            break;

          default:
            quote = null;
        }

        var valueStop = 0;
        l = processString.length;

        switch (quote) {
          case '"':
          case '\'':
            for (i = 1; i < l; i++) {
              if (processString[i] === quote) {
                valueStop = i + 1;

                break;
              }
            }

            break;

          default:
            for (i = 0; i < l; i++) {
              // If the value is not wrapped in quotes, check that each following character is an alphanumeric, decimal
              // point, or plus or minus sign for exponential notation.
              // eslint-disable-next-line no-useless-escape
              if (!/[\w\.\+\-]/.test(processString[i])) {
                valueStop = i;

                break;
              }
            }
        }

        for (i = valueStop; i < l; i++) {
          if (/\s/.test(processString[i])) {
            valueStop++;
          }
          else {
            break;
          }
        }

        // Add this slice to values.
        values.push(processString.slice(0, valueStop).trim());

        // Truncate the beginning from processString and continue either looking for a
        // key, or returning.
        processString = processString.slice(valueStop);

        // Exit do while if the final char is '}'
        if (processString === '}') {
          processString = '';

          break;
        }

      // If there are no more colons, and we're looking for a value, probably a problem. Stop further processing.
      }
      else {
        /* istanbul ignore next */
        processString = '';
        /* istanbul ignore next */
        break;
      }
    } while (processString);

    // Build jsonString string for JSON parsing.
    jsonString = '{';
    l = keys.length;

    for (i = 0; i < l; i++) {
      // Keys.
      // Replace single-quote wrappers with double-quotes.
      if (keys[i][0] === '\'' && keys[i][keys[i].length - 1] === '\'') {
        jsonString += '"';
        // Any enclosed double-quotes must be escaped.
        jsonString += replaceSubstringGlobally(keys[i].substring(1, keys[i].length - 1), '"', '\\"');
        jsonString += '"';
      }
      else {
        // Open wrap with double-quotes if no wrapper.
        if (keys[i][0] !== '"' && keys[i][0] !== '\'') {
          jsonString += '"';

          // The following line is to clean up vestiges from Pattern Lab PHP's escaping scheme.
          // F.Y.I. Pattern Lab PHP would allow special characters like question marks in parameter keys so long as the
          // key was unwrapped and the special character escaped with a backslash. In Node, we need to wrap those keys
          // and unescape those characters.
          keys[i] = replaceSubstringGlobally(keys[i], '\\', '');
        }

        jsonString += keys[i];

        // Close wrap with double-quotes if no wrapper.
        if (keys[i][keys[i].length - 1] !== '"' && keys[i][keys[i].length - 1] !== '\'') {
          jsonString += '"';
        }
      }

      // Colon delimiter.
      jsonString += ':';

      // Values.
      // Replace single-quote wrappers with double-quotes.
      if (values[i][0] === '\'' && values[i][values[i].length - 1] === '\'') {
        jsonString += '"';
        // Any enclosed double-quotes must be escaped.
        jsonString += replaceSubstringGlobally(values[i].substring(1, values[i].length - 1), '"', '\\"');
        jsonString += '"';
      }
      // For everything else, just add the value however it's wrapped.
      else {
        jsonString += values[i];
      }

      // Comma delimiter.
      if (i < keys.length - 1) {
        jsonString += ',';
      }
    }

    jsonString += '}';
    // Unescape escaped unicode except for double-quotes.
    jsonString = replaceSubstringGlobally(jsonString, '\\u0027', '\'');
    jsonString = replaceSubstringGlobally(jsonString, '\\u0058', ':');

    try {
      jsObject = JSON5.parse(jsonString);
    }
    catch (err1) {
      /* istanbul ignore next */
      console.error('\n' + err.message); // eslint-disable-line no-console
    }
  }

  return jsObject;
}

if (typeof define === 'function') {
  define(function () {
    return jsonEval;
  });
}
/* istanbul ignore next */
else if (typeof window === 'object') {
  /* istanbul ignore next */
  window.jsonEval = jsonEval;
}

module.exports = jsonEval;
<commit_msg>improved coverage<commit_after>'use strict';

var JSON5 = require('json5');

/**
 * Helper function. Search for and replace substrings in a greater string without using RegExp.
 *
 * @private
 * @param {string} string - the greater string.
 * @param {string} substring - the substring to be searched for.
 * @param {string} replacement - the search replacement.
 * @returns {string} JS object.
 */
function replaceSubstringGlobally(string, substring, replacement) {
  var i = 0;
  var l = string.length;
  var stringNew = '';

  while (i < l) {
    var substringLength = substring.length;

    if (string.substr(i, substringLength) === substring) {
      stringNew += replacement;
      i += substringLength;
    }
    else {
      stringNew += string[i];
      i++;
    }
  }

  return stringNew;
}

/**
 * Main function for export.
 * The recommended syntax for the submitted string is that it be valid JSON5.
 * However, this function was expressly created to parse the quoting and special character escaping scheme employed by
 * Pattern Lab for PHP.
 *
 * The steps on a high-level are as follows:
 *   * First, try to parse the string with JSON5.
 *   * If that fails, traverse the submitted string, modifying the data in the following manner:
 *   * Further escape all escaped quotes and colons. Use the string representation of their unicodes for this. This has
 *     the added bonus of being interpreted correctly by JSON5.parse() without further modification. This will be useful
 *     later in the function.
 *   * Once escaped quotes are out of the way, we know the remaining quotes are either key/value wrappers or wrapped
 *     within those wrappers. We know that remaining commas and colons are either delimiters, or wrapped within quotes
 *     to not be recognized as such.
 *   * A do-while loop traverses processString to write keys to a keys array and values to a values array.
 *   * Start by parsing the first key. Determine the type of wrapping quote, if any.
 *   * By knowing the open wrapper, we know that the next quote of that kind (if the key is wrapped in quotes) HAS to
 *     be the close wrapper. Similarly, if the key is unwrapped, we know the next colon HAS to be the delimiter between
 *     key and value.
 *   * Save the key to the keys array.
 *   * Next, search for a value. It will either be the next block wrapped in quotes, or a string of alphanumerics,
 *     decimal points, or minus signs.
 *   * Save the value to the values array.
 *   * The do-while loop truncates the processString value while parsing. Its condition for completion is when the
 *     processString is whittled down to an empty string.
 *   * After the keys and values arrays are built, a for loop iterates through them to build the final jsonString.
 *   * No quote substitution had been done prior to this loop. In this loop, all keys are ensured to be wrapped in
 *     double-quotes. String values are also ensured to be wrapped in double-quotes.
 *   * Unescape escaped unicodes except for double-quotes. Everything beside double-quotes will be wrapped in
 *     double-quotes without need for escape.
 *   * Return jsonString.
 *
 * @param {string} jsonLikeString - a JSON-like string.
 * @returns {object} JS object.
 */
function jsonEval(jsonLikeString) {
  var colonPos = -1;
  var i;
  var keys = [];
  var l;
  var processString = jsonLikeString.trim();
  var jsObject;
  var jsonString;
  var quote = null;
  var quotePos = -1;
  var values = [];
  var wrapper;

  // First, try parsing valid JSON5.
  try {
    jsObject = JSON5.parse(processString);
  }

  // Else, traverse string to massage it into valid JSON5.
  catch (err) {
    // Replace all escaped double-quotes with escaped unicode.
    processString = replaceSubstringGlobally(processString, '\\"', '\\u0022');

    // Replace all escaped single-quotes with escaped unicode.
    processString = replaceSubstringGlobally(processString, '\\\'', '\\u0027');

    // Replace all escaped colons with escaped unicode.
    processString = replaceSubstringGlobally(processString, '\\:', '\\u0058');

    // With escaped chars out of the way, traverse processString looking for keys and values.
    do {
      // Check if searching for a key.
      if (processString[0] === '{' || processString[0] === ',') {
        processString = processString.substring(1, processString.length).trim();

        // Search for an end quote if wrapped in quotes. Else search for a colon. Everything up to that position will be
        // saved in the keys array.
        switch (processString[0]) {
          // Determine the quote type of the opening quote.
          case '"':
          case '\'':
            wrapper = processString[0];
            // Find the position of the closing quote.
            quotePos = processString.indexOf(wrapper, 1);

            break;

          default:
            colonPos = processString.indexOf(':');
        }

        if (quotePos > -1) {
          keys.push(processString.substring(0, quotePos + 1).trim());

          // Truncate the beginning from processString and look for a value.
          processString = processString.substring(quotePos + 1, processString.length).trim();

          // Unset quotePos.
          quotePos = -1;
        }
        else if (colonPos > -1) {
          keys.push(processString.substring(0, colonPos).trim());

          // Truncate the beginning from processString and look for a value.
          processString = processString.substring(colonPos, processString.length);

          // Unset colonPos.
          colonPos = -1;

        // If there are no more colons, and we're looking for a key, probably a problem. Stop further processing.
        }
        else {
          /* istanbul ignore next */
          processString = '';
          /* istanbul ignore next */
          break;
        }
      }

      // Now, search for a value.
      if (processString[0] === ':') {
        processString = processString.substring(1, processString.length).trim();

        switch (processString[0]) {
          // Since a quote of the same type as its wrappers would be escaped, and we escaped those even further with
          // their unicodes, it is safe to look for wrapper pairs and conclude that their contents are values.
          case '"':
            quote = '"';

            break;
          case '\'':
            quote = '\'';

            break;

          default:
            quote = null;
        }

        var valueStop = 0;
        l = processString.length;

        switch (quote) {
          case '"':
          case '\'':
            for (i = 1; i < l; i++) {
              if (processString[i] === quote) {
                valueStop = i + 1;

                break;
              }
            }

            break;

          default:
            for (i = 0; i < l; i++) {
              // If the value is not wrapped in quotes, check that each following character is an alphanumeric, decimal
              // point, or plus or minus sign for exponential notation.
              // eslint-disable-next-line no-useless-escape
              if (!/[\w\.\+\-]/.test(processString[i])) {
                valueStop = i;

                break;
              }
            }
        }

        for (i = valueStop; i < l; i++) {
          if (/\s/.test(processString[i])) {
            valueStop++;
          }
          else {
            break;
          }
        }

        // Add this slice to values.
        values.push(processString.slice(0, valueStop).trim());

        // Truncate the beginning from processString and continue either looking for a
        // key, or returning.
        processString = processString.slice(valueStop);

        // Exit do while if the final char is '}'
        if (processString === '}') {
          processString = '';

          break;
        }

      // If there are no more colons, and we're looking for a value, probably a problem. Stop further processing.
      }
      else {
        /* istanbul ignore next */
        processString = '';
        /* istanbul ignore next */
        break;
      }
    } while (processString);

    // Build jsonString string for JSON parsing.
    jsonString = '{';
    l = keys.length;

    for (i = 0; i < l; i++) {
      // Keys.
      // Replace single-quote wrappers with double-quotes.
      if (keys[i][0] === '\'' && keys[i][keys[i].length - 1] === '\'') {
        jsonString += '"';
        // Any enclosed double-quotes must be escaped.
        jsonString += replaceSubstringGlobally(keys[i].substring(1, keys[i].length - 1), '"', '\\"');
        jsonString += '"';
      }
      else {
        // Open wrap with double-quotes if no wrapper.
        if (keys[i][0] !== '"' && keys[i][0] !== '\'') {
          jsonString += '"';

          // The following line is to clean up vestiges from Pattern Lab PHP's escaping scheme.
          // F.Y.I. Pattern Lab PHP would allow special characters like question marks in parameter keys so long as the
          // key was unwrapped and the special character escaped with a backslash. In Node, we need to wrap those keys
          // and unescape those characters.
          keys[i] = replaceSubstringGlobally(keys[i], '\\', '');
        }

        jsonString += keys[i];

        // Close wrap with double-quotes if no wrapper.
        if (keys[i][keys[i].length - 1] !== '"' && keys[i][keys[i].length - 1] !== '\'') {
          jsonString += '"';
        }
      }

      // Colon delimiter.
      jsonString += ':';

      // Values.
      // Replace single-quote wrappers with double-quotes.
      if (values[i][0] === '\'' && values[i][values[i].length - 1] === '\'') {
        jsonString += '"';
        // Any enclosed double-quotes must be escaped.
        jsonString += replaceSubstringGlobally(values[i].substring(1, values[i].length - 1), '"', '\\"');
        jsonString += '"';
      }
      // For everything else, just add the value however it's wrapped.
      else {
        jsonString += values[i];
      }

      // Comma delimiter.
      if (i < keys.length - 1) {
        jsonString += ',';
      }
    }

    jsonString += '}';
    // Unescape escaped unicode except for double-quotes.
    jsonString = replaceSubstringGlobally(jsonString, '\\u0027', '\'');
    jsonString = replaceSubstringGlobally(jsonString, '\\u0058', ':');

    try {
      jsObject = JSON5.parse(jsonString);
    }
    catch (err1) {
      /* istanbul ignore next */
      console.error('\n' + err.message); // eslint-disable-line no-console
    }
  }

  return jsObject;
}

/* istanbul ignore else */
if (typeof define === 'function') {
  define(function () {
    return jsonEval;
  });
}
else if (typeof window === 'object') {
  window.jsonEval = jsonEval;
}

module.exports = jsonEval;
<|endoftext|>


<commit_before>function pack(array) {
  let position = -1

  // Remove empty arrays
  for (let i = 0; i < array.length; i++) {
    if (Array.isArray(array[i]) && array[i].length === 0) {
      array[i] = null
    }
  }

  // Trim `null`
  for (let i = array.length - 1; i >= 0; i--) {
    if (array[i] != null) {
      position = i
      break
    }
  }

  array.length = position + 1
  return array
}

export const NodeType = {
  QUERY: 1,
  FRAGMENT: 2,
  FIELD: 3,
  CALL: 4,
}

export class Node {
  constructor(fields, fragments) {
    this.fields = fields || []
    this.fragments = fragments || []
  }

  static getFields(nodes) {
    const fieldMap = {}

    for (let iterations = 0; nodes.length > 0; iterations++) {
      if (iterations++ > 1e4) {
        throw new Error('Endless loop')
      }

      const node = nodes.shift()

      // Process fragments
      nodes.push(...node.fragments)

      // Process fields
      for (let i = 0; i < node.fields.length; i++) {
        const field = node.fields[i]
        const name = field.name
        if (!fieldMap[name]) {
          fieldMap[name] = field
        }
      }
    }

    return Object.keys(fieldMap).sort().map((name) => fieldMap[name])
  }

  static _toString(nodes) {
    const fields = Node.getFields(nodes)
    if (fields.length === 0) {
      return ''
    }

    return `{${ fields.join(',') }}`
  }
}

export class Query extends Node {
  constructor(action, args, fields, fragments) {
    super(fields, fragments)
    this.rootCall = action
    this.rootNode = new Call(action, args)
  }

  toString() {
    return this.rootNode.toString() + Node._toString([this])
  }

  toJSON() {
    return pack([ NodeType.QUERY, this.rootNode.name, this.rootNode.args, this.fields, this.fragments ])
  }

  static fromJSON(data) {
    let [ type, action, args, fields, fragments ] = data
    if (type !== NodeType.QUERY) {
      throw new Error('Expected query descriptor')
    }

    if (!args) args = []
    if (!fields) fields = []
    if (!fragments) fragments = []

    return new Query(action, args, fields.map(Field.fromJSON), fragments.map(Fragment.fromJSON))
  }
}

export class Fragment extends Node {
  constructor(name, fields, fragments) {
    super(fields, fragments)
    this.name = name
  }

  toString() {
    return Node._toString([this])
  }

  toJSON() {
    return pack([ NodeType.FRAGMENT, this.name, this.fields, this.fragments ])
  }

  static fromJSON(data) {
    let [ type, name, fields, fragments ] = data
    if (type !== NodeType.FRAGMENT) {
      throw new Error('Expected fragment descriptor')
    }

    if (!fields) fields = []
    if (!fragments) fragments = []

    return new Fragment(name, fields.map(Field.fromJSON), fragments.map(Fragment.fromJSON))
  }
}

export class Field extends Node {
  constructor(name, fields, fragments, calls) {
    super(fields, fragments)
    this.name = name
    this.calls = calls || []
  }

  toString() {
    const calls = this.calls.length > 0
      ? '.' + this.calls.join('.') : ''

    return this.name + calls + Node._toString([this])
  }

  toJSON() {
    return pack([ NodeType.FIELD, this.name, this.fields, this.fragments, this.calls ])
  }

  static fromJSON(data) {
    let [ type, name, fields, fragments, calls ] = data
    if (type !== NodeType.FIELD) {
      throw new Error('Expected field descriptor')
    }

    if (!fields) fields = []
    if (!fragments) fragments = []
    if (!calls) calls = []

    return new Field(name, fields.map(Field.fromJSON), fragments.map(Fragment.fromJSON), calls.map(Call.fromJSON))
  }
}

export class Call {
  constructor(name, args) {
    this.name = name
    this.args = args || []
  }

  toString() {
    return `${ this.name }(${ this.args.map(String).join(',') })`
  }

  toJSON() {
    return pack([ NodeType.CALL, this.name, this.args ])
  }

  static fromJSON(data) {
    let [ type, name, args ] = data
    if (type !== NodeType.CALL) {
      throw new Error('Expected call descriptor')
    }

    if (!args) args = []

    return new Call(name, args)
  }
}
<commit_msg>Object args<commit_after>function pack(array) {
  let position = -1

  // Remove empty arrays
  for (let i = 0; i < array.length; i++) {
    if (Array.isArray(array[i]) && array[i].length === 0) {
      array[i] = null
    }
  }

  // Trim `null`
  for (let i = array.length - 1; i >= 0; i--) {
    if (array[i] != null) {
      position = i
      break
    }
  }

  array.length = position + 1
  return array
}

export const NodeType = {
  QUERY: 1,
  FRAGMENT: 2,
  FIELD: 3,
  CALL: 4,
}

export class Node {
  constructor(fields, fragments) {
    this.fields = fields || []
    this.fragments = fragments || []
  }

  static getFields(nodes) {
    const fieldMap = {}

    for (let iterations = 0; nodes.length > 0; iterations++) {
      if (iterations++ > 1e4) {
        throw new Error('Endless loop')
      }

      const node = nodes.shift()

      // Process fragments
      nodes.push(...node.fragments)

      // Process fields
      for (let i = 0; i < node.fields.length; i++) {
        const field = node.fields[i]
        const name = field.name
        if (!fieldMap[name]) {
          fieldMap[name] = field
        }
      }
    }

    return Object.keys(fieldMap).sort().map((name) => fieldMap[name])
  }

  static _toString(nodes) {
    const fields = Node.getFields(nodes)
    if (fields.length === 0) {
      return ''
    }

    return `{${ fields.join(',') }}`
  }
}

export class Query extends Node {
  constructor(action, args, fields, fragments) {
    super(fields, fragments)
    this.rootCall = action
    this.rootNode = new Call(action, args)
  }

  toString() {
    return this.rootNode.toString() + Node._toString([this])
  }

  toJSON() {
    return pack([ NodeType.QUERY, this.rootNode.name, this.rootNode.args, this.fields, this.fragments ])
  }

  static fromJSON(data) {
    let [ type, action, args, fields, fragments ] = data
    if (type !== NodeType.QUERY) {
      throw new Error('Expected query descriptor')
    }

    if (!args) args = []
    if (!fields) fields = []
    if (!fragments) fragments = []

    return new Query(action, args, fields.map(Field.fromJSON), fragments.map(Fragment.fromJSON))
  }
}

export class Fragment extends Node {
  constructor(name, fields, fragments) {
    super(fields, fragments)
    this.name = name
  }

  toString() {
    return Node._toString([this])
  }

  toJSON() {
    return pack([ NodeType.FRAGMENT, this.name, this.fields, this.fragments ])
  }

  static fromJSON(data) {
    let [ type, name, fields, fragments ] = data
    if (type !== NodeType.FRAGMENT) {
      throw new Error('Expected fragment descriptor')
    }

    if (!fields) fields = []
    if (!fragments) fragments = []

    return new Fragment(name, fields.map(Field.fromJSON), fragments.map(Fragment.fromJSON))
  }
}

export class Field extends Node {
  constructor(name, fields, fragments, calls) {
    super(fields, fragments)
    this.name = name
    this.calls = calls || []
  }

  toString() {
    const calls = this.calls.length > 0
      ? '.' + this.calls.join('.') : ''

    return this.name + calls + Node._toString([this])
  }

  toJSON() {
    return pack([ NodeType.FIELD, this.name, this.fields, this.fragments, this.calls ])
  }

  static fromJSON(data) {
    let [ type, name, fields, fragments, calls ] = data
    if (type !== NodeType.FIELD) {
      throw new Error('Expected field descriptor')
    }

    if (!fields) fields = []
    if (!fragments) fragments = []
    if (!calls) calls = []

    return new Field(name, fields.map(Field.fromJSON), fragments.map(Fragment.fromJSON), calls.map(Call.fromJSON))
  }
}

export class Call {
  constructor(name, args) {
    this.name = name
    this.args = args || []
  }

  argsString() {
    if(util.isArray(this.args)){
      return this.args.map(String).join(',')
    }
    return Object.keys(this.args).map((key) =>{
      return key + ":" + this.args[key]
    }).join(",")
  }

  toString() {
    return `${ this.name }(${ this.argsString()})`
  }

  toJSON() {
    return pack([ NodeType.CALL, this.name, this.args ])
  }

  static fromJSON(data) {
    let [ type, name, args ] = data
    if (type !== NodeType.CALL) {
      throw new Error('Expected call descriptor')
    }

    if (!args) args = []

    return new Call(name, args)
  }
}
<|endoftext|>


<commit_before>import path from 'path'
import WebpackNotifierPlugin from 'webpack-notifier'

let devConfig = {
    context: path.join(__dirname, 'src'),
    entry: [
        './app.js'
    ],
    output: {
        path: path.join(__dirname, 'build/assets/js'),
        publicPath: 'assets/js/',
        filename: 'app.js'
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: 'static',
        historyApiFallback: false
    },
    module: {
        preLoaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'eslint'
            }
        ],
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel'
            },
            {
                test: /\.css$/,
                exclude: /node_modules/,
                loaders: ['style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]']
            },
            {
                test: /\.scss$/,
                exclude: /node_modules/,
                loaders: ['style', 'css', 'sass']
            },
            {
                test: /\.(jpg|png|ttf|eot|woff|woff2|svg)$/,
                exclude: /node_modules/,
                loader: 'url?limit=100000'
            }
        ]
    },
    plugins: [
        new WebpackNotifierPlugin()
    ]
}

if (process.env.NODE_ENV === 'production') {
    devConfig.devtool = ''
    devConfig.devServer = {}
}

export default devConfig
<commit_msg>Enable Webpack historyApiFallback<commit_after>import path from 'path'
import WebpackNotifierPlugin from 'webpack-notifier'

let devConfig = {
    context: path.join(__dirname, 'src'),
    entry: [
        './app.js'
    ],
    output: {
        path: path.join(__dirname, 'build/assets/js'),
        publicPath: 'assets/js/',
        filename: 'app.js'
    },
    devtool: 'eval-source-map',
    devServer: {
        contentBase: 'static',
        historyApiFallback: true
    },
    module: {
        preLoaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'eslint'
            }
        ],
        loaders: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel'
            },
            {
                test: /\.css$/,
                exclude: /node_modules/,
                loaders: ['style', 'css?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]']
            },
            {
                test: /\.scss$/,
                exclude: /node_modules/,
                loaders: ['style', 'css', 'sass']
            },
            {
                test: /\.(jpg|png|ttf|eot|woff|woff2|svg)$/,
                exclude: /node_modules/,
                loader: 'url?limit=100000'
            }
        ]
    },
    plugins: [
        new WebpackNotifierPlugin()
    ]
}

if (process.env.NODE_ENV === 'production') {
    devConfig.devtool = ''
    devConfig.devServer = {}
}

export default devConfig
<|endoftext|>


<commit_before>'use strict'

import { fromJS } from 'immutable'
import objectMerge from 'object-merge';

const MODULE_NAME = '[Redux-LocalStorage-Simple]'
const NAMESPACE_DEFAULT = 'redux_localstorage_simple'
const STATES_DEFAULT = []
const DEBOUNCE_DEFAULT = 0
const IMMUTABLEJS_DEFAULT = false
let debounceTimeout = null

// ---------------------------------------------------
/* lensPath

  DESCRIPTION
  ----------
  Gets inner data from an object based on a specified path

  PARAMETERS
  ----------
  @path (Array of Strings) - Path used to get an object's inner data
                              e.g. ['prop', 'innerProp']
  @obj (Object) - Object to get inner data from

  USAGE EXAMPLE
  -------------
  lensPath(
    ['prop', 'innerProp'],
    { prop: { innerProp: 123 } }
  )

    returns

  123
*/

function lensPath (path, obj) {
  if (path.length === 1) {
    return obj[path[0]]
  } else {
    return lensPath(path.slice(1), obj[path[0]])
  }
}

// ---------------------------------------------------
/* realiseObject

  DESCRIPTION
  ----------
  Create an object from a specified path, with
  the innermost property set with an initial value

  PARAMETERS
  ----------
  @objectPath (String) - Object path e.g. 'myObj.prop1.prop2'
  @objectInitialValue (Any, optional) - Value of the innermost property once object is created

  USAGE EXAMPLE
  -------------

  realiseObject('myObj.prop1.prop2', 123)

    returns

  {
    myObj: {
      prop1: {
          prop2: 123
        }
      }
  }
*/

function realiseObject (objectPath, objectInitialValue = {}) {
  function realiseObject_ (objectPathArr, objectInProgress) {
    if (objectPathArr.length === 0) {
      return objectInProgress
    } else {
      return realiseObject_(objectPathArr.slice(1), {[objectPathArr[0]]: objectInProgress})
    }
  }
  return realiseObject_(objectPath.split('.').reverse(), objectInitialValue)
}

// ---------------------------------------------------

/**
  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
              debounce (Number, optional) - Debouncing period (in milliseconds) to wait before saving to LocalStorage
                                            Use this as a performance optimization if you feel you are saving
                                            to LocalStorage too often. Recommended value: 500 - 1000 milliseconds

  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 the entire state tree only after a debouncing period of 500 milliseconds has elapsed
    save({
      debounce: 500
    })

    // 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',
        debounce: 500
    })
*/

export function save ({
      states = STATES_DEFAULT,
      namespace = NAMESPACE_DEFAULT,
      debounce = DEBOUNCE_DEFAULT
    } = {}) {
  return store => next => action => {
    next(action)

    // Validate 'states' parameter
    if (!isArray(states)) {
      console.error(MODULE_NAME, '\'states\' parameter in \'save()\' method was passed a non-array value. Setting default value instead. Check your \'save()\' method.')
      states = STATES_DEFAULT
    }

    // Validate 'namespace' parameter
    if (!isString(namespace)) {
      console.error(MODULE_NAME, '\'namespace\' parameter in \'save()\' method was passed a non-string value. Setting default value instead. Check your \'save()\' method.')
      namespace = NAMESPACE_DEFAULT
    }

    // Validate 'debounce' parameter
    if (!isInteger(debounce)) {
      console.error(MODULE_NAME, '\'debounce\' parameter in \'save()\' method was passed a non-integer value. Setting default value instead. Check your \'save()\' method.')
      debounce = DEBOUNCE_DEFAULT
    }

    // Check to see whether to debounce LocalStorage saving
    if (debounce) {
      // Clear the debounce timeout if it was previously set
      if (debounceTimeout) {
        clearTimeout(debounceTimeout)
      }

      // Save to LocalStorage after the debounce period has elapsed
      debounceTimeout = setTimeout(function () {
        _save(states, namespace)
      }, debounce)
    // No debouncing necessary so save to LocalStorage right now
    } else {
      _save(states, namespace)
    }

    // Digs into rootState for the data to put in LocalStorage
    function getStateForLocalStorage (state, rootState) {
      const delimiter = '.'

      if (state.split(delimiter).length > 1) {
        return lensPath(state.split(delimiter), rootState)
      } else {
        return lensPath([state], rootState)
      }
    }

    // Local function to avoid duplication of code above
    function _save () {
      if (states.length === 0) {
        localStorage[namespace] = JSON.stringify(store.getState())
      } else {
        states.forEach(state => {
          localStorage[namespace + '_' + state] = JSON.stringify(getStateForLocalStorage(state, store.getState()))
        })
      }
    }
  }
}

/**
  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 = STATES_DEFAULT,
      immutablejs = IMMUTABLEJS_DEFAULT,
      namespace = NAMESPACE_DEFAULT,
      preloadedState = {}
    } = {}) {
  // Validate 'states' parameter
  if (!isArray(states)) {
    console.error(MODULE_NAME, '\'states\' parameter in \'load()\' method was passed a non-array value. Setting default value instead. Check your \'load()\' method.')
    states = STATES_DEFAULT
  }

  // Validate 'immutablejs' parameter
  if (!isBoolean(immutablejs)) {
    console.error(MODULE_NAME, '\'immutablejs\' parameter in \'load()\' method was passed a non-boolean value. Setting default value instead. Check your \'load()\' method.')
    immutablejs = IMMUTABLEJS_DEFAULT
  }

  // Validate 'namespace' parameter
  if (!isString(namespace)) {
    console.error(MODULE_NAME, '\'namespace\' parameter in \'load()\' method was passed a non-string value. Setting default value instead. Check your \'load()\' method.')
    namespace = NAMESPACE_DEFAULT
  }

  let loadedState = preloadedState

  // Load all of the namespaced Redux data from LocalStorage into local Redux state tree
  if (states.length === 0) {
    if (localStorage[namespace]) {
      loadedState = JSON.parse(localStorage[namespace])

      if (immutablejs) {
        loadedState = fromJS(loadedState)
      }
    }
  } else { // Load only specified states into the local Redux state tree
    states.forEach(function (state) {
      if (localStorage[namespace + '_' + state]) {
        loadedState = objectMerge(loadedState, realiseObject(state, JSON.parse(localStorage[namespace + '_' + state])))
      } else {
        console.error(MODULE_NAME, "Invalid load '" + (namespace + '_' + state) + "' provided. Check your 'states' in 'load()'")
      }
    })

    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 => {
    // Make sure current 'load' is an object
    if (!isObject(load)) {
      console.error(MODULE_NAME, 'One or more loads provided to \'combineLoads()\' is not a valid object. Ignoring the invalid load/s. Check your \'combineLoads()\' method.')
      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 } = {}) {
  // Validate 'namespace' parameter
  if (!isString(namespace)) {
    console.error(MODULE_NAME, '\'namespace\' parameter in \'clear()\' method was passed a non-string value. Setting default value instead. Check your \'clear()\' method.')
    namespace = NAMESPACE_DEFAULT
  }

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

// ---------------------------------------------------
// Utility functions

function isArray (value) {
  return Object.prototype.toString.call(value) === '[object Array]'
}

function isString (value) {
  return typeof value === 'string'
}

function isInteger (value) {
  return typeof value === 'number' &&
    isFinite(value) &&
    Math.floor(value) === value
}

function isBoolean (value) {
  return typeof value === 'boolean'
}

function isObject (value) {
  return value !== null && typeof value === 'object'
}
<commit_msg>Removal of Immutable.js support with warning message<commit_after>'use strict'

import objectMerge from 'object-merge'

const MODULE_NAME = '[Redux-LocalStorage-Simple]'
const NAMESPACE_DEFAULT = 'redux_localstorage_simple'
const STATES_DEFAULT = []
const DEBOUNCE_DEFAULT = 0
const IMMUTABLEJS_DEFAULT = false
let debounceTimeout = null

// ---------------------------------------------------
/* lensPath

  DESCRIPTION
  ----------
  Gets inner data from an object based on a specified path

  PARAMETERS
  ----------
  @path (Array of Strings) - Path used to get an object's inner data
                              e.g. ['prop', 'innerProp']
  @obj (Object) - Object to get inner data from

  USAGE EXAMPLE
  -------------
  lensPath(
    ['prop', 'innerProp'],
    { prop: { innerProp: 123 } }
  )

    returns

  123
*/

function lensPath (path, obj) {
  if (path.length === 1) {
    return obj[path[0]]
  } else {
    return lensPath(path.slice(1), obj[path[0]])
  }
}

// ---------------------------------------------------
/* realiseObject

  DESCRIPTION
  ----------
  Create an object from a specified path, with
  the innermost property set with an initial value

  PARAMETERS
  ----------
  @objectPath (String) - Object path e.g. 'myObj.prop1.prop2'
  @objectInitialValue (Any, optional) - Value of the innermost property once object is created

  USAGE EXAMPLE
  -------------

  realiseObject('myObj.prop1.prop2', 123)

    returns

  {
    myObj: {
      prop1: {
          prop2: 123
        }
      }
  }
*/

function realiseObject (objectPath, objectInitialValue = {}) {
  function realiseObject_ (objectPathArr, objectInProgress) {
    if (objectPathArr.length === 0) {
      return objectInProgress
    } else {
      return realiseObject_(objectPathArr.slice(1), {[objectPathArr[0]]: objectInProgress})
    }
  }
  return realiseObject_(objectPath.split('.').reverse(), objectInitialValue)
}

// ---------------------------------------------------

/**
  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
              debounce (Number, optional) - Debouncing period (in milliseconds) to wait before saving to LocalStorage
                                            Use this as a performance optimization if you feel you are saving
                                            to LocalStorage too often. Recommended value: 500 - 1000 milliseconds

  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 the entire state tree only after a debouncing period of 500 milliseconds has elapsed
    save({
      debounce: 500
    })

    // 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',
        debounce: 500
    })
*/

export function save ({
      states = STATES_DEFAULT,
      namespace = NAMESPACE_DEFAULT,
      debounce = DEBOUNCE_DEFAULT
    } = {}) {
  return store => next => action => {
    next(action)

    // Validate 'states' parameter
    if (!isArray(states)) {
      console.error(MODULE_NAME, '\'states\' parameter in \'save()\' method was passed a non-array value. Setting default value instead. Check your \'save()\' method.')
      states = STATES_DEFAULT
    }

    // Validate 'namespace' parameter
    if (!isString(namespace)) {
      console.error(MODULE_NAME, '\'namespace\' parameter in \'save()\' method was passed a non-string value. Setting default value instead. Check your \'save()\' method.')
      namespace = NAMESPACE_DEFAULT
    }

    // Validate 'debounce' parameter
    if (!isInteger(debounce)) {
      console.error(MODULE_NAME, '\'debounce\' parameter in \'save()\' method was passed a non-integer value. Setting default value instead. Check your \'save()\' method.')
      debounce = DEBOUNCE_DEFAULT
    }

    // Check to see whether to debounce LocalStorage saving
    if (debounce) {
      // Clear the debounce timeout if it was previously set
      if (debounceTimeout) {
        clearTimeout(debounceTimeout)
      }

      // Save to LocalStorage after the debounce period has elapsed
      debounceTimeout = setTimeout(function () {
        _save(states, namespace)
      }, debounce)
    // No debouncing necessary so save to LocalStorage right now
    } else {
      _save(states, namespace)
    }

    // Digs into rootState for the data to put in LocalStorage
    function getStateForLocalStorage (state, rootState) {
      const delimiter = '.'

      if (state.split(delimiter).length > 1) {
        return lensPath(state.split(delimiter), rootState)
      } else {
        return lensPath([state], rootState)
      }
    }

    // Local function to avoid duplication of code above
    function _save () {
      if (states.length === 0) {
        localStorage[namespace] = JSON.stringify(store.getState())
      } else {
        states.forEach(state => {
          localStorage[namespace + '_' + state] = JSON.stringify(getStateForLocalStorage(state, store.getState()))
        })
      }
    }
  }
}

/**
  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

  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 was previously saved with the namespace "my_cool_app"
    load({
        states: ['user', 'products'],
        namespace: 'my_cool_app'
    })

*/

export function load ({
      states = STATES_DEFAULT,
      immutablejs = IMMUTABLEJS_DEFAULT,
      namespace = NAMESPACE_DEFAULT,
      preloadedState = {}
    } = {}) {
  // Validate 'states' parameter
  if (!isArray(states)) {
    console.error(MODULE_NAME, '\'states\' parameter in \'load()\' method was passed a non-array value. Setting default value instead. Check your \'load()\' method.')
    states = STATES_DEFAULT
  }

  // Validate 'namespace' parameter
  if (!isString(namespace)) {
    console.error(MODULE_NAME, '\'namespace\' parameter in \'load()\' method was passed a non-string value. Setting default value instead. Check your \'load()\' method.')
    namespace = NAMESPACE_DEFAULT
  }

  // Display immmutablejs deprecation notice if developer tries to utilise it
  if (immutablejs === true) {
    console.error(MODULE_NAME, 'Support for Immutable.js data structures has been deprecated as of version 2.0.0. Please use version 1.4.0 if you require this functionality.')
  }

  let loadedState = preloadedState

  // Load all of the namespaced Redux data from LocalStorage into local Redux state tree
  if (states.length === 0) {
    if (localStorage[namespace]) {
      loadedState = JSON.parse(localStorage[namespace])
    }
  } else { // Load only specified states into the local Redux state tree
    states.forEach(function (state) {
      if (localStorage[namespace + '_' + state]) {
        loadedState = objectMerge(loadedState, realiseObject(state, JSON.parse(localStorage[namespace + '_' + state])))
      } else {
        console.error(MODULE_NAME, "Invalid load '" + (namespace + '_' + state) + "' provided. Check your 'states' in 'load()'")
      }
    })
  }

  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 use different namespaces

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

  Usage example:

    // 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 => {
    // Make sure current 'load' is an object
    if (!isObject(load)) {
      console.error(MODULE_NAME, 'One or more loads provided to \'combineLoads()\' is not a valid object. Ignoring the invalid load/s. Check your \'combineLoads()\' method.')
      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 } = {}) {
  // Validate 'namespace' parameter
  if (!isString(namespace)) {
    console.error(MODULE_NAME, '\'namespace\' parameter in \'clear()\' method was passed a non-string value. Setting default value instead. Check your \'clear()\' method.')
    namespace = NAMESPACE_DEFAULT
  }

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

// ---------------------------------------------------
// Utility functions

function isArray (value) {
  return Object.prototype.toString.call(value) === '[object Array]'
}

function isString (value) {
  return typeof value === 'string'
}

function isInteger (value) {
  return typeof value === 'number' &&
    isFinite(value) &&
    Math.floor(value) === value
}

function isObject (value) {
  return value !== null && typeof value === 'object'
}
<|endoftext|>


<commit_before>import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Docs from './docs/Docs';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<Docs />, document.getElementById('root'));
registerServiceWorker();
<commit_msg>style<commit_after>import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import Docs from './docs/Docs';
import '../node_modules/highlight.js/styles/ocean.css';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<Docs />, document.getElementById('root'));
registerServiceWorker();
<|endoftext|>


<commit_before>'use strict';
import Webpack from 'webpack';
import Merge from 'webpack-merge';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HTMLWebpackPlugin from 'html-webpack-plugin';
import Path from 'path';
import PostCSS from './postcss.config';
import DotEnv from 'dotenv-webpack';

// const appEnv = (process.env.NODE_ENV || 'dev');
const isProdEnv = (process.env.NODE_ENV === 'production');
  console.log(`Node Environment:\t${process.env.NODE_ENV}`);

const VENDOR_LIBS = [
  'body-parser',
  'bson',
  'cloudinary',
  'jquery',
  'lodash',
  'react',
  'react-dom',
  'react-redux',
  'react-router',
  'react-router-redux',
  'redux',
  'redux-thunk'
];

const BASE_CONFIG = {
  entry: {
    // 'webpack/hot/dev-server',
    bundle: Path.resolve(__dirname, 'src/Routes'),
    vendor: VENDOR_LIBS
  },
  output: {
    path: Path.join(__dirname, 'dist'),
    filename: '[name].[hash].js'
    // publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/i,
        exclude: /(node_modules|bower_components)/,
        use: 'babel'
      }, {
        test: /\.css$/i,
        loader: ExtractTextPlugin.extract({
          fallback: 'style',
          use: 'css'
        })
      }, {
        test: /\.(scss|sass)$/i,
        loaders: ExtractTextPlugin.extract({
          fallback: 'style',
          use: ['css', 'postcss', 'sass']
        })
        // [
        //   'style',
        //   'css',
        //   'autoprefixer?browsers=last 3 versions',
        //   'sass?outputStyle=expanded'
        // ]
      }, {
        test: /\.json$/i,
        use: 'json'
      }, {
        test: /\.(bmp|gif|jpe?g|png|svg|ttif)$/i,
        use: [
          // {
          //   loader: 'url',
          //   options: { limit: 40000 }
          // },
          'file?name=/images/[name].[ext]',
          'image-webpack'
        ]
      }, {
        test: /\.(pdf|doc[mstx]?|ppt[mstx]?|od[fpst])$/i,
        use: 'file?name=/docs/[name].[ext]'
      }
    ]
  },
  plugins: [
    // Configure and read in local environment variables:
    new DotEnv({
      path: './.env'
    }),
    new Webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
      minChunks: Infinity
    }),
    new Webpack.DefinePlugin({
      // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')

      // 'process.env': {
      //   NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      // }
    }),
    new Webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false,
      options: {
        postcss: { PostCSS }
      }
    }),
    new ExtractTextPlugin('styles.css'),
    new HTMLWebpackPlugin({
      template: 'assets/index.html'
    })
  ],
  node: {
    console: false,
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  },
  cache: true,
  watch: true,
  devtool: `${isProdEnv ? 'inline' : 'cheap-eval'}-source-map`,
  resolve: {
    extensions: ['.js', '.jsx']
  },
  resolveLoader: {
    moduleExtensions: ['-loader']
  }
};

const DEV_SERVER = {
  plugins: [
    new Webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    // contentBase: __dirname,
    historyApiFallback: true,
    host: 'localhost',
    hot: true,
    inline: true,
    noInfo: false,
    port: 3000
  },
  stats: {
    assets: true,
    children: false,
    chunks: false,
    colors: {
      bold: '\u001b[1m',
      cyan: '\u001b[1m\u001b[36m',
      green: '\u001b[1m\u001b[32m',
      magenta: '\u001b[1m\u001b[35m',
      red: '\u001b[1m\u001b[31m',
      yellow: '\u001b[1m\u001b[33m'
    },
    errorDetails: true,
    hash: false,
    modules: false,
    publicPath: false,
    reasons: true,
    timings: true,
    version: false,
    warnings: true
  }
};

const AGGREGATE_CONFIG = isProdEnv
  ? BASE_CONFIG
  : Merge(BASE_CONFIG, DEV_SERVER);

export default AGGREGATE_CONFIG;

//     new Webpack.optimize.DedupePlugin(),
//     new Webpack.optimize.UglifyJsPlugin({
//       compress: { warnings: false },
//       output: { comments: false },
//       sourceMap: false
//     })
<commit_msg>config (Webpack): Add new dependencies to vendor libraries<commit_after>'use strict';
import Webpack from 'webpack';
import Merge from 'webpack-merge';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import HTMLWebpackPlugin from 'html-webpack-plugin';
import DotEnv from 'dotenv-webpack';
import Path from 'path';
import PostCSS from './postcss.config';

// const appEnv = (process.env.NODE_ENV || 'dev');
const isProdEnv = (process.env.NODE_ENV === 'production');
  console.log(`Node Environment:\t${process.env.NODE_ENV}`);

const VENDOR_LIBS = [
  'body-parser',
  'bson',
  'cloudinary',
  'jquery',
  'lodash',
  'react',
  'react-dom',
  'react-motion',
  'react-redux',
  'react-router',
  'react-router-redux',
  'redux',
  'redux-thunk',
  'request',
  'uuid'
];

const BASE_CONFIG = {
  entry: {
    // 'webpack/hot/dev-server',
    bundle: Path.resolve(__dirname, 'src/RouterConfig'),
    vendor: VENDOR_LIBS
  },
  output: {
    path: Path.join(__dirname, 'dist'),
    filename: '[name].[hash].js'
    // publicPath: '/'
  },
  module: {
    rules: [
      {
        test: /\.jsx?$/i,
        exclude: /(node_modules|bower_components)/,
        use: 'babel'
      }, {
        test: /\.css$/i,
        loader: ExtractTextPlugin.extract({
          fallback: 'style',
          use: 'css'
        })
      }, {
        test: /\.(scss|sass)$/i,
        loaders: ExtractTextPlugin.extract({
          fallback: 'style',
          use: ['css', 'postcss', 'sass']
        })
        // [
        //   'style',
        //   'css',
        //   'autoprefixer?browsers=last 3 versions',
        //   'sass?outputStyle=expanded'
        // ]
      }, {
        test: /\.json$/i,
        use: 'json'
      }, {
        test: /\.(bmp|gif|jpe?g|png|svg|ttif)$/i,
        use: [
          // {
          //   loader: 'url',
          //   options: { limit: 40000 }
          // },
          'file?name=/images/[name].[ext]',
          'image-webpack'
        ]
      }, {
        test: /\.(pdf|doc[mstx]?|ppt[mstx]?|od[fpst])$/i,
        use: 'file?name=/docs/[name].[ext]'
      }
    ]
  },
  plugins: [
    // Configure and read in local environment variables:
    new DotEnv({
      path: './.env'
    }),
    new Webpack.optimize.CommonsChunkPlugin({
      names: ['vendor', 'manifest'],
      minChunks: Infinity
    }),
    new Webpack.DefinePlugin({
      // 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')

      // 'process.env': {
      //   NODE_ENV: JSON.stringify(process.env.NODE_ENV)
      // }
    }),
    new Webpack.LoaderOptionsPlugin({
      minimize: true,
      debug: false,
      options: {
        postcss: { PostCSS }
      }
    }),
    new ExtractTextPlugin('styles.css'),
    new HTMLWebpackPlugin({
      template: 'assets/index.html'
    })
  ],
  node: {
    console: false,
    fs: 'empty',
    net: 'empty',
    tls: 'empty'
  },
  cache: true,
  watch: true,
  devtool: `${isProdEnv ? 'inline' : 'cheap-eval'}-source-map`,
  resolve: {
    extensions: ['.js', '.jsx']
  },
  resolveLoader: {
    moduleExtensions: ['-loader']
  }
};

const DEV_SERVER = {
  plugins: [
    new Webpack.HotModuleReplacementPlugin()
  ],
  devServer: {
    // contentBase: __dirname,
    historyApiFallback: true,
    host: 'localhost',
    hot: true,
    inline: true,
    noInfo: false,
    port: 3000
  },
  stats: {
    assets: true,
    children: false,
    chunks: false,
    colors: {
      bold: '\u001b[1m',
      cyan: '\u001b[1m\u001b[36m',
      green: '\u001b[1m\u001b[32m',
      magenta: '\u001b[1m\u001b[35m',
      red: '\u001b[1m\u001b[31m',
      yellow: '\u001b[1m\u001b[33m'
    },
    errorDetails: true,
    hash: false,
    modules: false,
    publicPath: false,
    reasons: true,
    timings: true,
    version: false,
    warnings: true
  }
};

const AGGREGATE_CONFIG = isProdEnv
  ? BASE_CONFIG
  : Merge(BASE_CONFIG, DEV_SERVER);

export default AGGREGATE_CONFIG;

//     new Webpack.optimize.DedupePlugin(),
//     new Webpack.optimize.UglifyJsPlugin({
//       compress: { warnings: false },
//       output: { comments: false },
//       sourceMap: false
//     })
<|endoftext|>


<commit_before>'use strict';

(function () {

  function setupRequestAnimationFrame () {
    var lastTime = 0;
    var vendors = ['webkit', 'moz', 'ms', 'o'];

    for (var i = 0; !window.requestAnimationFrame && i < vendors.length; i += 1) {
      var vendor = vendors[i];
      window.requestAnimationFrame = window[vendor + 'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[vendor + 'CancelAnimationFrame'] ||
        window[vendor + 'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function (callback) {
        var now = new Date().getTime();
        var nextTime = Math.max(0, 16 - (now - lastTime));

        var id = window.setTimeout(function () {
          callback(now + nextTime);
        }, nextTime);

        lastTime = now + nextTime;
        return id;
      };
    }

    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function (id) {
        clearTimeout(id);
      };
    }
  }

  setupRequestAnimationFrame();

  function setupPerformanceNow () {
    if (!('performance' in window)) {
      window.performance = {};
    }

    if (!('now' in window.performance)) {
      var dateNow = Date.now || function () {
        return new Date().getTime();
      };

      var nowOffset = dateNow();

      if (performance.timing && performance.timing.navigationStart) {
        nowOffset = performance.timing.navigationStart;
      }

      window.performance.now = function now () {
        return dateNow() - nowOffset;
      };
    }
  }

  setupPerformanceNow();

  function getSlik (Immutable) {

    var animationIndex = 0;

    function getDefault (value, defaultValue) {
      return typeof value === 'undefined' ? defaultValue : value;
    }

    /* @group loop */

    var raf;
    var animations = Immutable.Map();

    function doLoop () {
      var now = performance.now();

      animations.forEach(function (animationLoop) {
        animationLoop(now);
      });

      if (animations.count()) {
        raf = requestAnimationFrame(doLoop);
      }
    }

    function startLoop (animationId, animationLoop) {
      var previousAnimationCount = animations.count();

      if (!animations.has(animationId)) {
        animations = animations.set(animationId, animationLoop);
      }

      if (!previousAnimationCount && animations.count()) {
        raf = requestAnimationFrame(doLoop);
      }
    }

    function stopLoop (animationId) {
      var previousAnimationCount = animations.count();

      if (animations.has(animationId)) {
        animations = animations.delete(animationId);
      }

      if (previousAnimationCount && !animations.count()) {
        cancelAnimationFrame(raf);
      }
    }

    /* @end loop */

    /* @group easing */

    function multiply (fromValue, toValue, multiplier) {
      return fromValue * (1 - multiplier) + toValue * multiplier;
    }

    function Linear (progress) {
      return progress;
    }

    function Ease (progress) {
      return (1 - Math.cos(progress * Math.PI)) / 2;
    }

    function EaseIn (progress) {
      return 1 - Math.cos(progress * Math.PI / 2);
    }

    function EaseOut (progress) {
      return Math.sin(progress * Math.PI / 2);
    }

    function Dip (progress) {
      return (progress >= 0.5 ? 1 : 0) + (Math.sin(progress * Math.PI) / 2) * (progress >= 0.5 ? -1 : 1);
    }

    function Spring (progress) {
      return 1 - (Math.cos(progress * progress * Math.PI * 10) * (1 - progress));
    }

    var Easing = {
      Linear: Linear,
      Ease: Ease,
      EaseIn: EaseIn,
      EaseOut: EaseOut,
      Dip: Dip,
      Spring: Spring
    };

    /* @end easing */

    function Animation (initial) {
      var self = this;

      var startTime, lastTime, pausedAfter;

      var animationId = animationIndex;
      animationIndex += 1;

      var fromValues = Immutable.fromJS(getDefault(initial.from, {}));
      var toValues = Immutable.fromJS(getDefault(initial.to, {}));
      var durationMillis = getDefault(initial.duration, 500);
      var frameRate = 1000 / getDefault(initial.fps, 120);
      var easing = getDefault(initial.ease, Linear);
      var shouldLoop = getDefault(initial.loop, false);

      var currentValues = fromValues;

      var events = Immutable.Map({
        all: Immutable.List(),
        start: Immutable.List(),
        stop: Immutable.List(),
        pause: Immutable.List(),
        end: Immutable.List(),
        update: Immutable.List(),
        loop: Immutable.List()
      });

      function triggerEvent (type) {
        events.get(type, Immutable.List()).forEach(function (callback) {
          callback(currentValues);
        });

        events.get('all', Immutable.List()).forEach(function (callback) {
          callback(currentValues);
        });
      }

      function mapValues (progress, parentPath, fromValue, key) {
        var path = parentPath.push(key);

        if (Immutable.Iterable.isIterable(fromValue)) {
          // Ease nested immutable objects
          return fromValue.map(mapValues.bind(null, progress, path));
        }

        var toValue = toValues.getIn(path);

        // Only ease values if the toValue exists
        if (typeof toValue !== 'undefined') {
          return multiply(fromValue, toValue, easing(progress));
        }

        return fromValue;
      }

      function animationLoop (now) {
        var progress = Math.min(Math.max((now - startTime) / durationMillis, 0), 1);

        // Limit frame rate
        if (now - lastTime >= frameRate) {
          if (Immutable.Iterable.isIterable(fromValues)) {
            // Ease immutable objects
            currentValues = fromValues.map(mapValues.bind(null, progress, Immutable.List()));
          } else {
            // Ease individual value
            currentValues = multiply(fromValues, toValues, easing(progress));
          }

          triggerEvent('update');
        }

        lastTime = now;

        if (progress >= 1) {
          if (shouldLoop) {
            startTime = now;
            triggerEvent('loop');
          } else {
            stopLoop(animationId);
            triggerEvent('end');
          }
        }
      }

      // Set the frameRate
      function fps (input) {
        frameRate = 1000 / input;
        return self;
      }

      // Set from values
      function from (input) {
        fromValues = Immutable.fromJS(input);
        return self;
      }

      // Set to values
      function to (input) {
        toValues = Immutable.fromJS(input);
        return self;
      }

      // Set animation duration
      function duration (input) {
        durationMillis = input;
        return self;
      }

      // Set easing
      function ease (input) {
        if (typeof input !== 'function') {
          throw new Error('Ease must be a function, instead got: ' + (typeof input));
        }

        easing = input;
        return self;
      }

      // Set whether the animation should loop
      function loop (input) {
        shouldLoop = input;
        return self;
      }

      // Invert animation values
      function invert () {
        return self;
      }

      // Play animation in reverse
      function reverse () {
        var temp = fromValues;
        fromValues = toValues;
        toValues = temp;
        return self;
      }

      // Start or resume animation
      function start () {
        if (typeof pausedAfter !== 'undefined') {
          startTime = performance.now() - pausedAfter;
        } else {
          startTime = performance.now();
        }

        lastTime = startTime;
        pausedAfter = undefined;

        triggerEvent('start');

        startLoop(animationId, animationLoop);

        return self;
      }

      // Stop animation and resume from beginning
      function stop () {
        triggerEvent('stop');
        stopLoop(animationId);
        pausedAfter = undefined;
        startTime = undefined;
        return self;
      }

      // Pause animation and resume from this point
      function pause () {
        triggerEvent('pause');
        stopLoop(animationId);
        pausedAfter = startTime - performance.now();
        startTime = undefined;
        return self;
      }

      // Add event listener
      function bind (type, callback) {
        if (!events.has(type)) {
          throw new Error('Unknown event: ' + type);
        }

        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        var eventsOfType = events.get(type, Immutable.List());

        if (!eventsOfType.includes(callback)) {
          events = events.set(type, eventsOfType.push(callback));
        }

        return self;
      }

      // Remove event listener
      function unbind (type, callback) {
        if (!events.has(type)) {
          throw new Error('Unknown event: ' + type);
        }

        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        var eventsOfType = events.get(type, Immutable.List());

        if (eventsOfType.includes(callback)) {
          events = events.set(type, eventsOfType.delete(eventsOfType.indexOf(callback)));
        }

        return self;
      }

      // Run on complete and automatically unbind
      function then (callback) {
        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        function thenCallback (result) {
          callback(result);
          unbind('end', thenCallback);
        }

        bind('end', thenCallback);

        return self;
      }

      self.fps = fps;
      self.from = from;
      self.to = to;
      self.duration = duration;
      self.ease = ease;
      self.loop = loop;
      self.invert = invert;
      self.reverse = reverse;
      self.start = start;
      self.stop = self.reset = stop;
      self.pause = pause;
      self.bind = self.on = bind;
      self.unbind = self.off = unbind;
      self.then = then;
    }

    return {
      Animation: Animation,
      Easing: Easing
    };

  }

  // Export for commonjs / browserify
  if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = getSlik(require('immutable')); // eslint-disable-line no-undef
  // Export for amd / require
  } else if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef
    define(['immutable'], function (Immutable) { // eslint-disable-line no-undef
      return getSlik(Immutable);
    });
  // Export globally
  } else {
    var root;

    if (typeof window !== 'undefined') {
      root = window;
    } else if (typeof global !== 'undefined') {
      root = global; // eslint-disable-line no-undef
    } else if (typeof self !== 'undefined') {
      root = self; // eslint-disable-line no-undef
    } else {
      root = this;
    }

    root.Slik = getSlik(root.Immutable);
  }

})();
<commit_msg>Invert toValues<commit_after>'use strict';

(function () {

  function setupRequestAnimationFrame () {
    var lastTime = 0;
    var vendors = ['webkit', 'moz', 'ms', 'o'];

    for (var i = 0; !window.requestAnimationFrame && i < vendors.length; i += 1) {
      var vendor = vendors[i];
      window.requestAnimationFrame = window[vendor + 'RequestAnimationFrame'];
      window.cancelAnimationFrame = window[vendor + 'CancelAnimationFrame'] ||
        window[vendor + 'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame) {
      window.requestAnimationFrame = function (callback) {
        var now = new Date().getTime();
        var nextTime = Math.max(0, 16 - (now - lastTime));

        var id = window.setTimeout(function () {
          callback(now + nextTime);
        }, nextTime);

        lastTime = now + nextTime;
        return id;
      };
    }

    if (!window.cancelAnimationFrame) {
      window.cancelAnimationFrame = function (id) {
        clearTimeout(id);
      };
    }
  }

  setupRequestAnimationFrame();

  function setupPerformanceNow () {
    if (!('performance' in window)) {
      window.performance = {};
    }

    if (!('now' in window.performance)) {
      var dateNow = Date.now || function () {
        return new Date().getTime();
      };

      var nowOffset = dateNow();

      if (performance.timing && performance.timing.navigationStart) {
        nowOffset = performance.timing.navigationStart;
      }

      window.performance.now = function now () {
        return dateNow() - nowOffset;
      };
    }
  }

  setupPerformanceNow();

  function getSlik (Immutable) {

    var animationIndex = 0;

    function getDefault (value, defaultValue) {
      return typeof value === 'undefined' ? defaultValue : value;
    }

    /* @group loop */

    var raf;
    var animations = Immutable.Map();

    function doLoop () {
      var now = performance.now();

      animations.forEach(function (animationLoop) {
        animationLoop(now);
      });

      if (animations.count()) {
        raf = requestAnimationFrame(doLoop);
      }
    }

    function startLoop (animationId, animationLoop) {
      var previousAnimationCount = animations.count();

      if (!animations.has(animationId)) {
        animations = animations.set(animationId, animationLoop);
      }

      if (!previousAnimationCount && animations.count()) {
        raf = requestAnimationFrame(doLoop);
      }
    }

    function stopLoop (animationId) {
      var previousAnimationCount = animations.count();

      if (animations.has(animationId)) {
        animations = animations.delete(animationId);
      }

      if (previousAnimationCount && !animations.count()) {
        cancelAnimationFrame(raf);
      }
    }

    /* @end loop */

    /* @group easing */

    function multiply (fromValue, toValue, multiplier) {
      return fromValue * (1 - multiplier) + toValue * multiplier;
    }

    function Linear (progress) {
      return progress;
    }

    function Ease (progress) {
      return (1 - Math.cos(progress * Math.PI)) / 2;
    }

    function EaseIn (progress) {
      return 1 - Math.cos(progress * Math.PI / 2);
    }

    function EaseOut (progress) {
      return Math.sin(progress * Math.PI / 2);
    }

    function Dip (progress) {
      return (progress >= 0.5 ? 1 : 0) + (Math.sin(progress * Math.PI) / 2) * (progress >= 0.5 ? -1 : 1);
    }

    function Spring (progress) {
      return 1 - (Math.cos(progress * progress * Math.PI * 10) * (1 - progress));
    }

    var Easing = {
      Linear: Linear,
      Ease: Ease,
      EaseIn: EaseIn,
      EaseOut: EaseOut,
      Dip: Dip,
      Spring: Spring
    };

    /* @end easing */

    function Animation (initial) {
      var self = this;

      var startTime, lastTime, pausedAfter;

      var animationId = animationIndex;
      animationIndex += 1;

      var fromValues = Immutable.fromJS(getDefault(initial.from, {}));
      var toValues = Immutable.fromJS(getDefault(initial.to, {}));
      var durationMillis = getDefault(initial.duration, 500);
      var frameRate = 1000 / getDefault(initial.fps, 120);
      var easing = getDefault(initial.ease, Linear);
      var shouldLoop = getDefault(initial.loop, false);

      var currentValues = fromValues;

      var events = Immutable.Map({
        all: Immutable.List(),
        start: Immutable.List(),
        stop: Immutable.List(),
        pause: Immutable.List(),
        end: Immutable.List(),
        update: Immutable.List(),
        loop: Immutable.List()
      });

      function triggerEvent (type) {
        events.get(type, Immutable.List()).forEach(function (callback) {
          callback(currentValues);
        });

        events.get('all', Immutable.List()).forEach(function (callback) {
          callback(currentValues);
        });
      }

      function mapValues (progress, parentPath, fromValue, key) {
        var path = parentPath.push(key);

        if (Immutable.Iterable.isIterable(fromValue)) {
          // Ease nested immutable objects
          return fromValue.map(mapValues.bind(null, progress, path));
        }

        var toValue = toValues.getIn(path);

        // Only ease values if the toValue exists
        if (typeof toValue !== 'undefined') {
          return multiply(fromValue, toValue, easing(progress));
        }

        return fromValue;
      }

      function animationLoop (now) {
        var progress = Math.min(Math.max((now - startTime) / durationMillis, 0), 1);

        // Limit frame rate
        if (now - lastTime >= frameRate) {
          if (Immutable.Iterable.isIterable(fromValues)) {
            // Ease immutable objects
            currentValues = fromValues.map(mapValues.bind(null, progress, Immutable.List()));
          } else {
            // Ease individual value
            currentValues = multiply(fromValues, toValues, easing(progress));
          }

          triggerEvent('update');
        }

        lastTime = now;

        if (progress >= 1) {
          if (shouldLoop) {
            startTime = now;
            triggerEvent('loop');
          } else {
            stopLoop(animationId);
            triggerEvent('end');
          }
        }
      }

      // Set the frameRate
      function fps (input) {
        frameRate = 1000 / input;
        return self;
      }

      // Set from values
      function from (input) {
        fromValues = Immutable.fromJS(input);
        return self;
      }

      // Set to values
      function to (input) {
        toValues = Immutable.fromJS(input);
        return self;
      }

      // Set animation duration
      function duration (input) {
        durationMillis = input;
        return self;
      }

      // Set easing
      function ease (input) {
        if (typeof input !== 'function') {
          throw new Error('Ease must be a function, instead got: ' + (typeof input));
        }

        easing = input;
        return self;
      }

      // Set whether the animation should loop
      function loop (input) {
        shouldLoop = input;
        return self;
      }

      function mapInvertValues (value) {
        if (Immutable.Iterable.isIterable(value)) {
          return value.map(mapInvertValues);
        }

        return value * -1;
      }

      // Invert animation values
      function invert () {
        if (Immutable.Iterable.isIterable(toValues)) {
          toValues = toValues.map(mapInvertValues);
        } else {
          toValues = mapInvertValues(toValues);
        }

        return self;
      }

      // Play animation in reverse
      function reverse () {
        var temp = fromValues;
        fromValues = toValues;
        toValues = temp;
        return self;
      }

      // Start or resume animation
      function start () {
        if (typeof pausedAfter !== 'undefined') {
          startTime = performance.now() - pausedAfter;
        } else {
          startTime = performance.now();
        }

        lastTime = startTime;
        pausedAfter = undefined;

        triggerEvent('start');

        startLoop(animationId, animationLoop);

        return self;
      }

      // Stop animation and resume from beginning
      function stop () {
        triggerEvent('stop');
        stopLoop(animationId);
        pausedAfter = undefined;
        startTime = undefined;
        return self;
      }

      // Pause animation and resume from this point
      function pause () {
        triggerEvent('pause');
        stopLoop(animationId);
        pausedAfter = startTime - performance.now();
        startTime = undefined;
        return self;
      }

      // Add event listener
      function bind (type, callback) {
        if (!events.has(type)) {
          throw new Error('Unknown event: ' + type);
        }

        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        var eventsOfType = events.get(type, Immutable.List());

        if (!eventsOfType.includes(callback)) {
          events = events.set(type, eventsOfType.push(callback));
        }

        return self;
      }

      // Remove event listener
      function unbind (type, callback) {
        if (!events.has(type)) {
          throw new Error('Unknown event: ' + type);
        }

        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        var eventsOfType = events.get(type, Immutable.List());

        if (eventsOfType.includes(callback)) {
          events = events.set(type, eventsOfType.delete(eventsOfType.indexOf(callback)));
        }

        return self;
      }

      // Run on complete and automatically unbind
      function then (callback) {
        if (typeof callback !== 'function') {
          throw new Error('Callback must be a function, instead got: ' + (typeof callback));
        }

        function thenCallback (result) {
          callback(result);
          unbind('end', thenCallback);
        }

        bind('end', thenCallback);

        return self;
      }

      self.fps = fps;
      self.from = from;
      self.to = to;
      self.duration = duration;
      self.ease = ease;
      self.loop = loop;
      self.invert = invert;
      self.reverse = reverse;
      self.start = start;
      self.stop = self.reset = stop;
      self.pause = pause;
      self.bind = self.on = bind;
      self.unbind = self.off = unbind;
      self.then = then;
    }

    return {
      Animation: Animation,
      Easing: Easing
    };

  }

  // Export for commonjs / browserify
  if (typeof exports === 'object' && typeof module !== 'undefined') {
    module.exports = getSlik(require('immutable')); // eslint-disable-line no-undef
  // Export for amd / require
  } else if (typeof define === 'function' && define.amd) { // eslint-disable-line no-undef
    define(['immutable'], function (Immutable) { // eslint-disable-line no-undef
      return getSlik(Immutable);
    });
  // Export globally
  } else {
    var root;

    if (typeof window !== 'undefined') {
      root = window;
    } else if (typeof global !== 'undefined') {
      root = global; // eslint-disable-line no-undef
    } else if (typeof self !== 'undefined') {
      root = self; // eslint-disable-line no-undef
    } else {
      root = this;
    }

    root.Slik = getSlik(root.Immutable);
  }

})();
<|endoftext|>


<commit_before>import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import webpack from 'webpack';

/** removes falsy items from array */
const array = (...target) => target.filter((item) => item);

export default ({dev}) => ({
    entry: array(
        dev && 'react-hot-loader/patch',
        'babel-polyfill',
        './src/',
    ),
    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
        filename: dev ? '[name].js' : '[name].[chunkhash].js',
    },
    plugins: array(
        new HtmlWebpackPlugin({
            template: 'src/index.html',
            inject: true,
        }),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production'),
        }),
        !dev && new webpack.NoEmitOnErrorsPlugin(),
        !dev && new webpack.optimize.UglifyJsPlugin({
            output: {
                comments: false,
            },
        }),
        !dev && new ExtractTextPlugin('style.[chunkhash].css'),
    ),
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                options: {
                    presets: [
                        ['es2015', {modules: false}],
                        'stage-1',
                        'react',
                    ],
                    cacheDirectory: true,
                },
            },
            {
                test: /\.less$/,
                loader: dev
                    ? ['style-loader', 'css-loader', 'less-loader']
                    : ExtractTextPlugin.extract({
                        fallbackLoader: 'style-loader',
                        loader: ['css-loader', 'less-loader'],
                    }),
            },
        ],
    },
});
<commit_msg> #10 webpack upgrade aftermath<commit_after>import path from 'path';
import HtmlWebpackPlugin from 'html-webpack-plugin';
import ExtractTextPlugin from 'extract-text-webpack-plugin';
import webpack from 'webpack';

/** removes falsy items from array */
const array = (...target) => target.filter((item) => item);

export default ({dev}) => ({
    entry: array(
        dev && 'react-hot-loader/patch',
        'babel-polyfill',
        './src/',
    ),
    output: {
        path: path.resolve(__dirname, 'dist'),
        publicPath: '/',
        filename: dev ? '[name].js' : '[name].[chunkhash].js',
    },
    plugins: array(
        new HtmlWebpackPlugin({
            template: 'src/index.html',
            inject: true,
        }),
        new webpack.DefinePlugin({
            'process.env.NODE_ENV': JSON.stringify(dev ? 'development' : 'production'),
        }),
        !dev && new webpack.NoEmitOnErrorsPlugin(),
        !dev && new webpack.optimize.UglifyJsPlugin({
            output: {
                comments: false,
            },
        }),
        !dev && new ExtractTextPlugin('style.[chunkhash].css'),
    ),
    module: {
        rules: [
            {
                test: /\.js$/,
                loader: 'babel-loader',
                exclude: /node_modules/,
                options: {
                    presets: [
                        ['es2015', {modules: false}],
                        'stage-1',
                        'react',
                    ],
                    cacheDirectory: true,
                },
            },
            {
                test: /\.less$/,
                loader: dev
                    ? ['style-loader', 'css-loader', 'less-loader']
                    : ExtractTextPlugin.extract({
                        fallback: 'style-loader',
                        use: ['css-loader', 'less-loader'],
                    }),
            },
        ],
    },
});
<|endoftext|>


<commit_before>#!/usr/bin/env node
'use strict';

/**
 * Node Dependencies
 */
const fs = require('fs')
const stdio = require('stdio');
const tumblrLksDownldr = require('tumblr-lks-downldr');
const ProgressBar = require('progress');

/**
 * Module Globals
 */
let downloadBar;

const args = stdio.getopt(
  {
    'url': {
      key: 'u',
      args: 1,
      description: 'Valid Tumblr URL.',
      mandatory: true
    },
    'postsToLoad': {
      key: 'l',
      args: 1,
      description: 'Number of liked posts that you want to download.'
    },
    'path': {
      key: 'p',
      args: 1,
      description: 'Path where the images will be saved.'
    }
  }
);

const trackDownloadProgress = () => {
  downloadBar.tick();
  if (downloadBar.complete) {
    console.log('\n       Done!');
  }
};

const params = {
  url: args.url,
  onStart: (info) => {
    console.log(`
      ┌┬┐┬ ┬┌┬┐┌┐ ┬  ┬─┐   ┬  ┬┌─┌─┐  ┌┬┐┌─┐┬ ┬┌┐┌┬  ┌┬┐┬─┐   ┌─┐┬  ┬
       │ │ ││││├┴┐│  ├┬┘───│  ├┴┐└─┐───│││ ││││││││   ││├┬┘───│  │  │
       ┴ └─┘┴ ┴└─┘┴─┘┴└─   ┴─┘┴ ┴└─┘  ─┴┘└─┘└┴┘┘└┘┴─┘─┴┘┴└─   └─┘┴─┘┴
       2.0.4

       Tumblr Blog      :   ${args.url}
       Saving in        :   ${args.path || process.cwd()}
       Posts to load    :   ${info.postsToLoad}

       Note: Be patient if you requested a lot of posts.

       Loading list in memory...
    `);
  },
  onFetch: (info) => {
    if(info.downloadedPosts === Number(args.postsToLoad)){
      downloadBar = new ProgressBar(
        `       Downloading: :percent Current Image: :current Total: ${info.imagesToDownload} Estimated Time: :etas`,
        {
          total: info.imagesToDownload
        }
      );
    }
  },
  onSuccess: (info) => {
    trackDownloadProgress();
  },
  onError: (error, info) => {
    trackDownloadProgress();
  }
};

if(args.postsToLoad && args.postsToLoad < 1){
  process.exit();
}

if(args.postsToLoad){
  params.postsToLoad = args.postsToLoad;
}

if(args.path){
  params.path = args.path;
}

tumblrLksDownldr.setGlobalParams(
  params
);

tumblrLksDownldr.getLikedPosts();
<commit_msg>add offset arg<commit_after>#!/usr/bin/env node
'use strict';

/**
 * Node Dependencies
 */
const fs = require('fs')
const stdio = require('stdio');
const tumblrLksDownldr = require('tumblr-lks-downldr');
const ProgressBar = require('progress');

/**
 * Module Globals
 */
let downloadBar;

const args = stdio.getopt(
  {
    'url': {
      key: 'u',
      args: 1,
      description: 'Valid Tumblr URL.',
      mandatory: true
    },
    'postsToLoad': {
      key: 'l',
      args: 1,
      description: 'Number of liked posts that you want to download.'
    },
    'postsOffset': {
      key: 'o',
      args: 1,
      description: 'Which liked posts to start at (where 0 is most recent).'
    },
    'path': {
      key: 'p',
      args: 1,
      description: 'Path where the images will be saved.'
    }
  }
);

const trackDownloadProgress = () => {
  downloadBar.tick();
  if (downloadBar.complete) {
    console.log('\n       Done!');
  }
};

const params = {
  url: args.url,
  onStart: (info) => {
    console.log(`
      ┌┬┐┬ ┬┌┬┐┌┐ ┬  ┬─┐   ┬  ┬┌─┌─┐  ┌┬┐┌─┐┬ ┬┌┐┌┬  ┌┬┐┬─┐   ┌─┐┬  ┬
       │ │ ││││├┴┐│  ├┬┘───│  ├┴┐└─┐───│││ ││││││││   ││├┬┘───│  │  │
       ┴ └─┘┴ ┴└─┘┴─┘┴└─   ┴─┘┴ ┴└─┘  ─┴┘└─┘└┴┘┘└┘┴─┘─┴┘┴└─   └─┘┴─┘┴
       2.0.4

       Tumblr Blog      :   ${args.url}
       Saving in        :   ${args.path || process.cwd()}
       Posts to load    :   ${info.postsToLoad}
       Offset posts by    :  ${args.postsOffset}

       Note: Be patient if you requested a lot of posts.

       Loading list in memory...
    `);
  },
  onFetch: (info) => {
    if(info.downloadedPosts === Number(args.postsToLoad)){
      downloadBar = new ProgressBar(
        `       Downloading: :percent Current Image: :current Total: ${info.imagesToDownload} Estimated Time: :etas`,
        {
          total: info.imagesToDownload
        }
      );
    }
  },
  onSuccess: (info) => {
    trackDownloadProgress();
  },
  onError: (error, info) => {
    trackDownloadProgress();
  }
};

if(args.postsToLoad && args.postsToLoad < 1){
  process.exit();
}

if(args.postsToLoad){
  params.postsToLoad = args.postsToLoad;
}

if(args.postsOffset){
  params.postsOffset = args.postsOffset;
}

if(args.path){
  params.path = args.path;
}

tumblrLksDownldr.setGlobalParams(
  params
);

tumblrLksDownldr.getLikedPosts();
<|endoftext|>