{"size": 8543, "ext": "js", "max_stars_repo_path": "icepick.js", "max_stars_repo_name": "quintype/icepick", "max_stars_repo_stars_event_min_datetime": null, "max_stars_repo_stars_event_max_datetime": null, "max_issues_repo_path": "icepick.js", "max_issues_repo_name": "quintype/icepick", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "icepick.js", "max_forks_repo_name": "quintype/icepick", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": null, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 24.4085714286, "max_line_length": 80, "alphanum_fraction": 0.6397050217}
/**
 * This allows you to work with object hierarchies that have been frozen
 * with Object.freeze().  "get" operations can use the normal JS syntax,
 * but operations that modify the data will have to return partial copies of
 * the structure. The portions of the structure that did not change will
 * === their previous values.
 *
 * Inspired by clojure/mori and Immutable.js
 */

"use strict";

var i = exports;

// we only care about objects or arrays for now
function weCareAbout(val) {
  return null !== val &&
    (Array.isArray(val) ||
      // This will skip objects created with `new Foo()`
      // and objects created with `Object.create(proto)`
      // The benefit is ignoring DOM elements and event emitters,
      // which are often circular.
      isObjectLike(val));
}

function isObjectLike(val) {
  return typeof val === "object" &&
    val.constructor === Object &&
    Object.getPrototypeOf(val) === Object.prototype;
}


function arrayClone(arr) {
  var index = 0,
    length = arr.length,
    result = Array(length);

  for (; index < length; index += 1) {
    result[index] = arr[index];
  }
  return result;
}

function objClone(obj) {
  var index = 0,
    keys = Object.keys(obj),
    length = keys.length,
    key,
    result = {};

  for (; index < length; index += 1) {
    key = keys[index];
    result[key] = obj[key];
  }
  return result;
}

function clone(coll) {
  if (Array.isArray(coll)) {
    return arrayClone(coll);
  } else {
    return objClone(coll);
  }
}

function freezeIfNeeded(coll) {
  if (
      weCareAbout(coll) &&
      (
        !Object.isFrozen(coll) &&
        process.env.NODE_ENV !== "production"
      )) {
    return baseFreeze(coll, []);
  }
  return coll;
}

function _freeze(coll) {
  if (process.env.NODE_ENV === "production") {
    return coll;
  }
  return Object.freeze(coll);
}

function baseFreeze(coll, prevNodes) {
  if (prevNodes.some(function (node) { return node === coll; })) {
    throw new Error("object has a reference cycle");
  }

  Object.freeze(coll);
  prevNodes.push(coll);
  Object.keys(coll).forEach(function (key) {
    var prop = coll[key];
    if (weCareAbout(prop)) {
      baseFreeze(prop, prevNodes);
    }
  });
  prevNodes.pop();

  return coll;
}

/**
 * recrursively freeze an object and all its child objects
 * @param  {Object|Array} coll
 * @return {Object|Array}
 */
exports.freeze = function freeze(coll) {
  if (process.env.NODE_ENV === "production") {
    return coll;
  }
  return baseFreeze(coll, []);
};

/**
 * recursively un-freeze an object, by cloning frozen collections
 * @param  {[type]} coll [description]
 * @return {[type]}      [description]
 */
exports.thaw = function thaw(coll) {
  if (weCareAbout(coll) && Object.isFrozen(coll)) {
    var newColl = clone(coll);
    Object.keys(newColl).forEach(function (key) {
      newColl[key] = thaw(newColl[key]);
    });
    return newColl;
  }
  return coll;
};

/**
 * set a value on an object or array
 * @param  {Object|Array}  coll
 * @param  {String|Number} key   Key or index
 * @param  {Object}        value
 * @return {Object|Array}        new object hierarchy with modifications
 */
exports.assoc = function assoc(coll, key, value) {
  var newObj = clone(coll);

  newObj[key] = freezeIfNeeded(value);

  return _freeze(newObj);

};
exports.set = exports.assoc;

/**
 * un-set a value on an object or array
 * @param  {Object|Array}  coll
 * @param  {String|Number} key  Key or Index
 * @return {Object|Array}       New object or array
 */
exports.dissoc = function dissoc(coll, key) {
  var newObj = clone(coll);

  delete newObj[key];

  return _freeze(newObj);
};
exports.unset = exports.dissoc;

exports.update = function update(coll, callback) {
  return _freeze(callback(coll));
};

/**
 * set a value deep in a hierarchical structure
 * @param  {Object|Array} coll
 * @param  {Array}        path    A list of keys to traverse
 * @param  {Object}       value
 * @return {Object|Array}       new object hierarchy with modifications
 */
exports.assocIn = function assocIn(coll, path, value) {
  var key0 = path[0];
  if (path.length === 1) {
    // simplest case is a 1-element array.  Just a simple assoc.
    return i.assoc(coll, key0, value);
  } else {
    // break the problem down.  Assoc this object with the first key
    // and the result of assocIn with the rest of the keys
    return i.assoc(coll, key0, assocIn(coll[key0] || {}, path.slice(1), value));
  }
};
exports.setIn = exports.assocIn;

/**
 * get an object from a hierachy based on an array of keys
 * @param  {Object|Array} coll
 * @param  {Array}        path    list of keys
 * @return {Object}       value, or undefined
 */
function baseGet(coll, path) {
  return (path || []).reduce(function (curr, key) {
    if (!curr) { return; }
    return curr[key];
  }, coll);
}

exports.getIn = baseGet;

/**
 * Update a value in a hierarchy
 * @param  {Object|Array}   coll
 * @param  {Array}          path     list of keys
 * @param  {Function} callback The existing value with be passed to this.
 *                             Return the new value to set
 * @return {Object|Array}      new object hierarchy with modifications
 */
exports.updateIn = function updateIn(coll, path, callback) {
  var existingVal = baseGet(coll, path);
  return i.assocIn(coll, path, callback(existingVal));
};


// generate wrappers for the mutative array methods
["push", "unshift", "pop", "shift", "reverse", "sort"]
.forEach(function (methodName) {
  exports[methodName] = function (arr, val) {
    var newArr = arrayClone(arr);

    newArr[methodName](freezeIfNeeded(val));

    return _freeze(newArr);
  };

  exports[methodName].displayName = "icepick." + methodName;
});

// splice is special because it is variadic
exports.splice = function splice(arr/*, args*/) {
  var newArr = arrayClone(arr),
    args = rest(arguments).map(freezeIfNeeded);

  newArr.splice.apply(newArr, args);

  return _freeze(newArr);
};

// slice is non-mutative
exports.slice = function slice(arr, arg1, arg2) {
  var newArr = arr.slice(arg1, arg2);

  return _freeze(newArr);
};

["map", "filter"].forEach(function (methodName) {
  exports[methodName] = function (fn, arr) {
    var newArr = arr[methodName](fn);

    return _freeze(newArr);
  };

  exports[methodName].displayName = "icepick." + methodName;
});

exports.extend =
exports.assign = function assign(/*...objs*/) {
  var newObj = _slice(arguments).reduce(singleAssign, {});

  return _freeze(newObj);
};

function singleAssign(obj1, obj2) {
  return Object.keys(obj2).reduce(function (obj, key) {
    obj[key] = freezeIfNeeded(obj2[key]);
    return obj;
  }, obj1);
}

exports.merge = merge;
function merge(target, source) {
  if (target == null || source == null) {
    return target;
  }
  return Object.keys(source).reduce(function (obj, key) {
    var sourceVal = source[key];
    var targetVal = obj[key];

    if (weCareAbout(sourceVal) && weCareAbout(targetVal)) {
      // if they are both frozen and reference equal, assume they are deep equal
      if ((
            (Object.isFrozen(sourceVal) && Object.isFrozen(targetVal)) ||
            process.env.NODE_ENV === "production"
          ) &&
          sourceVal === targetVal) {
        return obj;
      }
      if (Array.isArray(sourceVal)) {
        return i.assoc(obj, key, sourceVal);
      }
      // recursively merge pairs of objects
      return assocIfDifferent(obj, key, merge(targetVal, sourceVal));
    }

    // primitive values, stuff with prototypes
    return assocIfDifferent(obj, key, sourceVal);
  }, target);
}

function assocIfDifferent(target, key, value) {
  if (target[key] === value) {
    return target;
  }
  return i.assoc(target, key, value);
}

function _slice(array, start) {
  var begin = start || 0;
  var len = array.length;
  len -= begin;
  len = len < 0 ? 0 : len;
  var result = new Array(len);
  for (var i = 0; i < len; i += 1) {
    result[i] = array[i + begin];
  }
  return result;
}


function rest(args) {
  return _slice(args, 1);
}


var chainProto = {
  value: function value() {
    return this.val;
  }
};

Object.keys(exports).forEach(function (methodName) {
  chainProto[methodName] = function (/*...args*/) {
    var args = _slice(arguments);
    args.unshift(this.val);
    this.val = exports[methodName].apply(null, args);
    return this;
  };
});

exports.chain = function chain(val) {
  var wrapped = Object.create(chainProto);
  wrapped.val = val;
  return wrapped;
};

// for testing
exports._weCareAbout = weCareAbout;
exports._slice = _slice;
