code: function cartesian(list)
  {
    var last, init, keys, product = [];
  
    if (Array.isArray(list))
    {
      init = [];
      last = list.length - 1;
    }
    else if (typeof list == 'object' && list !== null)
    {
      init = {};
      keys = Object.keys(list);
      last = keys.length - 1;
    }
    else
    {
      throw new TypeError('Expecting an Array or an Object, but `' + (list === null ? 'null' : typeof list) + '` provided.');
    }
  
    function add(row, i)
    {
      var j, k, r;
  
      k = keys ? keys[i] : i;
  
      // either array or not, not expecting objects here
      Array.isArray(list[k]) || (typeof list[k] == 'undefined' ? list[k] = [] : list[k] = [list[k]]);
  
      for (j=0; j < list[k].length; j++)
      {
        r = clone(row);
        store(r, list[k][j], k);
  
        if (i >= last)
        {
          product.push(r);
        }
        else
        {
          add(r, i + 1);
        }
      }
    }
  
    add(init, 0);
  
    return product;
  }
docs: Creates cartesian product of the provided properties
  
  @param   {object|array} list - list of (array) properties or array of arrays
  @returns {array} all the combinations of the properties