diff --git a/constraints.txt b/constraints.txt
index 46df2f70..6c287837 100644
--- a/constraints.txt
+++ b/constraints.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# ./scripts/update_dependencies.sh
@@ -10,8 +10,6 @@ alabaster==0.7.13
# via
# dlgr-griduniverse
# sphinx
-appnope==0.1.3
- # via ipython
apscheduler==3.10.1
# via dallinger
asttokens==2.2.1
@@ -26,11 +24,13 @@ babel==2.12.1
# via sphinx
backcall==0.2.0
# via ipython
+black==23.7.0
+ # via dlgr-griduniverse
blinker==1.6.2
# via flask
-boto3==1.28.5
+boto3==1.28.11
# via dallinger
-botocore==1.31.5
+botocore==1.31.11
# via
# boto3
# s3transfer
@@ -42,7 +42,7 @@ cached-property==1.5.2
# via dallinger
cachetools==5.3.1
# via tox
-certifi==2023.5.7
+certifi==2023.7.22
# via
# requests
# selenium
@@ -54,6 +54,7 @@ charset-normalizer==3.2.0
# via requests
click==8.1.6
# via
+ # black
# dallinger
# flask
# pip-tools
@@ -84,13 +85,10 @@ docutils==0.20.1
# recommonmark
# sphinx
exceptiongroup==1.1.2
- # via
- # pytest
- # trio
- # trio-websocket
+ # via trio-websocket
executing==1.2.0
# via stack-data
-faker==19.1.0
+faker==19.2.0
# via
# dallinger
# dlgr-griduniverse
@@ -129,7 +127,7 @@ greenlet==2.0.2
# dallinger
# gevent
# sqlalchemy
-gunicorn==21.1.0
+gunicorn==21.2.0
# via dallinger
h11==0.14.0
# via wsproto
@@ -172,6 +170,8 @@ mccabe==0.7.0
# via flake8
mock==5.1.0
# via dlgr-griduniverse
+mypy-extensions==1.0.0
+ # via black
numpy==1.24.4
# via
# dallinger
@@ -180,6 +180,7 @@ outcome==1.2.0
# via trio
packaging==23.1
# via
+ # black
# build
# gunicorn
# pyproject-api
@@ -188,6 +189,8 @@ packaging==23.1
# tox
parso==0.8.3
# via jedi
+pathspec==0.11.1
+ # via black
pexpect==4.8.0
# via
# dallinger
@@ -200,6 +203,7 @@ pip-tools==7.1.0
# dlgr-griduniverse
platformdirs==3.9.1
# via
+ # black
# tox
# virtualenv
pluggy==1.2.0
@@ -280,7 +284,7 @@ snowballstemmer==2.2.0
# via sphinx
sortedcontainers==2.4.0
# via trio
-sphinx==7.0.1
+sphinx==7.1.0
# via
# dlgr-griduniverse
# recommonmark
@@ -313,14 +317,6 @@ tenacity==8.2.2
# via dallinger
timeago==1.0.16
# via dallinger
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-api
- # pyproject-hooks
- # pytest
- # tox
tox==4.6.4
# via dlgr-griduniverse
traitlets==5.9.0
@@ -348,7 +344,7 @@ urllib3==1.26.16
# selenium
user-agents==2.2.0
# via dallinger
-virtualenv==20.24.0
+virtualenv==20.24.2
# via tox
wcwidth==0.2.6
# via prompt-toolkit
@@ -356,7 +352,7 @@ werkzeug==2.3.6
# via
# flask
# flask-login
-wheel==0.40.0
+wheel==0.41.0
# via pip-tools
wsproto==1.2.0
# via trio-websocket
diff --git a/dev-requirements.txt b/dev-requirements.txt
index a8635b11..0736e68c 100644
--- a/dev-requirements.txt
+++ b/dev-requirements.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# ./scripts/update_dependencies.sh
@@ -10,8 +10,6 @@ alabaster==0.7.13
# via
# dlgr-griduniverse
# sphinx
-appnope==0.1.3
- # via ipython
apscheduler==3.10.1
# via dallinger
asttokens==2.2.1
@@ -26,11 +24,13 @@ babel==2.12.1
# via sphinx
backcall==0.2.0
# via ipython
+black==23.7.0
+ # via dlgr-griduniverse
blinker==1.6.2
# via flask
-boto3==1.28.5
+boto3==1.28.11
# via dallinger
-botocore==1.31.5
+botocore==1.31.11
# via
# boto3
# s3transfer
@@ -42,7 +42,7 @@ cached-property==1.5.2
# via dallinger
cachetools==5.3.1
# via tox
-certifi==2023.5.7
+certifi==2023.7.22
# via
# requests
# selenium
@@ -54,6 +54,7 @@ charset-normalizer==3.2.0
# via requests
click==8.1.6
# via
+ # black
# dallinger
# flask
# pip-tools
@@ -84,13 +85,10 @@ docutils==0.20.1
# recommonmark
# sphinx
exceptiongroup==1.1.2
- # via
- # pytest
- # trio
- # trio-websocket
+ # via trio-websocket
executing==1.2.0
# via stack-data
-faker==19.1.0
+faker==19.2.0
# via
# dallinger
# dlgr-griduniverse
@@ -129,7 +127,7 @@ greenlet==2.0.2
# dallinger
# gevent
# sqlalchemy
-gunicorn==21.1.0
+gunicorn==21.2.0
# via dallinger
h11==0.14.0
# via wsproto
@@ -172,6 +170,8 @@ mccabe==0.7.0
# via flake8
mock==5.1.0
# via dlgr-griduniverse
+mypy-extensions==1.0.0
+ # via black
numpy==1.24.4
# via
# dallinger
@@ -180,6 +180,7 @@ outcome==1.2.0
# via trio
packaging==23.1
# via
+ # black
# build
# gunicorn
# pyproject-api
@@ -188,6 +189,8 @@ packaging==23.1
# tox
parso==0.8.3
# via jedi
+pathspec==0.11.1
+ # via black
pexpect==4.8.0
# via
# dallinger
@@ -200,6 +203,7 @@ pip-tools==7.1.0
# dlgr-griduniverse
platformdirs==3.9.1
# via
+ # black
# tox
# virtualenv
pluggy==1.2.0
@@ -280,7 +284,7 @@ snowballstemmer==2.2.0
# via sphinx
sortedcontainers==2.4.0
# via trio
-sphinx==7.0.1
+sphinx==7.1.0
# via
# dlgr-griduniverse
# recommonmark
@@ -313,14 +317,6 @@ tenacity==8.2.2
# via dallinger
timeago==1.0.16
# via dallinger
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-api
- # pyproject-hooks
- # pytest
- # tox
tox==4.6.4
# via dlgr-griduniverse
traitlets==5.9.0
@@ -348,7 +344,7 @@ urllib3==1.26.16
# selenium
user-agents==2.2.0
# via dallinger
-virtualenv==20.24.0
+virtualenv==20.24.2
# via tox
wcwidth==0.2.6
# via prompt-toolkit
@@ -356,7 +352,7 @@ werkzeug==2.3.6
# via
# flask
# flask-login
-wheel==0.40.0
+wheel==0.41.0
# via pip-tools
wsproto==1.2.0
# via trio-websocket
diff --git a/dlgr/griduniverse/bots.py b/dlgr/griduniverse/bots.py
index f799985c..70be1e43 100644
--- a/dlgr/griduniverse/bots.py
+++ b/dlgr/griduniverse/bots.py
@@ -22,7 +22,7 @@
from .maze_utils import positions_to_maze, maze_to_graph, find_path_astar
-logger = logging.getLogger('griduniverse')
+logger = logging.getLogger("griduniverse")
class BaseGridUniverseBot(BotBase):
@@ -35,12 +35,12 @@ class BaseGridUniverseBot(BotBase):
def complete_questionnaire(self):
"""Complete the standard debriefing form randomly."""
- difficulty = Select(self.driver.find_element_by_id('difficulty'))
+ difficulty = Select(self.driver.find_element_by_id("difficulty"))
difficulty.select_by_value(str(random.randint(1, 7)))
- engagement = Select(self.driver.find_element_by_id('engagement'))
+ engagement = Select(self.driver.find_element_by_id("engagement"))
engagement.select_by_value(str(random.randint(1, 7)))
try:
- fun = Select(self.driver.find_element_by_id('fun'))
+ fun = Select(self.driver.find_element_by_id("fun"))
# This is executed by the IEC_demo.py script...
# No need to fill out a random value.
fun.select_by_value(str(0))
@@ -49,9 +49,11 @@ def complete_questionnaire(self):
return True
def get_wait_time(self):
- """ Return a random wait time approximately average to
+ """Return a random wait time approximately average to
MEAN_KEY_INTERVAL but never more than MAX_KEY_INTERVAL"""
- return min(random.expovariate(1.0 / self.MEAN_KEY_INTERVAL), self.MAX_KEY_INTERVAL)
+ return min(
+ random.expovariate(1.0 / self.MEAN_KEY_INTERVAL), self.MAX_KEY_INTERVAL
+ )
def wait_for_grid(self):
"""Blocks until the grid is visible"""
@@ -63,12 +65,12 @@ def wait_for_grid(self):
def get_js_variable(self, variable_name):
"""Return an arbitrary JavaScript variable from the browser"""
try:
- script = 'return window.{};'.format(variable_name)
+ script = "return window.{};".format(variable_name)
result = self.driver.execute_script(script)
if result is None:
# In some cases (older remote Firefox)
# we need to use window.wrappedJSObject
- script = 'return window.wrappedJSObject.{};'.format(variable_name)
+ script = "return window.wrappedJSObject.{};".format(variable_name)
result = self.driver.execute_script(script)
except WebDriverException:
result = None
@@ -88,8 +90,11 @@ def get_player_id(self):
def food_positions(self):
"""Return a list of food coordinates"""
try:
- return [tuple(item['position']) for item in self.state['food']
- if item['maturity'] > 0.5]
+ return [
+ tuple(item["position"])
+ for item in self.state["food"]
+ if item["maturity"] > 0.5
+ ]
except (AttributeError, TypeError, KeyError):
return []
@@ -97,16 +102,14 @@ def food_positions(self):
def wall_positions(self):
"""Return a list of wall coordinates"""
try:
- return [tuple(item['position']) for item in self.state['walls']]
+ return [tuple(item["position"]) for item in self.state["walls"]]
except (AttributeError, TypeError, KeyError):
return []
@property
def player_positions(self):
"""Return a dictionary that maps player id to their coordinates"""
- return {
- player['id']: player['position'] for player in self.state['players']
- }
+ return {player["id"]: player["position"] for player in self.state["players"]}
@property
def my_position(self):
@@ -128,7 +131,7 @@ def send_next_key(self, grid):
# to the grid element; it's needed to avoid a
# "cannot focus element" error with chromedriver
try:
- if self.driver.desired_capabilities['browserName'] == 'chrome':
+ if self.driver.desired_capabilities["browserName"] == "chrome":
action = ActionChains(self.driver).move_to_element(grid)
action.click().send_keys(self.get_next_key()).perform()
else:
@@ -144,10 +147,10 @@ def participate(self):
"""
self.wait_for_quorum()
if self._skip_experiment:
- self.log('Participant overrecruited. Skipping experiment.')
+ self.log("Participant overrecruited. Skipping experiment.")
return True
self.wait_for_grid()
- self.log('Bot player started')
+ self.log("Bot player started")
# Wait for state to be available
self.state = None
@@ -162,17 +165,20 @@ def participate(self):
expected_finish_time = datetime.datetime.now() + datetime.timedelta(days=1)
while self.is_still_on_grid:
-
# The proposed finish time is how many seconds we think remain plus the current time
proposed_finish_time = datetime.datetime.now() + datetime.timedelta(
- seconds=self.grid['remaining_time']
+ seconds=self.grid["remaining_time"]
)
# Update the expected finish time iff it is earlier than we thought
expected_finish_time = min(expected_finish_time, proposed_finish_time)
# If we expected to finish more than 30 seconds ago then bail out
now = datetime.datetime.now()
- if expected_finish_time + datetime.timedelta(seconds=self.END_BUFFER_SECONDS) < now:
+ if (
+ expected_finish_time
+ + datetime.timedelta(seconds=self.END_BUFFER_SECONDS)
+ < now
+ ):
return True
gevent.sleep(self.get_wait_time())
@@ -236,10 +242,10 @@ def translate_directions(self, directions):
"""Convert a string of letters representing cardinal directions
to a tuple of Selenium arrow keys"""
lookup = {
- 'N': Keys.UP,
- 'S': Keys.DOWN,
- 'E': Keys.RIGHT,
- 'W': Keys.LEFT,
+ "N": Keys.UP,
+ "S": Keys.DOWN,
+ "E": Keys.RIGHT,
+ "W": Keys.LEFT,
}
return tuple(map(lookup.get, directions))
@@ -264,17 +270,11 @@ def distance(self, origin, endpoint):
graph = self._graph
except AttributeError:
self._maze = maze = positions_to_maze(
- self.wall_positions,
- self.state['rows'],
- self.state['columns']
+ self.wall_positions, self.state["rows"], self.state["columns"]
)
self._graph = graph = maze_to_graph(maze)
result = find_path_astar(
- maze,
- tuple(origin),
- tuple(endpoint),
- max_iterations=10000,
- graph=graph
+ maze, tuple(origin), tuple(endpoint), max_iterations=10000, graph=graph
)
if result:
distance = result[0]
@@ -314,50 +314,47 @@ def _make_socket(self):
from dallinger.experiment_server.sockets import chat_backend
self.redis = dallinger.db.redis_conn
- chat_backend.subscribe(self, 'griduniverse')
+ chat_backend.subscribe(self, "griduniverse")
- self.publish({
- 'type': 'connect',
- 'player_id': self.participant_id
- })
+ self.publish({"type": "connect", "player_id": self.participant_id})
def send(self, message):
"""Redis handler to receive a message from the griduniverse channel to this bot."""
- channel, payload = message.split(':', 1)
+ channel, payload = message.split(":", 1)
data = json.loads(payload)
- if channel == 'quorum':
- handler = 'handle_quorum'
+ if channel == "quorum":
+ handler = "handle_quorum"
else:
- handler = 'handle_{}'.format(data['type'])
+ handler = "handle_{}".format(data["type"])
getattr(self, handler, lambda x: None)(data)
def publish(self, message):
"""Sends a message from this bot to the `griduniverse_ctrl` channel."""
- self.redis.publish('griduniverse_ctrl', json.dumps(message))
+ self.redis.publish("griduniverse_ctrl", json.dumps(message))
def handle_state(self, data):
"""Receive a grid state update an store it"""
- if 'grid' in data:
+ if "grid" in data:
# grid is a json encoded dictionary, we want to selectively
# update this rather than overwrite it as not all grid changes
# are sent each time (such as food and walls)
- data['grid'] = json.loads(data['grid'])
- if 'grid' not in self.grid:
- self.grid['grid'] = {}
- self.grid['grid'].update(data['grid'])
- data['grid'] = self.grid['grid']
+ data["grid"] = json.loads(data["grid"])
+ if "grid" not in self.grid:
+ self.grid["grid"] = {}
+ self.grid["grid"].update(data["grid"])
+ data["grid"] = self.grid["grid"]
self.grid.update(data)
def handle_stop(self, data):
"""Receive an update that the round has finished and mark the
remaining time as zero"""
- self.grid['remaining_time'] = 0
+ self.grid["remaining_time"] = 0
def handle_quorum(self, data):
"""Update an instance attribute when the quorum is reached, so it
can be checked in wait_for_quorum().
"""
- if 'q' in data and data['q'] == data['n']:
+ if "q" in data and data["q"] == data["n"]:
self.log("Quorum fulfilled... unleashing bot.")
self._quorum_reached = True
@@ -365,7 +362,7 @@ def handle_quorum(self, data):
def is_still_on_grid(self):
"""Returns True if the bot is still on an active grid,
otherwise False"""
- return self.grid.get('remaining_time', 0) > 0.25
+ return self.grid.get("remaining_time", 0) > 0.25
def send_next_key(self):
"""Determines the message to send that corresponds to
@@ -375,27 +372,27 @@ def send_next_key(self):
message = {}
if key == Keys.UP:
message = {
- 'type': "move",
- 'player_id': self.participant_id,
- 'move': 'up',
+ "type": "move",
+ "player_id": self.participant_id,
+ "move": "up",
}
elif key == Keys.DOWN:
message = {
- 'type': "move",
- 'player_id': self.participant_id,
- 'move': 'down',
+ "type": "move",
+ "player_id": self.participant_id,
+ "move": "down",
}
elif key == Keys.LEFT:
message = {
- 'type': "move",
- 'player_id': self.participant_id,
- 'move': 'left',
+ "type": "move",
+ "player_id": self.participant_id,
+ "move": "left",
}
elif key == Keys.RIGHT:
message = {
- 'type': "move",
- 'player_id': self.participant_id,
- 'move': 'right',
+ "type": "move",
+ "player_id": self.participant_id,
+ "move": "right",
}
if message:
self.publish(message)
@@ -405,11 +402,11 @@ def on_signup(self, data):
super(HighPerformanceBaseGridUniverseBot, self).on_signup(data)
# We may have been the player to complete the quorum, in which case
# we won't have to wait for status from the backend.
- if data['quorum']['n'] == data['quorum']['q']:
+ if data["quorum"]["n"] == data["quorum"]["q"]:
self._quorum_reached = True
# overrecruitment is handled by web ui, so high perf bots need to
# do that handling here instead.
- if data['participant']['status'] == u'overrecruited':
+ if data["participant"]["status"] == "overrecruited":
self._skip_experiment = True
def wait_for_quorum(self):
@@ -430,7 +427,7 @@ def wait_for_grid(self):
self.grid = {}
self._make_socket()
while True:
- if self.grid and self.grid['remaining_time']:
+ if self.grid and self.grid["remaining_time"]:
break
gevent.sleep(0.001)
@@ -439,9 +436,9 @@ def get_js_variable(self, variable_name):
in the browser using our accumulated state.
The only values of variable_name supported are 'state' and 'ego'"""
- if variable_name == 'state':
- return self.grid['grid']
- elif variable_name == 'ego':
+ if variable_name == "state":
+ return self.grid["grid"]
+ elif variable_name == "ego":
return self.participant_id
def get_player_id(self):
@@ -457,16 +454,7 @@ class RandomBot(HighPerformanceBaseGridUniverseBot):
"""A bot that plays griduniverse randomly"""
#: The Selenium keys that this bot will choose between
- VALID_KEYS = [
- Keys.UP,
- Keys.DOWN,
- Keys.RIGHT,
- Keys.LEFT,
- Keys.SPACE,
- 'r',
- 'b',
- 'y'
- ]
+ VALID_KEYS = [Keys.UP, Keys.DOWN, Keys.RIGHT, Keys.LEFT, Keys.SPACE, "r", "b", "y"]
def get_next_key(self):
"""Randomly press one of Up, Down, Left, Right, space, r, b or y"""
@@ -623,7 +611,7 @@ def get_logical_targets(self):
seen_players = set()
seen_food = set()
choices = {}
- for (player_id, food_id) in best_choices:
+ for player_id, food_id in best_choices:
if player_id in seen_players:
continue
if food_id in seen_food:
@@ -688,7 +676,7 @@ def Bot(*args, **kwargs):
"""
config = get_config()
- bot_implementation = config.get('bot_policy', u'RandomBot')
+ bot_implementation = config.get("bot_policy", "RandomBot")
bot_class = globals().get(bot_implementation, None)
if bot_class and issubclass(bot_class, BotBase):
return bot_class(*args, **kwargs)
diff --git a/dlgr/griduniverse/distributions.py b/dlgr/griduniverse/distributions.py
index 4f4e20e4..06ca1dd4 100644
--- a/dlgr/griduniverse/distributions.py
+++ b/dlgr/griduniverse/distributions.py
@@ -49,7 +49,7 @@ def vertical_gradient_probability_distribution(rows, columns, *args):
def edge_bias_probability_distribution(rows, columns, *args):
- """Do the inverse to a normal distribution """
+ """Do the inverse to a normal distribution"""
mu = rows / 2 # mean
sigma = 15 # standard deviation
row = numpy.random.normal(mu, sigma)
@@ -57,7 +57,7 @@ def edge_bias_probability_distribution(rows, columns, *args):
valid = False
while not valid:
if row > mu and column > mu:
- row = (mu + numpy.random.normal(mu, sigma))
+ row = mu + numpy.random.normal(mu, sigma)
column = random.randint(0, columns - 1)
elif row > mu and column < mu:
row = abs(numpy.random.normal(mu, sigma) - mu)
diff --git a/dlgr/griduniverse/experiment.py b/dlgr/griduniverse/experiment.py
index ca564605..283c48e0 100644
--- a/dlgr/griduniverse/experiment.py
+++ b/dlgr/griduniverse/experiment.py
@@ -1,5 +1,6 @@
"""The Griduniverse."""
+import collections
import datetime
import flask
import gevent
@@ -15,6 +16,7 @@
import yaml
from cached_property import cached_property
+from dataclasses import dataclass, field
from faker import Factory
from sqlalchemy import create_engine
from sqlalchemy import func
@@ -38,102 +40,98 @@
logger = logging.getLogger(__file__)
-GAME_CONFIG_FILE = 'game_config.yml'
+GAME_CONFIG_FILE = "game_config.yml"
# Make bot importable without triggering style warnings
Bot = Bot
GU_PARAMS = {
- 'network': unicode,
- 'max_participants': int,
- 'bot_policy': unicode,
- 'num_rounds': int,
- 'time_per_round': float,
- 'instruct': bool,
- 'columns': int,
- 'rows': int,
- 'window_columns': int,
- 'window_rows': int,
- 'block_size': int,
- 'padding': int,
- 'chat_visibility_threshold': float,
- 'spatial_chat': bool,
- 'visibility': int,
- 'visibility_ramp_time': int,
- 'background_animation': bool,
- 'player_overlap': bool,
- 'leaderboard_group': bool,
- 'leaderboard_individual': bool,
- 'leaderboard_time': int,
- 'motion_speed_limit': float,
- 'motion_auto': bool,
- 'motion_cost': float,
- 'motion_tremble_rate': float,
- 'show_chatroom': bool,
- 'show_grid': bool,
- 'others_visible': bool,
- 'num_colors': int,
- 'mutable_colors': bool,
- 'costly_colors': bool,
- 'pseudonyms': bool,
- 'pseudonyms_locale': unicode,
- 'pseudonyms_gender': unicode,
- 'contagion': int,
- 'contagion_hierarchy': bool,
- 'walls_density': float,
- 'walls_contiguity': float,
- 'walls_visible': bool,
- 'initial_score': int,
- 'dollars_per_point': float,
- 'tax': float,
- 'relative_deprivation': float,
- 'frequency_dependence': float,
- 'frequency_dependent_payoff_rate': float,
- 'donation_amount': int,
- 'donation_individual': bool,
- 'donation_group': bool,
- 'donation_ingroup': bool,
- 'donation_public': bool,
- 'num_food': int,
- 'respawn_food': bool,
- 'food_visible': bool,
- 'food_reward': int,
- 'food_pg_multiplier': float,
- 'food_growth_rate': float,
- 'food_maturation_speed': float,
- 'food_maturation_threshold': float,
- 'food_planting': bool,
- 'food_planting_cost': int,
- 'food_probability_distribution': unicode,
- 'seasonal_growth_rate': float,
- 'difi_question': bool,
- 'difi_group_label': unicode,
- 'difi_group_image': unicode,
- 'fun_survey': bool,
- 'pre_difi_question': bool,
- 'pre_difi_group_label': unicode,
- 'pre_difi_group_image': unicode,
- 'leach_survey': bool,
- 'intergroup_competition': float,
- 'intragroup_competition': float,
- 'identity_signaling': bool,
- 'identity_starts_visible': bool,
- 'score_visible': bool,
- 'alternate_consumption_donation': bool,
- 'use_identicons': bool,
- 'build_walls': bool,
- 'wall_building_cost': int,
- 'donation_multiplier': float,
- 'num_recruits': int,
- 'state_interval': float,
+ "network": unicode,
+ "max_participants": int,
+ "bot_policy": unicode,
+ "num_rounds": int,
+ "time_per_round": float,
+ "instruct": bool,
+ "columns": int,
+ "rows": int,
+ "window_columns": int,
+ "window_rows": int,
+ "block_size": int,
+ "padding": int,
+ "chat_visibility_threshold": float,
+ "spatial_chat": bool,
+ "visibility": int,
+ "visibility_ramp_time": int,
+ "background_animation": bool,
+ "player_overlap": bool,
+ "leaderboard_group": bool,
+ "leaderboard_individual": bool,
+ "leaderboard_time": int,
+ "motion_speed_limit": float,
+ "motion_auto": bool,
+ "motion_cost": float,
+ "motion_tremble_rate": float,
+ "show_chatroom": bool,
+ "show_grid": bool,
+ "others_visible": bool,
+ "num_colors": int,
+ "mutable_colors": bool,
+ "costly_colors": bool,
+ "pseudonyms": bool,
+ "pseudonyms_locale": unicode,
+ "pseudonyms_gender": unicode,
+ "contagion": int,
+ "contagion_hierarchy": bool,
+ "walls_density": float,
+ "walls_contiguity": float,
+ "walls_visible": bool,
+ "initial_score": int,
+ "dollars_per_point": float,
+ "tax": float,
+ "relative_deprivation": float,
+ "frequency_dependence": float,
+ "frequency_dependent_payoff_rate": float,
+ "donation_amount": int,
+ "donation_individual": bool,
+ "donation_group": bool,
+ "donation_ingroup": bool,
+ "donation_public": bool,
+ "difi_question": bool,
+ "difi_group_label": unicode,
+ "difi_group_image": unicode,
+ "fun_survey": bool,
+ "pre_difi_question": bool,
+ "pre_difi_group_label": unicode,
+ "pre_difi_group_image": unicode,
+ "leach_survey": bool,
+ "intergroup_competition": float,
+ "intragroup_competition": float,
+ "identity_signaling": bool,
+ "identity_starts_visible": bool,
+ "score_visible": bool,
+ "alternate_consumption_donation": bool,
+ "use_identicons": bool,
+ "build_walls": bool,
+ "wall_building_cost": int,
+ "donation_multiplier": float,
+ "num_recruits": int,
+ "state_interval": float,
+}
+
+DEFAULT_ITEM_CONFIG = {
+ 1: {
+ "item_id": 1,
+ "name": "Food",
+ "calories": 1,
+ }
}
class PluralFormatter(string.Formatter):
def format_field(self, value, format_spec):
- if format_spec.startswith('plural'):
- words = format_spec.split(',')
- if value == 1 or value == '1' or value == 1.0:
+ if format_spec.startswith("plural"):
+ words = format_spec.split(",")
+ if value == 1 or value == "1" or value == 1.0:
return words[1]
else:
return words[2]
@@ -155,14 +153,8 @@ def softmax(vector, temperature=1):
class Gridworld(object):
"""A Gridworld in the Griduniverse."""
- player_color_names = [
- "BLUE",
- "YELLOW",
- "ORANGE",
- "RED",
- "PURPLE",
- "TEAL"
- ]
+
+ player_color_names = ["BLUE", "YELLOW", "ORANGE", "RED", "PURPLE", "TEAL"]
player_colors = [
[0.50, 0.86, 1.00],
@@ -170,148 +162,135 @@ class Gridworld(object):
[0.91, 0.50, 0.02],
[0.64, 0.11, 0.31],
[0.85, 0.60, 0.85],
- [0.77, 0.96, 0.90]
+ [0.77, 0.96, 0.90],
]
GREEN = [0.51, 0.69, 0.61]
WHITE = [1.00, 1.00, 1.00]
wall_locations = None
- food_locations = None
+ item_locations = None
walls_updated = True
- food_updated = True
+ items_updated = True
def __new__(cls, **kwargs):
- if not hasattr(cls, 'instance'):
+ if not hasattr(cls, "instance"):
cls.instance = super(Gridworld, cls).__new__(cls)
return cls.instance
def __init__(self, **kwargs):
# If Singleton is already initialized, do nothing
- if hasattr(self, 'num_players'):
+ if hasattr(self, "num_players"):
return
- self.log_event = kwargs.get('log_event', lambda x: None)
+ self.log_event = kwargs.get("log_event", lambda x: None)
# Players
- self.num_players = kwargs.get('max_participants', 3)
+ self.num_players = kwargs.get("max_participants", 3)
# Rounds
- self.num_rounds = kwargs.get('num_rounds', 1)
- self.time_per_round = kwargs.get('time_per_round', 300)
+ self.num_rounds = kwargs.get("num_rounds", 1)
+ self.time_per_round = kwargs.get("time_per_round", 300)
# Instructions
- self.instruct = kwargs.get('instruct', True)
+ self.instruct = kwargs.get("instruct", True)
# Grid
- self.columns = kwargs.get('columns', 25)
- self.rows = kwargs.get('rows', 25)
- self.window_columns = kwargs.get('window_columns', min(self.columns, 25))
- self.window_rows = kwargs.get('window_rows', min(self.rows, 25))
- self.block_size = kwargs.get('block_size', 10)
- self.padding = kwargs.get('padding', 1)
- self.chat_visibility_threshold = kwargs.get('chat_visibility_threshold', 0.4)
- self.spatial_chat = kwargs.get('spatial_chat', False)
- self.visibility = kwargs.get('visibility', 40)
- self.visibility_ramp_time = kwargs.get('visibility_ramp_time', 4)
- self.background_animation = kwargs.get('background_animation', True)
- self.player_overlap = kwargs.get('player_overlap', False)
+ self.columns = kwargs.get("columns", 25)
+ self.rows = kwargs.get("rows", 25)
+ self.window_columns = kwargs.get("window_columns", min(self.columns, 25))
+ self.window_rows = kwargs.get("window_rows", min(self.rows, 25))
+ self.block_size = kwargs.get("block_size", 10)
+ self.padding = kwargs.get("padding", 1)
+ self.chat_visibility_threshold = kwargs.get("chat_visibility_threshold", 0.4)
+ self.spatial_chat = kwargs.get("spatial_chat", False)
+ self.visibility = kwargs.get("visibility", 40)
+ self.visibility_ramp_time = kwargs.get("visibility_ramp_time", 4)
+ self.background_animation = kwargs.get("background_animation", True)
+ self.player_overlap = kwargs.get("player_overlap", False)
# Motion
- self.motion_speed_limit = kwargs.get('motion_speed_limit', 8)
- self.motion_auto = kwargs.get('motion_auto', False)
- self.motion_cost = kwargs.get('motion_cost', 0)
- self.motion_tremble_rate = kwargs.get('motion_tremble_rate', 0)
+ self.motion_speed_limit = kwargs.get("motion_speed_limit", 8)
+ self.motion_auto = kwargs.get("motion_auto", False)
+ self.motion_cost = kwargs.get("motion_cost", 0)
+ self.motion_tremble_rate = kwargs.get("motion_tremble_rate", 0)
# Components
- self.show_chatroom = kwargs.get('show_chatroom', False)
- self.show_grid = kwargs.get('show_grid', True)
+ self.show_chatroom = kwargs.get("show_chatroom", False)
+ self.show_grid = kwargs.get("show_grid", True)
# Identity
- self.others_visible = kwargs.get('others_visible', True)
- self.num_colors = kwargs.get('num_colors', 3)
- self.mutable_colors = kwargs.get('mutable_colors', False)
- self.costly_colors = kwargs.get('costly_colors', False)
- self.pseudonyms = kwargs.get('pseudonyms', True)
- self.pseudonyms_locale = kwargs.get('pseudonyms_locale', 'en_US')
- self.pseudonyms_gender = kwargs.get('pseudonyms_gender', None)
- self.contagion = kwargs.get('contagion', 0)
- self.contagion_hierarchy = kwargs.get('contagion_hierarchy', False)
- self.identity_signaling = kwargs.get('identity_signaling', False)
- self.identity_starts_visible = kwargs.get('identity_starts_visible',
- False)
- self.use_identicons = kwargs.get('use_identicons', False)
+ self.others_visible = kwargs.get("others_visible", True)
+ self.num_colors = kwargs.get("num_colors", 3)
+ self.mutable_colors = kwargs.get("mutable_colors", False)
+ self.costly_colors = kwargs.get("costly_colors", False)
+ self.pseudonyms = kwargs.get("pseudonyms", True)
+ self.pseudonyms_locale = kwargs.get("pseudonyms_locale", "en_US")
+ self.pseudonyms_gender = kwargs.get("pseudonyms_gender", None)
+ self.contagion = kwargs.get("contagion", 0)
+ self.contagion_hierarchy = kwargs.get("contagion_hierarchy", False)
+ self.identity_signaling = kwargs.get("identity_signaling", False)
+ self.identity_starts_visible = kwargs.get("identity_starts_visible", False)
+ self.use_identicons = kwargs.get("use_identicons", False)
# Walls
- self.walls_visible = kwargs.get('walls_visible', True)
- self.walls_density = kwargs.get('walls_density', 0.0)
- self.walls_contiguity = kwargs.get('walls_contiguity', 1.0)
- self.build_walls = kwargs.get('build_walls', False)
- self.wall_building_cost = kwargs.get('wall_building_cost', 0)
+ self.walls_visible = kwargs.get("walls_visible", True)
+ self.walls_density = kwargs.get("walls_density", 0.0)
+ self.walls_contiguity = kwargs.get("walls_contiguity", 1.0)
+ self.build_walls = kwargs.get("build_walls", False)
+ self.wall_building_cost = kwargs.get("wall_building_cost", 0)
self.wall_locations = {}
# Payoffs
- self.initial_score = kwargs.get('initial_score', 0)
- self.dollars_per_point = kwargs.get('dollars_per_point', 0.02)
- self.tax = kwargs.get('tax', 0.00)
- self.relative_deprivation = kwargs.get('relative_deprivation', 1)
- self.frequency_dependence = kwargs.get('frequency_dependence', 0)
+ self.initial_score = kwargs.get("initial_score", 0)
+ self.dollars_per_point = kwargs.get("dollars_per_point", 0.02)
+ self.tax = kwargs.get("tax", 0.00)
+ self.relative_deprivation = kwargs.get("relative_deprivation", 1)
+ self.frequency_dependence = kwargs.get("frequency_dependence", 0)
self.frequency_dependent_payoff_rate = kwargs.get(
- 'frequency_dependent_payoff_rate', 0)
- self.leaderboard_group = kwargs.get('leaderboard_group', False)
- self.leaderboard_individual = kwargs.get('leaderboard_individual', False)
- self.leaderboard_time = kwargs.get('leaderboard_time', 0)
+ "frequency_dependent_payoff_rate", 0
+ )
+ self.leaderboard_group = kwargs.get("leaderboard_group", False)
+ self.leaderboard_individual = kwargs.get("leaderboard_individual", False)
+ self.leaderboard_time = kwargs.get("leaderboard_time", 0)
# Donations
- self.donation_amount = kwargs.get('donation_amount', 0)
- self.donation_multiplier = kwargs.get('donation_multiplier', 1.0)
- self.donation_individual = kwargs.get('donation_individual', False)
- self.donation_group = kwargs.get('donation_group', False)
- self.donation_ingroup = kwargs.get('donation_ingroup', False)
- self.donation_public = kwargs.get('donation_public', False)
- self.intergroup_competition = kwargs.get('intergroup_competition', 1)
- self.intragroup_competition = kwargs.get('intragroup_competition', 1)
- self.score_visible = kwargs.get('score_visible', False)
+ self.donation_amount = kwargs.get("donation_amount", 0)
+ self.donation_multiplier = kwargs.get("donation_multiplier", 1.0)
+ self.donation_individual = kwargs.get("donation_individual", False)
+ self.donation_group = kwargs.get("donation_group", False)
+ self.donation_ingroup = kwargs.get("donation_ingroup", False)
+ self.donation_public = kwargs.get("donation_public", False)
+ self.intergroup_competition = kwargs.get("intergroup_competition", 1)
+ self.intragroup_competition = kwargs.get("intragroup_competition", 1)
+ self.score_visible = kwargs.get("score_visible", False)
self.alternate_consumption_donation = kwargs.get(
- 'alternate_consumption_donation', False)
-
- # Food
- self.num_food = kwargs.get('num_food', 8)
- self.respawn_food = kwargs.get('respawn_food', True)
- self.food_visible = kwargs.get('food_visible', True)
- self.food_reward = kwargs.get('food_reward', 1)
- self.food_pg_multiplier = kwargs.get('food_pg_multiplier', 1)
- self.food_growth_rate = kwargs.get('food_growth_rate', 1.00)
- self.food_maturation_speed = kwargs.get('food_maturation_speed', 1)
- self.food_maturation_threshold = kwargs.get(
- 'food_maturation_threshold', 0.0)
- self.food_planting = kwargs.get('food_planting', False)
- self.food_planting_cost = kwargs.get('food_planting_cost', 1)
- self.food_probability_distribution = kwargs.get('food_probability_distribution', 'random')
- self.seasonal_growth_rate = kwargs.get('seasonal_growth_rate', 1)
+ "alternate_consumption_donation", False
+ )
# Chat
self.chat_message_history = []
# Questionnaire
- self.difi_question = kwargs.get('difi_question', False)
- self.difi_group_label = kwargs.get('difi_group_label', 'Group')
- self.difi_group_image = kwargs.get('difi_group_image', '/static/images/group.jpg')
- self.fun_survey = kwargs.get('fun_survey', False)
- self.pre_difi_question = kwargs.get('pre_difi_question', False)
- self.pre_difi_group_label = kwargs.get('pre_difi_group_label', 'Group')
- self.pre_difi_group_image = kwargs.get('pre_difi_group_image', '/static/images/group.jpg')
- self.leach_survey = kwargs.get('leach_survey', False)
+ self.difi_question = kwargs.get("difi_question", False)
+ self.difi_group_label = kwargs.get("difi_group_label", "Group")
+ self.difi_group_image = kwargs.get(
+ "difi_group_image", "/static/images/group.jpg"
+ )
+ self.fun_survey = kwargs.get("fun_survey", False)
+ self.pre_difi_question = kwargs.get("pre_difi_question", False)
+ self.pre_difi_group_label = kwargs.get("pre_difi_group_label", "Group")
+ self.pre_difi_group_image = kwargs.get(
+ "pre_difi_group_image", "/static/images/group.jpg"
+ )
+ self.leach_survey = kwargs.get("leach_survey", False)
# Set some variables.
self.players = {}
- self.food_locations = {}
- self.food_consumed = []
- self.start_timestamp = kwargs.get('start_timestamp', None)
+ self.item_locations = {}
+ self.items_consumed = []
+ self.start_timestamp = kwargs.get("start_timestamp", None)
self.round = 0
- self.public_good = (
- (self.food_reward * self.food_pg_multiplier) / self.num_players
- )
if self.contagion_hierarchy:
self.contagion_hierarchy = range(self.num_colors)
@@ -321,23 +300,48 @@ def __init__(self, **kwargs):
self.color_costs = [2**i for i in range(self.num_colors)]
random.shuffle(self.color_costs)
- # get food spawning probability distribution function and args
- self.food_probability_info = {}
- self.probability_function_args = []
- parts = self.food_probability_distribution.split()
+ # Items and transitions
+ self.item_config = kwargs.get("item_config", DEFAULT_ITEM_CONFIG)
+ self.transition_config = kwargs.get("transition_config", {})
+ self.player_config = kwargs.get("player_config", {})
+
+ if self.player_config.get('available_colors'):
+ self.player_color_names = []
+ self.player_colors = []
+ for name, value in self.player_config['available_colors'].items():
+ self.player_color_names.append(name)
+ self.player_colors.append(value)
+
+ # Any maturing items?
+ self.includes_maturing_items = any(
+ item["maturation_threshold"] > 0.0 for item in self.item_config.values()
+ )
+
+ # Get item spawning probability distribution and public good calories
+ for item_type in self.item_config.values():
+ item_type["probability_function"], item_type["probability_function_args"] = self._get_probablity_func_for_config(item_type["probability_distribution"])
+
+ item_type["public_good"] = (
+ item_type["calories"]
+ * item_type["public_good_multiplier"]
+ / self.num_players
+ )
+
+ # Player probability distribution
+ self.player_config["probability_function"], self.player_config["probability_function_args"] = self._get_probablity_func_for_config(self.player_config["probability_distribution"])
+
+ def _get_probablity_func_for_config(self, probability_distribution):
+ probability_function_args = []
+ parts = probability_distribution.split()
if len(parts) > 1:
- self.food_probability_distribution = parts[0]
- self.probability_function_args = parts[1:]
- probability_distribution = "{}_probability_distribution".format(
- self.food_probability_distribution)
- self.food_probability_function = getattr(distributions,
- probability_distribution,
- None)
- if self.food_probability_function is None:
- logger.info(
- "Unknown food probability distribution: {}.".format(
- self.food_probability_distribution))
- self.food_probability_function = distributions.random_probability_distribution
+ probability_distribution = parts[0]
+ probability_function_args = parts[1:]
+ probability_distribution = f"{probability_distribution}_probability_distribution"
+ probability_function = getattr(distributions, probability_distribution, None)
+ if probability_function is None:
+ logger.info(f"Unknown item probability distribution: {probability_distribution}.")
+ probability_function = distributions.random_probability_distribution
+ return probability_function, probability_function_args
def can_occupy(self, position):
if self.player_overlap:
@@ -346,11 +350,11 @@ def can_occupy(self, position):
@property
def limited_player_colors(self):
- return self.player_colors[:self.num_colors]
+ return self.player_colors[: self.num_colors]
@property
def limited_player_color_names(self):
- return self.player_color_names[:self.num_colors]
+ return self.player_color_names[: self.num_colors]
@property
def elapsed_round_time(self):
@@ -373,12 +377,10 @@ def group_donation_enabled(self):
@property
def donation_enabled(self):
return (
- (
- self.group_donation_enabled or
- self.donation_individual or
- self.donation_public
- ) and bool(self.donation_amount)
- )
+ self.group_donation_enabled
+ or self.donation_individual
+ or self.donation_public
+ ) and bool(self.donation_amount)
@property
def is_even_round(self):
@@ -466,11 +468,11 @@ def compute_payoffs(self):
group_scores = []
for p in players:
group_info = player_groups.setdefault(
- p.color_idx, {'players': [], 'scores': [], 'total': 0}
+ p.color_idx, {"players": [], "scores": [], "total": 0}
)
- group_info['players'].append(p)
- group_info['scores'].append(p.score)
- group_info['total'] += p.score
+ group_info["players"].append(p)
+ group_info["scores"].append(p.score)
+ group_info["total"] += p.score
total_payoff += p.score
for g in range(len(self.player_colors)):
@@ -478,9 +480,9 @@ def compute_payoffs(self):
if not group_info:
group_scores.append(0)
continue
- ingroup_players = group_info['players']
- ingroup_scores = group_info['scores']
- group_scores.append(group_info['total'])
+ ingroup_players = group_info["players"]
+ ingroup_scores = group_info["scores"]
+ group_scores.append(group_info["total"])
intra_proportions = softmax(
ingroup_scores,
temperature=self.intragroup_competition,
@@ -499,16 +501,16 @@ def compute_payoffs(self):
def build_labyrinth(self):
if self.walls_density and not self.wall_locations:
start = time.time()
- logger.info('Building labyrinth:')
+ logger.info("Building labyrinth:")
walls = labyrinth(
columns=self.columns,
rows=self.rows,
density=self.walls_density,
contiguity=self.walls_contiguity,
)
- logger.info('Built {} walls in {} seconds.'.format(
- len(walls), time.time() - start
- ))
+ logger.info(
+ "Built {} walls in {} seconds.".format(len(walls), time.time() - start)
+ )
self.wall_locations = {tuple(w.position): w for w in walls}
def _start_if_ready(self):
@@ -524,7 +526,7 @@ def game_started(self):
def game_over(self):
return self.round >= self.num_rounds
- def serialize(self, include_walls=True, include_food=True):
+ def serialize(self, include_walls=True, include_items=True):
grid_data = {
"players": [player.serialize() for player in self.players.values()],
"round": self.round,
@@ -534,50 +536,56 @@ def serialize(self, include_walls=True, include_food=True):
}
if include_walls:
- grid_data['walls'] = [w.serialize() for w in self.wall_locations.values()]
- if include_food:
- grid_data['food'] = [f.serialize() for f in self.food_locations.values()]
+ grid_data["walls"] = [w.serialize() for w in self.wall_locations.values()]
+ if include_items:
+ grid_data["items"] = [f.serialize() for f in self.item_locations.values()]
return grid_data
def deserialize(self, state):
- if self.rows != state['rows'] or self.columns != state['columns']:
+ if self.rows != state["rows"] or self.columns != state["columns"]:
raise ValueError(
- 'State has wrong grid size ({}x{}, configured as {}x{})'.format(
- state['rows'], state['columns'], self.rows, self.columns,
- ))
- self.round = state['round']
+ "State has wrong grid size ({}x{}, configured as {}x{})".format(
+ state["rows"],
+ state["columns"],
+ self.rows,
+ self.columns,
+ )
+ )
+ self.round = state["round"]
# @@@ can't set donation_active because it's a property
# self.donation_active = state['donation_active']
self.players = {}
- for player_state in state['players']:
- player_state['color_name'] = player_state.pop('color', None)
+ for player_state in state["players"]:
+ player_state["color_name"] = player_state.pop("color", None)
player = Player(
pseudonym_locale=self.pseudonyms_locale,
pseudonym_gender=self.pseudonyms_gender,
grid=self,
- **player_state
+ **player_state,
)
self.players[player.id] = player
- if 'walls' in state:
+ if "walls" in state:
self.wall_locations = {}
- for wall_state in state['walls']:
+ for wall_state in state["walls"]:
if isinstance(wall_state, list):
- wall_state = {'position': wall_state}
+ wall_state = {"position": wall_state}
wall = Wall(**wall_state)
self.wall_locations[tuple(wall.position)] = wall
- if 'food' in state:
- self.food_locations = {}
- for food_state in state['food']:
- food = Food(maturation_speed=self.food_maturation_speed, **food_state)
- self.food_locations[tuple(food.position)] = food
+ if "items" in state:
+ self.item_locations = {}
+ for item_state in state["items"]:
+ # TODO verify this works at some point!
+ item_props = self.item_config[item_state["item_id"]]
+ obj = Item(item_config=item_props, **item_state)
+ self.item_locations[tuple(obj.position)] = obj
def instructions(self):
- color_costs = ''
- order = ''
+ color_costs = ""
+ order = ""
text = """
The objective of the game is to maximize your final payoff.
The game is played on a {g.columns} x {g.rows} grid, where each
player occupies one block. {g.time_per_round} seconds.
"""
else:
- text += " The game duration is {g.time_per_round} seconds."
+ text += (
+ " The game duration is {g.time_per_round} seconds."
+ )
if self.num_players > 1:
text += """There are {g.num_players} players participating
in the game."""
@@ -615,10 +625,13 @@ def instructions(self):
if self.mutable_colors:
text += " Players can change color using the 'c' key."
if self.costly_colors:
- costs = ['{c}, {p} points'.format(c=c, p=p)
- for p, c in zip(self.color_costs,
- self.limited_player_color_names)]
- color_costs = '; '.join(costs)
+ costs = [
+ "{c}, {p} points".format(c=c, p=p)
+ for p, c in zip(
+ self.color_costs, self.limited_player_color_names
+ )
+ ]
+ color_costs = "; ".join(costs)
text += """ Changing color has a different cost in
points for each color: {color_costs}."""
if self.contagion > 0:
@@ -627,8 +640,12 @@ def instructions(self):
blocks are of a different color, that player will take
on the color of the plurality."""
if self.contagion_hierarchy:
- order = ', '.join([self.limited_player_color_names[h]
- for h in self.contagion_hierarchy])
+ order = ", ".join(
+ [
+ self.limited_player_color_names[h]
+ for h in self.contagion_hierarchy
+ ]
+ )
text += """ However, there is a hierarchy of colors, so
that only players of some colors are susceptible to
changing color in this way. The hierarchy, from
@@ -670,29 +687,23 @@ def instructions(self):
text += """ Movement commands will be ignored almost all of the time,
and the player will move in a random direction instead."""
text += """
Players gain points by getting to squares that have
- food on them. Each piece of food is worth {g.food_reward}
- {g.food_reward:plural, point, points}. When the game starts there
- are {g.num_food} {g.num_food:plural, piece, pieces}
+ food on them. Each piece of food is worth x
+ points. When the game starts there
+ are n pieces
of food on the grid. Food is represented by a green"""
- if self.food_maturation_threshold > 0:
- text += " or brown"
+
+ text += " or brown"
text += " square: "
- if self.food_maturation_threshold > 0:
- text += " "
- if self.respawn_food:
- text += " Food is automatically respawned after it is consumed."
- if self.food_maturation_threshold > 0:
- text += """It will appear immediately, but not be consumable for
- some time, because it has a maturation period. It will show
- up as brown initially, and then as green when it matures."""
+ text += " "
+ text += " Food can be respawned after it is consumed."
+
+ text += """It will appear immediately, but may not be consumable for
+ some time if it has a maturation period. It will show
+ up as brown initially, and then as green when it matures."""
text += """ The location where the food will appear after respawning is
- is determined by the {g.food_probability_distribution}
- probability distribution."""
- if self.food_planting:
- text += " Players can plant more food by pressing the spacebar."
- if self.food_planting_cost > 0:
- text += """ The cost for planting food is {g.food_planting_cost}
- {g.food_planting_cost:plural, point, points}."""
+ is determined by the configured
+ probability distribution for each item type."""
+ text += " Players may be able to plant more food by pressing the spacebar."
text += "
"
if self.alternate_consumption_donation and self.num_rounds > 1:
text += """ Rounds will alternate between consumption and
@@ -731,77 +742,133 @@ def instructions(self):
if self.dollars_per_point > 0:
text += """
You will receive ${g.dollars_per_point} for each point
that you score at the end of the game.
"""
- return formatter.format(text,
- g=self,
- order=order,
- color_costs=color_costs,
- color_list=', '.join(self.limited_player_color_names))
+ return formatter.format(
+ text,
+ g=self,
+ order=order,
+ color_costs=color_costs,
+ color_list=", ".join(self.limited_player_color_names),
+ )
def consume(self):
- """Players consume the food."""
+ """Players consume the non-interactive items"""
consumed = 0
for player in self.players.values():
position = tuple(player.position)
- if position in self.food_locations:
- food = self.food_locations[position]
- if food.maturity < self.food_maturation_threshold:
+ if position in self.item_locations:
+ item = self.item_locations[position]
+ if item.interactive or not item.calories:
continue
- del self.food_locations[position]
- # Update existence and count of food.
- self.food_consumed.append(food)
- self.food_updated = True
- if self.respawn_food:
- self.spawn_food()
+ if item.maturity < item.maturation_threshold:
+ continue
+ del self.item_locations[position]
+ # Update existence and count of item.
+ self.items_consumed.append(item)
+ self.items_updated = True
+ if item.respawn:
+ # respawn same type of item.
+ self.spawn_item(item_id=item.item_id)
else:
- self.num_food -= 1
+ self.item_config[item.item_id]["item_count"] -= 1
# Update scores.
if player.color_idx > 0:
- reward = self.food_reward
+ calories = item.calories
else:
- reward = self.food_reward * self.relative_deprivation
+ calories = item.calories * self.relative_deprivation
- player.score += reward
+ player.score += calories
consumed += 1
- if consumed and self.public_good:
+ if consumed and item.public_good:
for player_to in self.players.values():
- player_to.score += self.public_good * consumed
+ player_to.score += item.public_good * consumed
+
+ def spawn_item(self, position=None, item_id=None):
+ """Respawn an item for a single position"""
+ if not item_id:
+ # No specific item type, pick randomly.
+ item_id = None
+ while item_id is None:
+ # As many loops as it takes until one is chosen.
+ for obj in self.item_config.values():
+ spawn_rate = random.random()
+ if spawn_rate < obj.get("spawn_rate", 0.1):
+ item_id = obj.get("item_id", 1)
- def spawn_food(self, position=None):
- """Respawn the food for a single position"""
if not position:
- position = self._random_empty_position()
+ position = self._random_empty_position(item_id)
- self.food_locations[tuple(position)] = Food(
- id=(len(self.food_locations) + len(self.food_consumed)),
+ item_props = self.item_config[item_id]
+ new_item = Item(
+ id=(len(self.item_locations) + len(self.items_consumed)),
position=position,
- maturation_speed=self.food_maturation_speed,
+ item_config=item_props,
+ )
+ self.item_locations[tuple(position)] = new_item
+ self.items_updated = True
+ logger.warning(f"Spawning new item: {new_item}")
+ self.log_event(
+ {
+ "type": "spawn item",
+ "position": position,
+ }
)
- self.food_updated = True
- self.log_event({
- 'type': 'spawn_food',
- 'position': position,
- })
-
- def food_changed(self, last_food):
- locations = self.food_locations
- if len(last_food) != len(locations):
+
+ def items_changed(self, last_items):
+ locations = self.item_locations
+ if len(last_items) != len(locations):
return True
- for food in last_food:
- position = tuple(food['position'])
+ for item in last_items:
+ position = tuple(item["position"])
if position not in locations:
return True
found = locations[position]
- if found.id != food['id'] or found.maturity != food['maturity']:
+ if found.id != item["id"] or found.maturity != item["maturity"]:
return True
return False
+ def replenish_items(self):
+ items_by_type = collections.defaultdict(list)
+ for item in self.item_locations.values():
+ items_by_type[item.item_id].append(item)
+
+ for item_type in self.item_config.values():
+ # Alternate positive and negative growth rates
+ seasonal_growth = item_type["seasonal_growth_rate"] ** (
+ -1 if self.round % 2 else 1
+ )
+ logger.warning(
+ f"item_type: {item_type['name']}, seasonal_growth: {seasonal_growth}"
+ )
+ # Compute how many items of this type we should have on the grid,
+ # ensuring it's not less than zero.
+ item_type["item_count"] = max(
+ min(
+ item_type["item_count"] * item_type["spawn_rate"] * seasonal_growth,
+ self.rows * self.columns,
+ ),
+ 0,
+ )
+ logger.warning(
+ f"item_type: {item_type['name']}, target count: {item_type['item_count']}"
+ )
+
+ # Only items of the same type.
+ items_of_this_type = items_by_type[item_type["item_id"]]
+
+ for i in range(int(round(item_type["item_count"]) - len(items_of_this_type))):
+ self.spawn_item(item_id=item_type["item_id"])
+
+ for i in range(len(items_of_this_type) - int(round(item_type["item_count"]))):
+ del self.item_locations[random.choice(items_of_this_type).position]
+ self.items_updated = True
+
def spawn_player(self, id=None, **kwargs):
"""Spawn a player."""
player = Player(
id=id,
- position=self._random_empty_position(),
+ position=self._random_empty_position(player=True),
num_possible_colors=self.num_colors,
motion_speed_limit=self.motion_speed_limit,
motion_cost=self.motion_cost,
@@ -810,23 +877,33 @@ def spawn_player(self, id=None, **kwargs):
pseudonym_locale=self.pseudonyms_locale,
pseudonym_gender=self.pseudonyms_gender,
grid=self,
- identity_visible=(not self.identity_signaling or
- self.identity_starts_visible),
- **kwargs
+ identity_visible=(
+ not self.identity_signaling or self.identity_starts_visible
+ ),
+ **kwargs,
)
self.players[id] = player
self._start_if_ready()
return player
- def _random_empty_position(self):
+ def _random_empty_position(self, item_id=None, player=False):
"""Select an empty cell at random, using the configured probability
distribution."""
rows = self.rows
columns = self.columns
empty_cell = False
- while (not empty_cell):
- position = self.food_probability_function(
- rows, columns, *self.probability_function_args)
+ if item_id:
+ prob_func = self.item_config[item_id]["probability_function"]
+ func_args = self.item_config[item_id]["probability_function_args"]
+ elif player:
+ prob_func = self.player_config["probability_function"]
+ func_args = self.player_config["probability_function_args"]
+ else:
+ prob_func = distributions.random_probability_distribution
+ func_args = []
+
+ while not empty_cell:
+ position = prob_func(rows, columns, *func_args)
empty_cell = self._empty(position)
return position
@@ -834,9 +911,9 @@ def _random_empty_position(self):
def _empty(self, position):
"""Determine whether a particular cell is empty."""
return not (
- self.has_player(position) or
- self.has_food(position) or
- self.has_wall(position)
+ self.has_player(position)
+ or self.has_item(position)
+ or self.has_wall(position)
)
def has_player(self, position):
@@ -845,8 +922,8 @@ def has_player(self, position):
return True
return False
- def has_food(self, position):
- return tuple(position) in self.food_locations
+ def has_item(self, position):
+ return tuple(position) in self.item_locations
def has_wall(self, position):
return tuple(position) in self.wall_locations
@@ -860,43 +937,56 @@ def spread_contagion(self):
colors.append(player.color)
plurality_color = max(colors, key=colors.count)
if colors.count(plurality_color) > len(colors) / 2.0:
- if (self.rank(plurality_color) <= self.rank(player.color)):
+ if self.rank(plurality_color) <= self.rank(player.color):
color_updates.append((player, plurality_color))
- for (player, color) in color_updates:
+ for player, color in color_updates:
player.color = color
def rank(self, color):
"""Where does this color fall on the color hierarchy?"""
if self.contagion_hierarchy:
- return self.contagion_hierarchy[
- Gridworld.player_colors.index(color)]
+ return self.contagion_hierarchy[Gridworld.player_colors.index(color)]
else:
return 1
-class Food(object):
- """Food."""
- def __init__(self, **kwargs):
- self.id = kwargs.get('id', uuid.uuid4())
- self.position = kwargs.get('position', [0, 0])
- self.color = kwargs.get('color')
- self.maturation_speed = kwargs.get('maturation_speed', 0.1)
- self.creation_timestamp = time.time()
+@dataclass(frozen=True)
+class Item:
+ """A generic object supporting configuration via a game_config.yml
+ definition.
+
+ All instances sharing an item_id will share a reference to the same
+ item_config, and values will be looked up from this common key/value map.
+ Only values that vary by instance will be stored on the object itself.
+ """
+ item_config: dict
+ id: int = field(default_factory=lambda: uuid.uuid4())
+ creation_timestamp: float = field(default_factory=time.time)
+ position: tuple = (0, 0)
+
+ def __post_init__(self):
+ object.__setattr__(self, "item_id", self.item_config["item_id"])
+
+ def __getattr__(self, name):
+ # Look up value from the item type's shared definition.
+ return self.item_config[name]
+
+ def __repr__(self):
+ return (
+ f"Item(name='{self.name}', item_id={self.item_id}, id={self.id}, "
+ f"position={self.position}, creation_timestamp={self.creation_timestamp})"
+ )
def serialize(self):
return {
"id": self.id,
+ "item_id": self.item_id,
"position": self.position,
"maturity": self.maturity,
- "color": self.color or self._maturity_to_rgb(self.maturity),
+ "creation_timestamp": self.creation_timestamp,
}
- def _maturity_to_rgb(self, maturity):
- B = [0.48, 0.42, 0.33] # Brown
- G = [0.54, 0.61, 0.06] # Green
- return [B[i] + maturity * (G[i] - B[i]) for i in range(3)]
-
@property
def maturity(self):
return round(1 - math.exp(-self._age * self.maturation_speed), 1)
@@ -907,10 +997,9 @@ def _age(self):
class IllegalMove(Exception):
- """A move sent from a client was denied by the server.
- """
+ """A move sent from a client was denied by the server."""
- def __init__(self, message=''):
+ def __init__(self, message=""):
self.message = message
@@ -920,20 +1009,20 @@ class Player(object):
def __init__(self, **kwargs):
super(Player, self).__init__()
- self.id = kwargs.get('id', uuid.uuid4())
- self.position = kwargs.get('position', [0, 0])
- self.motion_auto = kwargs.get('motion_auto', False)
- self.motion_direction = kwargs.get('motion_direction', 'right')
- self.motion_speed_limit = kwargs.get('motion_speed_limit', 8)
- self.num_possible_colors = kwargs.get('num_possible_colors', 2)
- self.motion_cost = kwargs.get('motion_cost', 0)
- self.motion_tremble_rate = kwargs.get('motion_tremble_rate', 0)
- self.grid = kwargs.get('grid', None)
- self.score = kwargs.get('score', 0)
- self.payoff = kwargs.get('payoff', 0)
- self.pseudonym_locale = kwargs.get('pseudonym_locale', 'en_US')
- self.identity_visible = kwargs.get('identity_visible', True)
- self.recruiter_id = kwargs.get('recruiter_id', '')
+ self.id = kwargs.get("id", uuid.uuid4())
+ self.position = kwargs.get("position", [0, 0])
+ self.motion_auto = kwargs.get("motion_auto", False)
+ self.motion_direction = kwargs.get("motion_direction", "right")
+ self.motion_speed_limit = kwargs.get("motion_speed_limit", 8)
+ self.num_possible_colors = kwargs.get("num_possible_colors", 2)
+ self.motion_cost = kwargs.get("motion_cost", 0)
+ self.motion_tremble_rate = kwargs.get("motion_tremble_rate", 0)
+ self.grid = kwargs.get("grid", None)
+ self.score = kwargs.get("score", 0)
+ self.payoff = kwargs.get("payoff", 0)
+ self.pseudonym_locale = kwargs.get("pseudonym_locale", "en_US")
+ self.identity_visible = kwargs.get("identity_visible", True)
+ self.recruiter_id = kwargs.get("recruiter_id", "")
self.add_wall = None
# Determine the player's color. We don't have access to the specific
@@ -941,10 +1030,10 @@ def __init__(self, **kwargs):
# We just find the index in the master list. This means it is possible
# to explicitly instantiate a player with an invalid colour, but only
# intentionally.
- if 'color' in kwargs:
- self.color_idx = Gridworld.player_colors.index(kwargs['color'])
- elif 'color_name' in kwargs:
- self.color_idx = Gridworld.player_color_names.index(kwargs['color_name'])
+ if "color" in kwargs:
+ self.color_idx = Gridworld.player_colors.index(kwargs["color"])
+ elif "color_name" in kwargs:
+ self.color_idx = Gridworld.player_color_names.index(kwargs["color_name"])
else:
self.color_idx = random.randint(0, self.num_possible_colors - 1)
@@ -954,24 +1043,19 @@ def __init__(self, **kwargs):
# Determine the player's profile.
self.fake = Factory.create(self.pseudonym_locale)
self.profile = self.fake.simple_profile(
- sex=kwargs.get('pseudonym_gender', None)
+ sex=kwargs.get("pseudonym_gender", None)
)
- self.name = kwargs.get('name', self.profile['name'])
- self.username = self.profile['username']
- self.gender = self.profile['sex']
- self.birthdate = self.profile['birthdate']
+ self.name = kwargs.get("name", self.profile["name"])
+ self.username = self.profile["username"]
+ self.gender = self.profile["sex"]
+ self.birthdate = self.profile["birthdate"]
self.motion_timestamp = 0
self.last_timestamp = 0
def tremble(self, direction):
"""Change direction with some probability."""
- directions = [
- "up",
- "down",
- "left",
- "right"
- ]
+ directions = ["up", "down", "left", "right"]
directions.remove(direction)
direction = random.choice(directions)
return direction
@@ -1039,30 +1123,26 @@ def move(self, direction, tremble_rate=None, timestamp=None):
new_wall = Wall(position=self.add_wall)
self.grid.wall_locations[tuple(new_wall.position)] = new_wall
self.add_wall = None
- wall_msg = {
- 'type': 'wall_built',
- 'wall': new_wall.serialize()
- }
+ wall_msg = {"type": "wall_built", "wall": new_wall.serialize()}
msgs["wall"] = wall_msg
return msgs
def is_neighbor(self, player, d=1):
"""Determine whether other player is adjacent."""
- manhattan_distance = (
- abs(self.position[0] - player.position[0]) +
- abs(self.position[1] - player.position[1])
+ manhattan_distance = abs(self.position[0] - player.position[0]) + abs(
+ self.position[1] - player.position[1]
)
- return (manhattan_distance <= d)
+ return manhattan_distance <= d
def neighbors(self, d=1):
"""Return all adjacent players."""
if self.grid is None:
return []
return [
- p for p in self.grid.players.values() if (
- self.is_neighbor(p, d=d) and (p is not self)
- )
+ p
+ for p in self.grid.players.values()
+ if (self.is_neighbor(p, d=d) and (p is not self))
]
def serialize(self):
@@ -1088,10 +1168,8 @@ def fermi(beta, p1, p2):
extra_routes = flask.Blueprint(
- 'extra_routes',
- __name__,
- template_folder='templates',
- static_folder='static')
+ "extra_routes", __name__, template_folder="templates", static_folder="static"
+)
@extra_routes.route("/consent")
@@ -1100,10 +1178,10 @@ def consent():
config = get_config()
return flask.render_template(
"consent.html",
- hit_id=flask.request.args['hit_id'],
- assignment_id=flask.request.args['assignment_id'],
- worker_id=flask.request.args['worker_id'],
- mode=config.get('mode'),
+ hit_id=flask.request.args["hit_id"],
+ assignment_id=flask.request.args["assignment_id"],
+ worker_id=flask.request.args["worker_id"],
+ mode=config.get("mode"),
)
@@ -1111,17 +1189,15 @@ def consent():
def serve_grid():
"""Return the game stage."""
config = get_config()
- return flask.render_template(
- "grid.html",
- app_id=config.get('id')
- )
+ return flask.render_template("grid.html", app_id=config.get("id"))
class Griduniverse(Experiment):
"""Define the structure of the experiment."""
- channel = 'griduniverse_ctrl'
+
+ channel = "griduniverse_ctrl"
state_count = 0
- replay_path = '/grid'
+ replay_path = "/grid"
def __init__(self, session=None):
"""Initialize the experiment."""
@@ -1133,28 +1209,48 @@ def __init__(self, session=None):
self.setup()
self.grid = Gridworld(
log_event=self.record_event,
- **self.config.as_dict()
+ item_config=self.item_config,
+ transition_config=self.transition_config,
+ player_config=self.player_config,
+ **self.config.as_dict(),
)
self.session.commit()
def configure(self):
super(Griduniverse, self).configure()
- self.num_participants = self.config.get('max_participants', 3)
+ self.num_participants = self.config.get("max_participants", 3)
self.quorum = self.num_participants
- self.initial_recruitment_size = self.config.get('num_recruits',
- self.num_participants)
- self.network_factory = self.config.get('network', 'FullyConnected')
+ self.initial_recruitment_size = self.config.get(
+ "num_recruits", self.num_participants
+ )
+ self.network_factory = self.config.get("network", "FullyConnected")
game_config_file = os.path.join(os.path.dirname(__file__), GAME_CONFIG_FILE)
- with open(game_config_file, 'r') as game_config_stream:
+ with open(game_config_file, "r") as game_config_stream:
self.game_config = yaml.safe_load(game_config_stream)
- self.object_config = {
- o['object_id']: o for o in self.game_config.get('objects', ())
- }
+ self.item_config = {o["item_id"]: o for o in self.game_config.get("items", ())}
+
+ # If any item is missing a key, add it with default value.
+ item_defaults = self.game_config.get("item_defaults", {})
+ for item in self.item_config.values():
+ for prop in item_defaults:
+ if prop not in item:
+ item[prop] = item_defaults[prop]
+
self.transition_config = {
- (t['actor_start'], t['target_start']): t for t in self.game_config.get('transitions', ())
+ (t["actor_start"], t["target_start"]): t
+ for t in self.game_config.get("transitions", ())
}
+ self.player_config = self.game_config.get("player_config")
+ # This is accessed by the grid.html template to load the configuration on the client side:
+ # TODO: could this instead be passed as an arg to the template in
+ # the /grid route?
+ self.item_config_json = json.dumps(self.item_config)
+ self.transition_config_json = json.dumps(
+ {"{}_{}".format(k[0], k[1]): v for k, v in self.transition_config.items()}
+ )
+
@classmethod
def extra_parameters(cls):
config = get_config()
@@ -1169,6 +1265,7 @@ def environment(self):
@cached_property
def socket_session(self):
from dallinger.db import db_url
+
engine = create_engine(db_url, pool_size=1000)
session = scoped_session(
sessionmaker(autocommit=False, autoflush=True, bind=engine)
@@ -1177,7 +1274,7 @@ def socket_session(self):
@property
def background_tasks(self):
- if self.config.get('replay', False):
+ if self.config.get("replay", False):
return []
return [
self.send_state_thread,
@@ -1186,17 +1283,12 @@ def background_tasks(self):
def create_network(self):
"""Create a new network by reading the configuration file."""
- class_ = getattr(
- dallinger.networks,
- self.network_factory
- )
+ class_ = getattr(dallinger.networks, self.network_factory)
return class_(max_size=self.num_participants + 1)
def create_node(self, participant, network):
try:
- return dallinger.models.Node(
- network=network, participant=participant
- )
+ return dallinger.models.Node(network=network, participant=participant)
finally:
if not self.networks(full=False):
# If there are no spaces left in our networks we can close
@@ -1228,36 +1320,37 @@ def bonus(self, participant):
if not data:
return 0.0
- return float("{0:.2f}".format(data['payoff']))
+ return float("{0:.2f}".format(data["payoff"]))
def bonus_reason(self):
- """The reason offered to the participant for giving the bonus.
- """
+ """The reason offered to the participant for giving the bonus."""
return (
"Thank you for participating! You earned a bonus based on your "
"performance in Griduniverse!"
)
def dispatch(self, msg):
- """Route to the appropriate method based on message type"""
+ """Route incoming messages to the appropriate method based on message type"""
mapping = {
- 'connect': self.handle_connect,
- 'disconnect': self.handle_disconnect,
+ "connect": self.handle_connect,
+ "disconnect": self.handle_disconnect,
}
- if not self.config.get('replay', False):
+ if not self.config.get("replay", False):
# Ignore these events in replay mode
- mapping.update({
- 'chat': self.handle_chat_message,
- 'change_color': self.handle_change_color,
- 'move': self.handle_move,
- 'donation_submitted': self.handle_donation,
- 'plant_food': self.handle_plant_food,
- 'toggle_visible': self.handle_toggle_visible,
- 'build_wall': self.handle_build_wall,
- })
-
- if msg['type'] in mapping:
- mapping[msg['type']](msg)
+ mapping.update(
+ {
+ "chat": self.handle_chat_message,
+ "change_color": self.handle_change_color,
+ "move": self.handle_move,
+ "donation_submitted": self.handle_donation,
+ "plant_food": self.handle_plant_food,
+ "toggle_visible": self.handle_toggle_visible,
+ "build_wall": self.handle_build_wall,
+ }
+ )
+
+ if msg["type"] in mapping:
+ mapping[msg["type"]](msg)
def send(self, raw_message):
"""Socket interface; point of entry for incoming messages.
@@ -1268,12 +1361,15 @@ def send(self, raw_message):
"""
message = self.parse_message(raw_message)
if message is not None:
- message['server_time'] = time.time()
+ message["server_time"] = time.time()
self.dispatch((message))
- if 'player_id' in message:
- self.record_event(message, message['player_id'])
+ if "player_id" in message:
+ self.record_event(message, message["player_id"])
def parse_message(self, raw_message):
+ """Strip the channel prefix off the raw message, then return
+ the parsed JSON.
+ """
if raw_message.startswith(self.channel + ":"):
body = raw_message.replace(self.channel + ":", "")
message = json.loads(body)
@@ -1283,7 +1379,7 @@ def parse_message(self, raw_message):
def record_event(self, details, player_id=None):
"""Record an event in the Info table."""
session = self.socket_session
- if player_id == 'spectator':
+ if player_id == "spectator":
return
elif player_id:
node_id = self.node_by_player_id[player_id]
@@ -1305,24 +1401,26 @@ def record_event(self, details, player_id=None):
def publish(self, msg):
"""Publish a message to all griduniverse clients"""
- self.redis_conn.publish('griduniverse', json.dumps(msg))
+ self.redis_conn.publish("griduniverse", json.dumps(msg))
def handle_connect(self, msg):
- player_id = msg['player_id']
- if self.config.get('replay', False):
+ player_id = msg["player_id"]
+ if self.config.get("replay", False):
# Force all participants to be specatators
- msg['player_id'] = 'spectator'
+ msg["player_id"] = "spectator"
if not self.grid.start_timestamp:
self.grid.start_timestamp = time.time()
- if player_id == 'spectator':
- logger.info('A spectator has connected.')
+ if player_id == "spectator":
+ logger.info("A spectator has connected.")
return
logger.info("Client {} has connected.".format(player_id))
client_count = len(self.grid.players)
logger.info("Grid num players: {}".format(self.grid.num_players))
if client_count < self.grid.num_players:
- participant = self.session.query(dallinger.models.Participant).get(player_id)
+ participant = self.session.query(dallinger.models.Participant).get(
+ player_id
+ )
network = self.get_network_for_participant(participant)
if network:
logger.info("Found an open network. Adding participant node...")
@@ -1336,40 +1434,42 @@ def handle_connect(self, msg):
# allocated to colours uniformly.
self.grid.spawn_player(
id=player_id,
- color_name=self.grid.limited_player_color_names[node.id % self.grid.num_colors],
+ color_name=self.grid.limited_player_color_names[
+ node.id % self.grid.num_colors
+ ],
recruiter_id=participant.recruiter_id,
)
else:
- logger.info(
- "No free network found for player {}".format(player_id)
- )
+ logger.info("No free network found for player {}".format(player_id))
def handle_disconnect(self, msg):
- logger.info('Client {} has disconnected.'.format(msg['player_id']))
+ logger.info("Client {} has disconnected.".format(msg["player_id"]))
def handle_chat_message(self, msg):
"""Publish the given message to all clients."""
message = {
- 'type': 'chat',
- 'message': msg,
+ "type": "chat",
+ "message": msg,
}
- self.grid.chat_message_history.append((
- self.grid.players[msg['player_id']],
- msg['server_time'],
- msg['contents'],
- ))
+ self.grid.chat_message_history.append(
+ (
+ self.grid.players[msg["player_id"]],
+ msg["server_time"],
+ msg["contents"],
+ )
+ )
# We only publish if it wasn't already broadcast
- if not msg.get('broadcast', False):
+ if not msg.get("broadcast", False):
self.publish(message)
def handle_change_color(self, msg):
- player = self.grid.players[msg['player_id']]
- color_name = msg['color']
+ player = self.grid.players[msg["player_id"]]
+ color_name = msg["color"]
color_idx = Gridworld.player_color_names.index(color_name)
old_color = Gridworld.player_color_names[player.color_idx]
- msg['old_color'] = old_color
- msg['new_color'] = color_name
+ msg["old_color"] = old_color
+ msg["new_color"] = color_name
if player.color_idx == color_idx:
return # Requested color change is no change at all.
@@ -1380,27 +1480,27 @@ def handle_change_color(self, msg):
else:
player.score -= self.grid.color_costs[color_idx]
- player.color = msg['color']
+ player.color = msg["color"]
player.color_idx = color_idx
player.color_name = color_name
message = {
- 'type': 'color_changed',
- 'player_id': msg['player_id'],
- 'old_color': old_color,
- 'new_color': player.color_name,
+ "type": "color_changed",
+ "player_id": msg["player_id"],
+ "old_color": old_color,
+ "new_color": player.color_name,
}
# Put the message back on the channel
self.publish(message)
- self.record_event(message, message['player_id'])
+ self.record_event(message, message["player_id"])
def handle_move(self, msg):
- player = self.grid.players[msg['player_id']]
+ player = self.grid.players[msg["player_id"]]
try:
- msgs = player.move(msg['move'], timestamp=msg.get('timestamp'))
+ msgs = player.move(msg["move"], timestamp=msg.get("timestamp"))
except IllegalMove:
error_msg = {
- 'type': 'move_rejection',
- 'player_id': player.id,
+ "type": "move_rejection",
+ "player_id": player.id,
}
self.publish(error_msg)
else:
@@ -1417,19 +1517,19 @@ def handle_donation(self, msg):
return
recipients = []
- recipient_id = msg['recipient_id']
+ recipient_id = msg["recipient_id"]
- if recipient_id.startswith('group:') and self.grid.group_donation_enabled:
+ if recipient_id.startswith("group:") and self.grid.group_donation_enabled:
color_id = recipient_id[6:]
recipients = self.grid.players_with_color(color_id)
- elif recipient_id == 'all' and self.grid.donation_public:
+ elif recipient_id == "all" and self.grid.donation_public:
recipients = self.grid.players.values()
elif self.grid.donation_individual:
recipient = self.grid.players.get(recipient_id)
if recipient:
recipients.append(recipient)
- donor = self.grid.players[msg['donor_id']]
- donation = msg['amount']
+ donor = self.grid.players[msg["donor_id"]]
+ donation = msg["amount"]
if donor.score >= donation and len(recipients):
donor.score -= donation
@@ -1439,32 +1539,34 @@ def handle_donation(self, msg):
for recipient in recipients:
recipient.score += donated
message = {
- 'type': 'donation_processed',
- 'donor_id': msg['donor_id'],
- 'recipient_id': msg['recipient_id'],
- 'amount': donation,
- 'received': donated
+ "type": "donation_processed",
+ "donor_id": msg["donor_id"],
+ "recipient_id": msg["recipient_id"],
+ "amount": donation,
+ "received": donated,
}
self.publish(message)
- self.record_event(message, message['donor_id'])
+ self.record_event(message, message["donor_id"])
def handle_plant_food(self, msg):
- player = self.grid.players[msg['player_id']]
- position = msg['position']
- can_afford = player.score >= self.grid.food_planting_cost
- if (can_afford and not self.grid.has_food(position)):
- player.score -= self.grid.food_planting_cost
- self.grid.spawn_food(position=position)
+ # Legacy. For now, take planting info from first defined item.
+ planting_cost = list(self.item_config.values())[0]["planting_cost"]
+ player = self.grid.players[msg["player_id"]]
+ position = msg["position"]
+ can_afford = player.score >= planting_cost
+ if can_afford and not self.grid.has_item(position):
+ player.score -= planting_cost
+ self.grid.spawn_item(position=position)
def handle_toggle_visible(self, msg):
- player = self.grid.players[msg['player_id']]
- player.identity_visible = msg['identity_visible']
+ player = self.grid.players[msg["player_id"]]
+ player.identity_visible = msg["identity_visible"]
def handle_build_wall(self, msg):
- player = self.grid.players[msg['player_id']]
- position = msg['position']
+ player = self.grid.players[msg["player_id"]]
+ position = msg["position"]
can_afford = player.score >= self.grid.wall_building_cost
- msg['success'] = can_afford
+ msg["success"] = can_afford
if can_afford:
player.score -= self.grid.wall_building_cost
player.add_wall = position
@@ -1475,50 +1577,49 @@ def send_state_thread(self):
last_player_count = 0
gevent.sleep(1.00)
last_walls = []
- last_food = []
+ last_items = []
# Sleep until we have walls
- while (self.grid.walls_density and not self.grid.wall_locations):
+ while self.grid.walls_density and not self.grid.wall_locations:
gevent.sleep(0.1)
while True:
- gevent.sleep(self.config.get('state_interval', 0.050))
+ gevent.sleep(self.config.get("state_interval", 0.050))
- # Send all food data once every 40 loops
- update_walls = update_food = False
+ # Send all item data once every 40 loops
+ update_walls = update_items = False
if (count % 50) == 0:
- update_food = True
+ update_items = True
count += 1
player_count = len(self.grid.players)
if not last_player_count or player_count != last_player_count:
update_walls = True
- update_food = True
+ update_items = True
last_player_count = player_count
if not last_walls:
update_walls = True
- if not last_food or self.grid.food_changed(last_food):
- update_food = True
+ if not last_items or self.grid.items_changed(last_items):
+ update_items = True
grid_state = self.grid.serialize(
- include_walls=update_walls,
- include_food=update_food
+ include_walls=update_walls, include_items=update_items
)
if update_walls:
- last_walls = grid_state['walls']
+ last_walls = grid_state["walls"]
- if update_food:
- last_food = grid_state['food']
+ if update_items:
+ last_items = grid_state["items"]
message = {
- 'type': 'state',
- 'grid': json.dumps(grid_state),
- 'count': count,
- 'remaining_time': self.grid.remaining_round_time,
- 'round': self.grid.round,
+ "type": "state",
+ "grid": json.dumps(grid_state),
+ "count": count,
+ "remaining_time": self.grid.remaining_round_time,
+ "round": self.grid.round,
}
self.publish(message)
@@ -1528,13 +1629,14 @@ def send_state_thread(self):
def game_loop(self):
"""Update the world state."""
gevent.sleep(0.1)
- if not self.config.get('replay', False):
+ if not self.config.get("replay", False):
self.grid.build_labyrinth()
- logger.info('Spawning food')
- for i in range(self.grid.num_food):
- if (i % 250) == 0:
- gevent.sleep(0.00001)
- self.grid.spawn_food()
+ logger.info("Spawning items")
+ for item_type in self.item_config.values():
+ for i in range(item_type["item_count"]):
+ if (i % 250) == 0:
+ gevent.sleep(0.00001)
+ self.grid.spawn_item(item_id=item_type["item_id"])
while not self.grid.game_started:
gevent.sleep(0.01)
@@ -1546,26 +1648,23 @@ def game_loop(self):
# Record grid state to database
state_data = self.grid.serialize(
include_walls=self.grid.walls_updated,
- include_food=self.grid.food_updated
- )
- state = self.environment.update(
- json.dumps(state_data),
- details=state_data
+ include_items=self.grid.items_updated,
)
+ state = self.environment.update(json.dumps(state_data), details=state_data)
self.socket_session.add(state)
self.socket_session.commit()
count += 1
self.grid.walls_updated = False
- self.grid.food_updated = False
+ self.grid.items_updated = False
gevent.sleep(0.010)
# TODO: Most of this code belongs in Gridworld; we're just looking
# at properties of that class and then telling it to do things based
# on the values.
- # Log food updates every hundred rounds to capture maturity changes
- if self.grid.food_maturation_threshold and (count % 100) == 0:
- self.grid.food_updated = True
+ # Log item updates every hundred rounds to capture maturity changes
+ if self.grid.includes_maturing_items and (count % 100) == 0:
+ self.grid.items_updated = True
now = time.time()
# Update motion.
@@ -1583,32 +1682,8 @@ def game_loop(self):
# Trigger time-based events.
if (now - previous_second_timestamp) > 1.000:
-
- # Grow or shrink the food stores.
-
- # Alternate positive and negative growth rates
- seasonal_growth = (
- self.grid.seasonal_growth_rate **
- (-1 if self.grid.round % 2 else 1)
- )
-
- # Compute how many food items we should have on the grid,
- # ensuring it's not less than zero.
- self.grid.num_food = max(min(
- self.grid.num_food *
- self.grid.food_growth_rate *
- seasonal_growth,
- self.grid.rows * self.grid.columns,
- ), 0)
-
- # TODO: self.grid.replenish_food()
- for i in range(int(round(self.grid.num_food) - len(self.grid.food_locations))):
- self.grid.spawn_food()
-
- # TODO: self.grid.prune_excess_food()
- for i in range(len(self.grid.food_locations) - int(round(self.grid.num_food))):
- del self.grid.food_locations[random.choice(self.grid.food_locations.keys())]
- self.grid.food_updated = True
+ # Grow or shrink the item stores.
+ self.grid.replenish_items()
abundances = {}
for player in self.grid.players.values():
@@ -1624,45 +1699,41 @@ def game_loop(self):
relative_frequency = (
1.0 * abundances[player.color] / len(self.grid.players)
)
- payoff = fermi(
- beta=self.grid.frequency_dependence,
- p1=relative_frequency,
- p2=0.5
- ) * self.grid.frequency_dependent_payoff_rate
+ payoff = (
+ fermi(
+ beta=self.grid.frequency_dependence,
+ p1=relative_frequency,
+ p2=0.5,
+ )
+ * self.grid.frequency_dependent_payoff_rate
+ )
player.score = max(player.score + payoff, 0)
previous_second_timestamp = now
self.grid.compute_payoffs()
-
game_round = self.grid.round
self.grid.check_round_completion()
if self.grid.round != game_round and not self.grid.game_over:
- self.publish({'type': 'new_round', 'round': self.grid.round})
- self.record_event({
- 'type': 'new_round',
- 'round': self.grid.round
- })
+ self.publish({"type": "new_round", "round": self.grid.round})
+ self.record_event({"type": "new_round", "round": self.grid.round})
- self.publish({'type': 'stop'})
+ self.publish({"type": "stop"})
self.socket_session.commit()
return
def player_feedback(self, data):
- engagement = int(json.loads(data.questions.list[-1][-1])['engagement'])
- difficulty = int(json.loads(data.questions.list[-1][-1])['difficulty'])
+ engagement = int(json.loads(data.questions.list[-1][-1])["engagement"])
+ difficulty = int(json.loads(data.questions.list[-1][-1])["difficulty"])
try:
- fun = int(json.loads(data.questions.list[-1][-1])['fun'])
+ fun = int(json.loads(data.questions.list[-1][-1])["fun"])
return engagement, difficulty, fun
except IndexError:
return engagement, difficulty
def replay_start(self):
- self.grid = Gridworld(
- log_event=self.record_event,
- **self.config.as_dict()
- )
+ self.grid = Gridworld(log_event=self.record_event, **self.config.as_dict())
def replay_started(self):
return self.grid.game_started
@@ -1670,9 +1741,12 @@ def replay_started(self):
def events_for_replay(self, session=None, target=None):
info_cls = dallinger.models.Info
from .models import Event
+
# Get the base query from the parent class, but remove order_by fields as we override them
# in the subqueries
- events = Experiment.events_for_replay(self, session=session, target=target).order_by(False)
+ events = Experiment.events_for_replay(
+ self, session=session, target=target
+ ).order_by(False)
if target is None:
# If we don't have a specific target time we can't optimise some states away
return events
@@ -1684,71 +1758,78 @@ def events_for_replay(self, session=None, target=None):
)
# Get the most recent eligible update that changed the food positions
- food_events = events.filter(
- info_cls.type == 'state',
- Event.details['food'] != None, # noqa: E711
- ).order_by(Event.creation_time.desc()).limit(1)
+ item_events = (
+ events.filter(
+ info_cls.type == "state",
+ Event.details["items"] != None, # noqa: E711
+ )
+ .order_by(Event.creation_time.desc())
+ .limit(1)
+ )
# Get the most recent eligible update that changed the wall positions
- wall_events = events.filter(
- info_cls.type == 'state',
- Event.details['walls'] != None, # noqa: E711
- ).order_by(Event.creation_time.desc()).limit(1)
+ wall_events = (
+ events.filter(
+ info_cls.type == "state",
+ Event.details["walls"] != None, # noqa: E711
+ )
+ .order_by(Event.creation_time.desc())
+ .limit(1)
+ )
# Get the most recent eligible update that changed the player positions
- update_events = events.filter(
- info_cls.type == 'state',
- Event.details['players'] != None, # noqa: E711
- ).order_by(Event.creation_time.desc()).limit(1)
+ update_events = (
+ events.filter(
+ info_cls.type == "state",
+ Event.details["players"] != None, # noqa: E711
+ )
+ .order_by(Event.creation_time.desc())
+ .limit(1)
+ )
# Get all eligible updates of the below types
- event_types = {'chat', 'new_round', 'donation_processed', 'color_changed'}
+ event_types = {"chat", "new_round", "donation_processed", "color_changed"}
typed_events = events.filter(
- info_cls.type == 'event',
- Event.details['type'].astext.in_(event_types)
+ info_cls.type == "event", Event.details["type"].astext.in_(event_types)
)
# Merge the above four queries, discarding duplicates, and put them in time ascending order
- merged_events = food_events.union(
- wall_events,
- update_events,
- typed_events
+ merged_events = item_events.union(
+ wall_events, update_events, typed_events
).order_by(Event.creation_time.asc())
# Limit the query to the type, the effective time and the JSONB field containing the data
return merged_events.with_entities(
- info_cls.type,
- info_cls.creation_time,
- info_cls.details
+ info_cls.type, info_cls.creation_time, info_cls.details
)
def replay_event(self, event):
- if 'server_time' not in event.details:
+ if "server_time" not in event.details:
# If we don't have a server time in the event we reconstruct it from
# the event metadata
- event.details['server_time'] = (
- time.mktime(event.creation_time.timetuple()) +
- event.creation_time.microsecond / 1e6
+ event.details["server_time"] = (
+ time.mktime(event.creation_time.timetuple())
+ + event.creation_time.microsecond / 1e6
)
- if event.type == 'event':
+ if event.type == "event":
self.publish(event.details)
- if event.details.get('type') == 'new_round':
+ if event.details.get("type") == "new_round":
self.grid.check_round_completion()
- elif event.details.get('type') == 'chat':
+ elif event.details.get("type") == "chat":
self.handle_chat_message(event.details)
- if event.type == 'state':
+ if event.type == "state":
self.state_count += 1
state = event.details
if not state:
# Allow loading older exports that didn't fill the details column
state = json.loads(event.contents)
msg = {
- 'type': 'state',
- 'grid': state,
- 'count': self.state_count,
- 'remaining_time': self.grid.remaining_round_time,
- 'round': state['round'],
+ "type": "state",
+ "grid": state,
+ "count": self.state_count,
+ "remaining_time": self.grid.remaining_round_time,
+ "round": state["round"],
}
self.grid.deserialize(state)
self.publish(msg)
@@ -1758,39 +1839,43 @@ def usable_replay_range(self):
# Start when the first player connects
start_time = (
self.import_session.query(Event)
- .filter(Event.details['type'].astext == 'connect')
- .order_by(Event.creation_time)
- [0].creation_time
+ .filter(Event.details["type"].astext == "connect")
+ .order_by(Event.creation_time)[0]
+ .creation_time
)
# At the start of the following second, as Dallinger truncates milliseconds for start time
start_time += datetime.timedelta(seconds=1)
# End at the last move
end_time = (
self.import_session.query(Event)
- .filter(Event.details['type'].astext == 'move')
- .order_by(Event.creation_time.desc())
- [0].creation_time
+ .filter(Event.details["type"].astext == "move")
+ .order_by(Event.creation_time.desc())[0]
+ .creation_time
)
return (start_time, end_time)
def revert_to_time(self, session=None, target=None):
- self._replay_time_index = self.usable_replay_range[0] - datetime.timedelta(minutes=1)
+ self._replay_time_index = self.usable_replay_range[0] - datetime.timedelta(
+ minutes=1
+ )
self.grid.chat_message_history = []
self.state_count = 0
self.grid.players = {}
- self.grid.food_locations = {}
+ self.grid.item_locations = {}
self.grid.wall_locations = {}
def replay_finish(self):
- self.publish({'type': 'stop'})
+ self.publish({"type": "stop"})
def analyze(self, data):
- return json.dumps({
- "average_payoff": self.average_payoff(data),
- "average_score": self.average_score(data),
- "number_of_actions": self.number_of_actions(data),
- "average_time_to_start": self.average_time_to_start(data),
- })
+ return json.dumps(
+ {
+ "average_payoff": self.average_payoff(data),
+ "average_score": self.average_score(data),
+ "number_of_actions": self.number_of_actions(data),
+ "average_time_to_start": self.average_time_to_start(data),
+ }
+ )
def isplit(self, seq, splitters):
"""Split a list into nested lists on a value (or tuple or list of values)
@@ -1798,7 +1883,7 @@ def isplit(self, seq, splitters):
auxilary function to number_of_actions"""
return [
list(g)
- for k, g in itertools.groupby(seq, lambda x:x in splitters)
+ for k, g in itertools.groupby(seq, lambda x: x in splitters)
if not k
]
@@ -1807,28 +1892,34 @@ def number_of_actions_per_round(self, origin_ids, moves):
auxilary function to number_of_actions"""
player_move_data = []
for player in origin_ids:
- players_moves = [x for x in moves if x[11] == player] # Get all the moves of a player
+ players_moves = [
+ x for x in moves if x[11] == player
+ ] # Get all the moves of a player
if len(players_moves) != 0:
player_id = json.loads(players_moves[0][9])["player_id"]
- player_move_data.append({"player_id": player_id,
- "total_moves": len(players_moves)})
+ player_move_data.append(
+ {"player_id": player_id, "total_moves": len(players_moves)}
+ )
return player_move_data
def number_of_actions(self, data):
"""Return a dictionary containing the # of actions taken
for each participant per round"""
df = data.infos.df
- dataState = df.loc[df['type'] == 'state']
+ dataState = df.loc[df["type"] == "state"]
if dataState.empty:
return []
dlist = data.infos.list
- moves_and_round_breaks = [x for x in dlist if x[10] == 'event' and
- ('move' in x[9] or 'new_round' in x[9])]
+ moves_and_round_breaks = [
+ x
+ for x in dlist
+ if x[10] == "event" and ("move" in x[9] or "new_round" in x[9])
+ ]
# Find all the round dividers/breaks
round_breaks = [x for x in moves_and_round_breaks if "new_round" in x[9]]
# Get the unique origin_id for each player to differentiate players
- moves = [x for x in moves_and_round_breaks if 'move' in x[9]]
+ moves = [x for x in moves_and_round_breaks if "move" in x[9]]
origin_ids = [set(x[11] for x in moves)][0]
# Split the move data of entire game into lists containing move data for each round
@@ -1841,8 +1932,10 @@ def number_of_actions(self, data):
# Note that the round number might not match how griduniverse numbers the rounds
# However the system used here to # rounds is verified to be accurate chronologically
# Round 1 happened before round 2 etc
- data_dict = {"round_number": round_number,
- "round_data": self.number_of_actions_per_round(origin_ids, moves)}
+ data_dict = {
+ "round_number": round_number,
+ "round_data": self.number_of_actions_per_round(origin_ids, moves),
+ }
number_of_actions_data.append(data_dict)
round_number += 1
@@ -1850,14 +1943,15 @@ def number_of_actions(self, data):
def average_time_to_start(self, data):
"""The average time to start the game.
- Compare the time of participant's first move info to the network creation timr"""
+ Compare the time of participant's first move info to the network creation timr
+ """
df = data.infos.df
- dataState = df.loc[df['type'] == 'state']
+ dataState = df.loc[df["type"] == "state"]
if dataState.empty:
return str(datetime.timedelta(0))
network_creation_time = data.networks.list[0][1]
dlist = data.infos.list
- moves = [x for x in dlist if x[10] == 'event' and 'move' in x[9]]
+ moves = [x for x in dlist if x[10] == "event" and "move" in x[9]]
# Get the unique origin_id for each player to differentiate players
origin_ids = set(x[11] for x in moves)
@@ -1865,7 +1959,9 @@ def average_time_to_start(self, data):
deltasum = datetime.timedelta(0) # init
delta_count = 0
for player in origin_ids:
- players_moves = [x for x in moves if x[11] == player] # get all the moves of a player
+ players_moves = [
+ x for x in moves if x[11] == player
+ ] # get all the moves of a player
if len(players_moves) != 0: # Is it possible that the player does not move?
# Use the time of their first move
delta = players_moves[0][1] - network_creation_time
@@ -1880,39 +1976,40 @@ def average_time_to_start(self, data):
def average_payoff(self, data):
df = data.infos.df
- dataState = df.loc[df['type'] == 'state']
+ dataState = df.loc[df["type"] == "state"]
if dataState.empty:
return 0.0
final_state = json.loads(dataState.iloc[-1][-1])
- players = final_state['players']
- payoff = [player['payoff'] for player in players]
+ players = final_state["players"]
+ payoff = [player["payoff"] for player in players]
return float(sum(payoff)) / len(payoff)
def average_score(self, data):
df = data.infos.df
- dataState = df.loc[df['type'] == 'state']
+ dataState = df.loc[df["type"] == "state"]
if dataState.empty:
return 0.0
final_state = json.loads(dataState.iloc[-1][-1])
- players = final_state['players']
- scores = [player['score'] for player in players]
+ players = final_state["players"]
+ scores = [player["score"] for player in players]
return float(sum(scores)) / len(scores)
def _last_state_for_player(self, player_id):
most_recent_grid_state = self.environment.state()
if most_recent_grid_state is not None:
- players = json.loads(most_recent_grid_state.contents)['players']
- id_matches = [p for p in players if int(p['id']) == player_id]
+ players = json.loads(most_recent_grid_state.contents)["players"]
+ id_matches = [p for p in players if int(p["id"]) == player_id]
if id_matches:
return id_matches[0]
def is_complete(self):
"""Don't consider the experiment finished until all initial
recruits have completed the experiment."""
- finished_count = self.session.query(
- dallinger.models.Participant
- ).filter(
- dallinger.models.Participant.status.in_(['approved', 'rejected'])
- ).with_entities(func.count(dallinger.models.Participant.id)).scalar()
+ finished_count = (
+ self.session.query(dallinger.models.Participant)
+ .filter(dallinger.models.Participant.status.in_(["approved", "rejected"]))
+ .with_entities(func.count(dallinger.models.Participant.id))
+ .scalar()
+ )
return finished_count >= self.initial_recruitment_size
diff --git a/dlgr/griduniverse/game_config.yml b/dlgr/griduniverse/game_config.yml
index 271c580b..ceddbbc7 100644
--- a/dlgr/griduniverse/game_config.yml
+++ b/dlgr/griduniverse/game_config.yml
@@ -1,82 +1,130 @@
---
+player_config:
+ probability_distribution: "random"
+ available_colors:
+ BLUE: [0.50, 0.86, 1.00]
+ YELLOW: [1.00, 0.86, 0.50]
+ ORANGE: [0.91, 0.50, 0.02]
+ RED: [0.64, 0.11, 0.31]
+ PURPLE: [0.85, 0.60, 0.85]
+ TEAL: [0.77, 0.96, 0.90]
-objects:
+item_defaults:
+ item_id: 1
+ item_count: 8
+ calories: 5
+ crossable: true
+ interactive: false
+ maturation_speed: 0.01
+ maturation_threshold: 0.5
+ n_uses: 1
+ name: Food
+ plantable: false
+ planting_cost: 1
+ portable: true
+ probability_distribution: "random"
+ public_good: 0.0
+ public_good_multiplier: 0.0
+ respawn: true
+ seasonal_growth_rate: 1.0
+ spawn_rate: 1.0
+ sprite: "#8a9b0f,#7a6b54"
- - calories: null
+items:
+ # Legacy GU Food item
+ - item_id: 9
+ calories: 5
crossable: true
- interactive: true
- n_uses: null
- name: Wild Carrot Plant
- object_id: 1
- portable: false
- spawn_rate: 0.15
+ interactive: false
+ maturation_speed: 0.1
+ maturation_threshold: 0.5
+ n_uses: 1
+ name: Food
+ plantable: false
+ planting_cost: 1
+ portable: true
+ respawn: true
+ sprite: "#8a9b0f,#7a6b54"
- - calories: null
+ - item_id: 10
+ calories: 3
crossable: true
- interactive: true
- n_uses: null
- name: Stone
- object_id: 2
+ interactive: false
+ item_count: 4
+ maturation_speed: 0.2
+ maturation_threshold: 0.5
+ n_uses: 1
+ name: Food2
+ plantable: false
+ planting_cost: 1
portable: true
- spawn_rate: 0.05
+ respawn: true
+ sprite: "#2a9b0f,#1a6b54"
- - calories: null
- crossable: false
- interactive: true
- n_uses: null
- name: Big Hard Rock
- object_id: 3
- portable: false
- spawn_rate: 0.05
+ # One Hour, One Life items
+ # - crossable: true
+ # interactive: true
+ # name: Wild Carrot Plant
+ # item_id: 1
+ # portable: false
+ # spawn_rate: 0.15
- - calories: null
- crossable: false
- interactive: true
- n_uses: 6
- name: Gooseberry Bush
- object_id: 4
- portable: false
- spawn_rate: 0.1
+ # - crossable: true
+ # interactive: true
+ # name: Stone
+ # item_id: 2
+ # portable: true
+ # spawn_rate: 0.05
- - calories: null
- crossable: false
- interactive: true
- n_uses: null
- name: Sharp Stone
- object_id: 5
- portable: true
- spawn_rate: null
+ # - crossable: false
+ # interactive: true
+ # name: Big Hard Rock
+ # item_id: 3
+ # portable: false
+ # spawn_rate: 0.05
- - calories: 5
- crossable: true
- interactive: true
- n_uses: 1
- name: Wild Carrot
- object_id: 6
- portable: true
- spawn_rate: null
+ # - crossable: false
+ # interactive: true
+ # n_uses: 6
+ # name: Gooseberry Bush
+ # item_id: 4
+ # portable: false
+ # spawn_rate: 0.1
- - calories: 3
- crossable: true
- interactive: true
- n_uses: 1
- name: Gooseberry
- object_id: 7
- portable: true
- spawn_rate: null
+ # - crossable: false
+ # interactive: true
+ # name: Sharp Stone
+ # item_id: 5
+ # portable: true
+ # spawn_rate: 0.1
- - calories: null
- crossable: false
- interactive: true
- n_uses: 1
- name: Empty Gooseberry Bush
- object_id: 8
- portable: false
- spawn_rate: null
+ # - calories: 5
+ # crossable: true
+ # interactive: true
+ # n_uses: 1
+ # name: Wild Carrot
+ # item_id: 6
+ # portable: true
+ # spawn_rate: 0.1
+ # - calories: 3
+ # crossable: true
+ # interactive: true
+ # n_uses: 1
+ # name: Gooseberry
+ # item_id: 7
+ # portable: true
+ # spawn_rate: 0.1
-transitions:
+ # - crossable: false
+ # interactive: true
+ # n_uses: 1
+ # name: Empty Gooseberry Bush
+ # item_id: 8
+ # portable: false
+ # spawn_rate: 0.1
+transitions:
- actor_end: 5
actor_start: 2
last_use: false
diff --git a/dlgr/griduniverse/jupyter.py b/dlgr/griduniverse/jupyter.py
index 6261a1c2..d391c20e 100644
--- a/dlgr/griduniverse/jupyter.py
+++ b/dlgr/griduniverse/jupyter.py
@@ -7,13 +7,16 @@
from dallinger.jupyter import ExperimentWidget as DallingerExperimentWidget
-header_template = Template(u"""
+header_template = Template(
+ """
{{ name }}
Status: {{ status }}
{% if app_id %}App ID: {{ app_id }}
{% endif %}
-""")
+"""
+)
-grid_template = Template(u"""
+grid_template = Template(
+ """
{% for row in rows %}
@@ -23,9 +26,11 @@
{% endfor %}
-""")
+"""
+)
-scores_template = Template(u"""
+scores_template = Template(
+ """
Name Score
@@ -38,9 +43,11 @@
{% endfor %}
-""")
+"""
+)
-chat_template = Template(u"""
+chat_template = Template(
+ """
Name Message
@@ -53,7 +60,8 @@
{% endfor %}
-""")
+"""
+)
NOTHING = "
"
@@ -63,10 +71,9 @@
class ExperimentWidget(DallingerExperimentWidget):
+ status = Unicode("Unknown")
- status = Unicode('Unknown')
-
- @observe('grid')
+ @observe("grid")
def render(self, change=None):
header = widgets.HTML(
header_template.render(
@@ -77,9 +84,11 @@ def render(self, change=None):
)
tab_list = []
- if hasattr(self.exp, 'grid'):
+ if hasattr(self.exp, "grid"):
grid = []
- player_positions = {tuple(p.position): p.color for p in self.exp.grid.players.values()}
+ player_positions = {
+ tuple(p.position): p.color for p in self.exp.grid.players.values()
+ }
for row_idx in range(self.exp.grid.rows):
row = []
for col_idx in range(self.exp.grid.columns):
@@ -97,15 +106,17 @@ def render(self, change=None):
grid_template.render(
rows=grid,
),
- description='Grid',
+ description="Grid",
)
tab_list += [grid_tab]
scores_tab = widgets.HTML(
scores_template.render(
- players=sorted(self.exp.grid.players.values(), key=lambda player: player.id),
+ players=sorted(
+ self.exp.grid.players.values(), key=lambda player: player.id
+ ),
),
- description='Scores',
+ description="Scores",
)
tab_list += [scores_tab]
@@ -113,12 +124,14 @@ def render(self, change=None):
chat_template.render(
chat_messages=self.exp.grid.chat_message_history,
),
- description='Chat',
+ description="Chat",
)
tab_list += [chat_tab]
self.config_tab.description = "Config"
- tab_list += [self.config_tab, ]
+ tab_list += [
+ self.config_tab,
+ ]
tabs = widgets.Tab(children=tab_list)
for i, tab in enumerate(tab_list):
tabs.set_title(i, tab.description)
diff --git a/dlgr/griduniverse/maze.py b/dlgr/griduniverse/maze.py
index 9f914c11..33bda5d9 100644
--- a/dlgr/griduniverse/maze.py
+++ b/dlgr/griduniverse/maze.py
@@ -5,11 +5,12 @@
class Wall(object):
"""A segment of colored wall occupying a single grid postion"""
+
DEFAULT_COLOR = [0.5, 0.5, 0.5]
def __init__(self, **kwargs):
- self.position = kwargs.get('position', [0, 0])
- self.color = kwargs.get('color', self.DEFAULT_COLOR)
+ self.position = kwargs.get("position", [0, 0])
+ self.color = kwargs.get("color", self.DEFAULT_COLOR)
def serialize(self):
if self.color != self.DEFAULT_COLOR:
@@ -42,8 +43,8 @@ def _generate(rows, columns):
c = (columns - 1) // 2
r = (rows - 1) // 2
visited = [[0] * c + [1] for _ in range(r)] + [[1] * (c + 1)]
- ver = [["* "] * c + ['*'] for _ in range(r)] + [[]]
- hor = [["**"] * c + ['*'] for _ in range(r + 1)]
+ ver = [["* "] * c + ["*"] for _ in range(r)] + [[]]
+ hor = [["**"] * c + ["*"] for _ in range(r + 1)]
# Select a starting position at random, and mark it as visited:
sx = random.randrange(c)
@@ -53,14 +54,9 @@ def _generate(rows, columns):
stack = [(sx, sy)]
while len(stack) > 0:
(x, y) = stack.pop()
- d = [
- (x - 1, y),
- (x, y + 1),
- (x + 1, y),
- (x, y - 1)
- ]
+ d = [(x - 1, y), (x, y + 1), (x + 1, y), (x, y - 1)]
random.shuffle(d)
- for (xx, yy) in d:
+ for xx, yy in d:
if visited[yy][xx]:
continue
if xx == x:
@@ -71,9 +67,9 @@ def _generate(rows, columns):
visited[yy][xx] = 1
# Convert the maze to a list of wall cell positions.
- the_rows = ([j for i in zip(hor, ver) for j in i])
+ the_rows = [j for i in zip(hor, ver) for j in i]
the_rows = [list("".join(j)) for j in the_rows]
- maze = [item == '*' for sublist in the_rows for item in sublist]
+ maze = [item == "*" for sublist in the_rows for item in sublist]
positions = []
for idx, value in enumerate(maze):
if value:
@@ -87,9 +83,7 @@ def _prune(walls, density, contiguity):
num_to_prune = int(round(len(walls) * (1 - density)))
num_pruned = 0
while num_pruned < num_to_prune:
- to_prune = _classify_terminals(
- walls, limit=num_to_prune - num_pruned
- )
+ to_prune = _classify_terminals(walls, limit=num_to_prune - num_pruned)
walls = [w for i, w in enumerate(walls) if i not in to_prune]
if len(to_prune) == 0:
break
@@ -147,5 +141,7 @@ def _classify_terminals(walls, limit=None):
to_prune = set()
for entries in found:
if len(to_prune) < limit:
- to_prune = to_prune.union(set(itertools.islice(entries, limit - len(to_prune))))
+ to_prune = to_prune.union(
+ set(itertools.islice(entries, limit - len(to_prune)))
+ )
return to_prune
diff --git a/dlgr/griduniverse/maze_utils.py b/dlgr/griduniverse/maze_utils.py
index 6498db4d..01ff67f4 100644
--- a/dlgr/griduniverse/maze_utils.py
+++ b/dlgr/griduniverse/maze_utils.py
@@ -63,8 +63,15 @@ def find_path_astar(maze, start, goal, max_iterations=None, graph=None):
continue
visited.add(current)
for direction, neighbour in graph[current]:
- heappush(pr_queue, (cost + heuristic(neighbour, goal), cost + 1,
- path + direction, neighbour))
+ heappush(
+ pr_queue,
+ (
+ cost + heuristic(neighbour, goal),
+ cost + 1,
+ path + direction,
+ neighbour,
+ ),
+ )
return None, ""
diff --git a/dlgr/griduniverse/models.py b/dlgr/griduniverse/models.py
index 512e814b..f8abb1e2 100644
--- a/dlgr/griduniverse/models.py
+++ b/dlgr/griduniverse/models.py
@@ -8,9 +8,7 @@
class Event(Info):
"""An event."""
- __mapper_args__ = {
- "polymorphic_identity": "event"
- }
+ __mapper_args__ = {"polymorphic_identity": "event"}
def __init__(self, origin, details):
super(Event, self).__init__(origin)
@@ -19,12 +17,16 @@ def __init__(self, origin, details):
@declared_attr
def details(cls):
"details column, if not present already."
- return Info.__table__.c.get('details', Column(JSONB))
+ return Info.__table__.c.get("details", Column(JSONB))
-if 'state_walls_idx' not in (index.name for index in State.__table__.indexes):
+if "state_walls_idx" not in (index.name for index in State.__table__.indexes):
# If the index is already defined then this module is being loaded for a second time.
# Do not declare the index in that case, or SQLAlchemy will see it as a duplicate
- state_walls_index = Index('state_walls_idx', State.details['walls'], postgresql_using='gin')
- state_food_index = Index('state_food_idx', State.details['food'], postgresql_using='gin')
- info_type_index = Index('info_type_idx', Info.details['type'].astext)
+ state_walls_index = Index(
+ "state_walls_idx", State.details["walls"], postgresql_using="gin"
+ )
+ state_food_index = Index(
+ "state_food_idx", State.details["food"], postgresql_using="gin"
+ )
+ info_type_index = Index("info_type_idx", Info.details["type"].astext)
diff --git a/dlgr/griduniverse/static/scripts/demo.js b/dlgr/griduniverse/static/scripts/demo.js
index 65681f46..4a54ceee 100755
--- a/dlgr/griduniverse/static/scripts/demo.js
+++ b/dlgr/griduniverse/static/scripts/demo.js
@@ -121,8 +121,8 @@ var mouse = position(pixels.canvas);
var isSpectator = false;
var start = performance.now();
-var food = [];
-var foodConsumed = [];
+var items = [];
+var itemsConsumed = [];
var walls = [];
var wall_map = {};
var row, column, rand;
@@ -149,17 +149,83 @@ var color2idx = function (color) {
var color2name = function (color) {
var idx = color2idx(color);
return settings.player_color_names[idx];
-};
+}
-var Food = function (settings) {
- if (!(this instanceof Food)) {
- return new Food();
+function hexToRgbPercentages(hexColor) {
+ if (hexColor.startsWith("#")) {
+ hexColor = hexColor.substring(1);
+ }
+
+ // Check if the hex color has a valid length (either 3 or 6 characters)
+ if (hexColor.length !== 3 && hexColor.length !== 6) {
+ throw new Error("Invalid hex color format. It should be either 3 or 6 characters long.");
+ }
+
+ // If the hex color is 3 characters long, expand it to 6 characters by
+ // duplicating each character
+ if (hexColor.length === 3) {
+ hexColor = hexColor
+ .split("")
+ .map((char) => char + char)
+ .join("");
+ }
+
+ // Convert the hex color to RGB percentage values
+ const red = parseInt(hexColor.substring(0, 2), 16) / 255;
+ const green = parseInt(hexColor.substring(2, 4), 16) / 255;
+ const blue = parseInt(hexColor.substring(4, 6), 16) / 255;
+
+ return [red, green, blue];
+}
+
+function rgbOnScale(startColor, endColor, percentage) {
+
+ const result = [];
+ for (let i = 0; i < 3; i++) {
+ result[i] = endColor[i] + percentage * (startColor[i] - endColor[i]);
}
- this.id = settings.id;
- this.position = settings.position;
- this.color = settings.color;
- return this;
-};
+
+ console.log("Item color: " + result);
+ return result;
+}
+/**
+ * Representation of a game item, which for the moment is limited to a
+ * simple Food type.
+ */
+class Item {
+ constructor(id, itemId, position, maturity) {
+ this.id = id;
+ this.itemId = itemId;
+ this.position = position;
+ this.maturity = maturity;
+ // XXX Maybe we can avoid this copy of every shared value
+ // to every instance, but going with it for now.
+ Object.assign(this, settings.item_config[this.itemId]);
+ }
+
+ /**
+ * Calculate a color based on sprite definition and maturity
+ */
+ get color() {
+ let immature, mature;
+
+ if (this.sprite.includes(",")) {
+ [immature, mature] = this.sprite.split(",");
+ // For now, assume these are hex colors
+ } else {
+ immature = mature = this.sprite;
+ }
+
+ return rgbOnScale(
+ hexToRgbPercentages(immature),
+ hexToRgbPercentages(mature),
+ this.maturity
+ );
+ }
+
+
+
+}
var Wall = function (settings) {
if (!(this instanceof Wall)) {
@@ -561,7 +627,6 @@ pixels.frame(function() {
// Update the background.
var ego = players.ego(),
w = getWindowPosition(),
- limitVisibility,
dimness,
rescaling,
i, j, x, y;
@@ -575,15 +640,16 @@ pixels.frame(function() {
return newColor;
});
- for (i = 0; i < food.length; i++) {
- // Players digest the food.
- var cur_food = food[i];
- if (players.isPlayerAt(cur_food.position)) {
- foodConsumed.push(food.splice(i, 1));
- } else {
- if (settings.food_visible) {
- section.plot(cur_food.position[1], cur_food.position[0], cur_food.color);
+ for (i = 0; i < items.length; i++) {
+ var currentItem = items[i];
+ if (players.isPlayerAt(currentItem.position)) {
+ if (! currentItem.interactive) {
+ // Non-interactive items get consumed immediately
+ itemsConsumed.push(items.splice(i, 1)); // XXX this push does nothing, AFAICT (Jesse)
}
+ // Else: show info about the item in some way
+ } else {
+ section.plot(currentItem.position[1], currentItem.position[0], currentItem.color);
}
}
@@ -952,16 +1018,17 @@ function onGameStateChange(msg) {
updateDonationStatus(state.donation_active);
- // Update food.
- if (state.food !== undefined && state.food !== null) {
- food = [];
- for (j = 0; j < state.food.length; j++) {
- food.push(
- new Food({
- id: state.food[j].id,
- position: state.food[j].position,
- color: state.food[j].color
- })
+ // Update items
+ if (state.items !== undefined && state.items !== null) {
+ items = [];
+ for (j = 0; j < state.items.length; j++) {
+ items.push(
+ new Item(
+ state.items[j].id,
+ state.items[j].item_id,
+ state.items[j].position,
+ state.items[j].maturity,
+ )
);
}
}
diff --git a/dlgr/griduniverse/static/scripts/dist/bundle.js b/dlgr/griduniverse/static/scripts/dist/bundle.js
index 7f5202cd..6158a6ab 100644
--- a/dlgr/griduniverse/static/scripts/dist/bundle.js
+++ b/dlgr/griduniverse/static/scripts/dist/bundle.js
@@ -33,9 +33,6 @@
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
-/******/ // identity function for calling harmony imports with the correct context
-/******/ __webpack_require__.i = function(value) { return value; };
-/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
@@ -63,12 +60,220 @@
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
-/******/ return __webpack_require__(__webpack_require__.s = 16);
+/******/ return __webpack_require__(__webpack_require__.s = 9);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */,
/* 1 */
+/***/ (function(module, exports) {
+
+var g;
+
+// This works in non-strict mode
+g = (function() {
+ return this;
+})();
+
+try {
+ // This works if eval is allowed (see CSP)
+ g = g || Function("return this")() || (1,eval)("this");
+} catch(e) {
+ // This works if the window reference is available
+ if(typeof window === "object")
+ g = window;
+}
+
+// g can still be undefined, but nothing to do about it...
+// We return undefined, instead of nothing here, so it's
+// easier to handle this case. if(!global) { ...}
+
+module.exports = g;
+
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+
+var convert = __webpack_require__(15);
+
+module.exports = function (cstr) {
+ var m, conv, parts, alpha;
+ if (m = /^((?:rgb|hs[lv]|cmyk|xyz|lab)a?)\s*\(([^\)]*)\)/.exec(cstr)) {
+ var name = m[1];
+ var base = name.replace(/a$/, '');
+ var size = base === 'cmyk' ? 4 : 3;
+ conv = convert[base];
+
+ parts = m[2].replace(/^\s+|\s+$/g, '')
+ .split(/\s*,\s*/)
+ .map(function (x, i) {
+ if (/%$/.test(x) && i === size) {
+ return parseFloat(x) / 100;
+ }
+ else if (/%$/.test(x)) {
+ return parseFloat(x);
+ }
+ return parseFloat(x);
+ })
+ ;
+ if (name === base) parts.push(1);
+ alpha = parts[size] === undefined ? 1 : parts[size];
+ parts = parts.slice(0, size);
+
+ conv[base] = function () { return parts };
+ }
+ else if (/^#[A-Fa-f0-9]+$/.test(cstr)) {
+ var base = cstr.replace(/^#/,'');
+ var size = base.length;
+ conv = convert.rgb;
+ parts = base.split(size === 3 ? /(.)/ : /(..)/);
+ parts = parts.filter(Boolean)
+ .map(function (x) {
+ if (size === 3) {
+ return parseInt(x + x, 16);
+ }
+ else {
+ return parseInt(x, 16)
+ }
+ })
+ ;
+ alpha = 1;
+ conv.rgb = function () { return parts };
+ if (!parts[0]) parts[0] = 0;
+ if (!parts[1]) parts[1] = 0;
+ if (!parts[2]) parts[2] = 0;
+ }
+ else {
+ conv = convert.keyword;
+ conv.keyword = function () { return cstr };
+ parts = cstr;
+ alpha = 1;
+ }
+
+ var res = {
+ rgb: undefined,
+ hsl: undefined,
+ hsv: undefined,
+ cmyk: undefined,
+ keyword: undefined,
+ hex: undefined
+ };
+ try { res.rgb = conv.rgb(parts) } catch (e) {}
+ try { res.hsl = conv.hsl(parts) } catch (e) {}
+ try { res.hsv = conv.hsv(parts) } catch (e) {}
+ try { res.cmyk = conv.cmyk(parts) } catch (e) {}
+ try { res.keyword = conv.keyword(parts) } catch (e) {}
+
+ if (res.rgb) res.hex = '#' + res.rgb.map(function (x) {
+ var s = x.toString(16);
+ if (s.length === 1) return '0' + s;
+ return s;
+ }).join('');
+
+ if (res.rgb) res.rgba = res.rgb.concat(alpha);
+ if (res.hsl) res.hsla = res.hsl.concat(alpha);
+ if (res.hsv) res.hsva = res.hsv.concat(alpha);
+ if (res.cmyk) res.cmyka = res.cmyk.concat(alpha);
+
+ return res;
+};
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+/*!
+ * is-number
+ *
+ * Copyright (c) 2014-2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+
+
+var typeOf = __webpack_require__(17);
+
+module.exports = function isNumber(num) {
+ var type = typeOf(num);
+ if (type !== 'number' && type !== 'string') {
+ return false;
+ }
+ var n = +num;
+ return (n - n + 1) >= 0 && num !== '';
+};
+
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var strValue = String.prototype.valueOf;
+var tryStringObject = function tryStringObject(value) {
+ try {
+ strValue.call(value);
+ return true;
+ } catch (e) {
+ return false;
+ }
+};
+var toStr = Object.prototype.toString;
+var strClass = '[object String]';
+var hasToStringTag = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
+
+module.exports = function isString(value) {
+ if (typeof value === 'string') { return true; }
+ if (typeof value !== 'object') { return false; }
+ return hasToStringTag ? tryStringObject(value) : toStr.call(value) === strClass;
+};
+
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports) {
+
+
+/**
+ * isArray
+ */
+
+var isArray = Array.isArray;
+
+/**
+ * toString
+ */
+
+var str = Object.prototype.toString;
+
+/**
+ * Whether or not the given `val`
+ * is an array.
+ *
+ * example:
+ *
+ * isArray([]);
+ * // > true
+ * isArray(arguments);
+ * // > false
+ * isArray('');
+ * // > false
+ *
+ * @param {mixed} val
+ * @return {bool}
+ */
+
+module.exports = isArray || function (val) {
+ return !! val && '[object Array]' == str.call(val);
+};
+
+
+/***/ }),
+/* 6 */
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/*
@@ -355,61 +560,216 @@ var __WEBPACK_AMD_DEFINE_RESULT__;/*
}(this))
/***/ }),
-/* 2 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* MIT license */
-var cssKeywords = __webpack_require__(27);
-
-// NOTE: conversions should only return primitive values (i.e. arrays, or
-// values that give correct `typeof` results).
-// do not use box values types (i.e. Number(), String(), etc.)
-
-var reverseKeywords = {};
-for (var key in cssKeywords) {
- if (cssKeywords.hasOwnProperty(key)) {
- reverseKeywords[cssKeywords[key]] = key;
- }
-}
-
-var convert = module.exports = {
- rgb: {channels: 3, labels: 'rgb'},
- hsl: {channels: 3, labels: 'hsl'},
- hsv: {channels: 3, labels: 'hsv'},
- hwb: {channels: 3, labels: 'hwb'},
- cmyk: {channels: 4, labels: 'cmyk'},
- xyz: {channels: 3, labels: 'xyz'},
- lab: {channels: 3, labels: 'lab'},
- lch: {channels: 3, labels: 'lch'},
- hex: {channels: 1, labels: ['hex']},
- keyword: {channels: 1, labels: ['keyword']},
- ansi16: {channels: 1, labels: ['ansi16']},
- ansi256: {channels: 1, labels: ['ansi256']},
- hcg: {channels: 3, labels: ['h', 'c', 'g']},
- apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
- gray: {channels: 1, labels: ['gray']}
-};
-
-// hide .channels and .labels properties
-for (var model in convert) {
- if (convert.hasOwnProperty(model)) {
- if (!('channels' in convert[model])) {
- throw new Error('missing channels property: ' + model);
- }
-
- if (!('labels' in convert[model])) {
- throw new Error('missing channel labels property: ' + model);
- }
-
- if (convert[model].labels.length !== convert[model].channels) {
- throw new Error('channel and label counts mismatch: ' + model);
- }
+/* 7 */
+/***/ (function(module, exports) {
- var channels = convert[model].channels;
- var labels = convert[model].labels;
- delete convert[model].channels;
- delete convert[model].labels;
- Object.defineProperty(convert[model], 'channels', {value: channels});
+module.exports = {
+ "aliceblue": [240, 248, 255],
+ "antiquewhite": [250, 235, 215],
+ "aqua": [0, 255, 255],
+ "aquamarine": [127, 255, 212],
+ "azure": [240, 255, 255],
+ "beige": [245, 245, 220],
+ "bisque": [255, 228, 196],
+ "black": [0, 0, 0],
+ "blanchedalmond": [255, 235, 205],
+ "blue": [0, 0, 255],
+ "blueviolet": [138, 43, 226],
+ "brown": [165, 42, 42],
+ "burlywood": [222, 184, 135],
+ "cadetblue": [95, 158, 160],
+ "chartreuse": [127, 255, 0],
+ "chocolate": [210, 105, 30],
+ "coral": [255, 127, 80],
+ "cornflowerblue": [100, 149, 237],
+ "cornsilk": [255, 248, 220],
+ "crimson": [220, 20, 60],
+ "cyan": [0, 255, 255],
+ "darkblue": [0, 0, 139],
+ "darkcyan": [0, 139, 139],
+ "darkgoldenrod": [184, 134, 11],
+ "darkgray": [169, 169, 169],
+ "darkgreen": [0, 100, 0],
+ "darkgrey": [169, 169, 169],
+ "darkkhaki": [189, 183, 107],
+ "darkmagenta": [139, 0, 139],
+ "darkolivegreen": [85, 107, 47],
+ "darkorange": [255, 140, 0],
+ "darkorchid": [153, 50, 204],
+ "darkred": [139, 0, 0],
+ "darksalmon": [233, 150, 122],
+ "darkseagreen": [143, 188, 143],
+ "darkslateblue": [72, 61, 139],
+ "darkslategray": [47, 79, 79],
+ "darkslategrey": [47, 79, 79],
+ "darkturquoise": [0, 206, 209],
+ "darkviolet": [148, 0, 211],
+ "deeppink": [255, 20, 147],
+ "deepskyblue": [0, 191, 255],
+ "dimgray": [105, 105, 105],
+ "dimgrey": [105, 105, 105],
+ "dodgerblue": [30, 144, 255],
+ "firebrick": [178, 34, 34],
+ "floralwhite": [255, 250, 240],
+ "forestgreen": [34, 139, 34],
+ "fuchsia": [255, 0, 255],
+ "gainsboro": [220, 220, 220],
+ "ghostwhite": [248, 248, 255],
+ "gold": [255, 215, 0],
+ "goldenrod": [218, 165, 32],
+ "gray": [128, 128, 128],
+ "green": [0, 128, 0],
+ "greenyellow": [173, 255, 47],
+ "grey": [128, 128, 128],
+ "honeydew": [240, 255, 240],
+ "hotpink": [255, 105, 180],
+ "indianred": [205, 92, 92],
+ "indigo": [75, 0, 130],
+ "ivory": [255, 255, 240],
+ "khaki": [240, 230, 140],
+ "lavender": [230, 230, 250],
+ "lavenderblush": [255, 240, 245],
+ "lawngreen": [124, 252, 0],
+ "lemonchiffon": [255, 250, 205],
+ "lightblue": [173, 216, 230],
+ "lightcoral": [240, 128, 128],
+ "lightcyan": [224, 255, 255],
+ "lightgoldenrodyellow": [250, 250, 210],
+ "lightgray": [211, 211, 211],
+ "lightgreen": [144, 238, 144],
+ "lightgrey": [211, 211, 211],
+ "lightpink": [255, 182, 193],
+ "lightsalmon": [255, 160, 122],
+ "lightseagreen": [32, 178, 170],
+ "lightskyblue": [135, 206, 250],
+ "lightslategray": [119, 136, 153],
+ "lightslategrey": [119, 136, 153],
+ "lightsteelblue": [176, 196, 222],
+ "lightyellow": [255, 255, 224],
+ "lime": [0, 255, 0],
+ "limegreen": [50, 205, 50],
+ "linen": [250, 240, 230],
+ "magenta": [255, 0, 255],
+ "maroon": [128, 0, 0],
+ "mediumaquamarine": [102, 205, 170],
+ "mediumblue": [0, 0, 205],
+ "mediumorchid": [186, 85, 211],
+ "mediumpurple": [147, 112, 219],
+ "mediumseagreen": [60, 179, 113],
+ "mediumslateblue": [123, 104, 238],
+ "mediumspringgreen": [0, 250, 154],
+ "mediumturquoise": [72, 209, 204],
+ "mediumvioletred": [199, 21, 133],
+ "midnightblue": [25, 25, 112],
+ "mintcream": [245, 255, 250],
+ "mistyrose": [255, 228, 225],
+ "moccasin": [255, 228, 181],
+ "navajowhite": [255, 222, 173],
+ "navy": [0, 0, 128],
+ "oldlace": [253, 245, 230],
+ "olive": [128, 128, 0],
+ "olivedrab": [107, 142, 35],
+ "orange": [255, 165, 0],
+ "orangered": [255, 69, 0],
+ "orchid": [218, 112, 214],
+ "palegoldenrod": [238, 232, 170],
+ "palegreen": [152, 251, 152],
+ "paleturquoise": [175, 238, 238],
+ "palevioletred": [219, 112, 147],
+ "papayawhip": [255, 239, 213],
+ "peachpuff": [255, 218, 185],
+ "peru": [205, 133, 63],
+ "pink": [255, 192, 203],
+ "plum": [221, 160, 221],
+ "powderblue": [176, 224, 230],
+ "purple": [128, 0, 128],
+ "rebeccapurple": [102, 51, 153],
+ "red": [255, 0, 0],
+ "rosybrown": [188, 143, 143],
+ "royalblue": [65, 105, 225],
+ "saddlebrown": [139, 69, 19],
+ "salmon": [250, 128, 114],
+ "sandybrown": [244, 164, 96],
+ "seagreen": [46, 139, 87],
+ "seashell": [255, 245, 238],
+ "sienna": [160, 82, 45],
+ "silver": [192, 192, 192],
+ "skyblue": [135, 206, 235],
+ "slateblue": [106, 90, 205],
+ "slategray": [112, 128, 144],
+ "slategrey": [112, 128, 144],
+ "snow": [255, 250, 250],
+ "springgreen": [0, 255, 127],
+ "steelblue": [70, 130, 180],
+ "tan": [210, 180, 140],
+ "teal": [0, 128, 128],
+ "thistle": [216, 191, 216],
+ "tomato": [255, 99, 71],
+ "turquoise": [64, 224, 208],
+ "violet": [238, 130, 238],
+ "wheat": [245, 222, 179],
+ "white": [255, 255, 255],
+ "whitesmoke": [245, 245, 245],
+ "yellow": [255, 255, 0],
+ "yellowgreen": [154, 205, 50]
+};
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports, __webpack_require__) {
+
+/* MIT license */
+var cssKeywords = __webpack_require__(7);
+
+// NOTE: conversions should only return primitive values (i.e. arrays, or
+// values that give correct `typeof` results).
+// do not use box values types (i.e. Number(), String(), etc.)
+
+var reverseKeywords = {};
+for (var key in cssKeywords) {
+ if (cssKeywords.hasOwnProperty(key)) {
+ reverseKeywords[cssKeywords[key]] = key;
+ }
+}
+
+var convert = module.exports = {
+ rgb: {channels: 3, labels: 'rgb'},
+ hsl: {channels: 3, labels: 'hsl'},
+ hsv: {channels: 3, labels: 'hsv'},
+ hwb: {channels: 3, labels: 'hwb'},
+ cmyk: {channels: 4, labels: 'cmyk'},
+ xyz: {channels: 3, labels: 'xyz'},
+ lab: {channels: 3, labels: 'lab'},
+ lch: {channels: 3, labels: 'lch'},
+ hex: {channels: 1, labels: ['hex']},
+ keyword: {channels: 1, labels: ['keyword']},
+ ansi16: {channels: 1, labels: ['ansi16']},
+ ansi256: {channels: 1, labels: ['ansi256']},
+ hcg: {channels: 3, labels: ['h', 'c', 'g']},
+ apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
+ gray: {channels: 1, labels: ['gray']}
+};
+
+// hide .channels and .labels properties
+for (var model in convert) {
+ if (convert.hasOwnProperty(model)) {
+ if (!('channels' in convert[model])) {
+ throw new Error('missing channels property: ' + model);
+ }
+
+ if (!('labels' in convert[model])) {
+ throw new Error('missing channel labels property: ' + model);
+ }
+
+ if (convert[model].labels.length !== convert[model].channels) {
+ throw new Error('channel and label counts mismatch: ' + model);
+ }
+
+ var channels = convert[model].channels;
+ var labels = convert[model].labels;
+ delete convert[model].channels;
+ delete convert[model].labels;
+ Object.defineProperty(convert[model], 'channels', {value: channels});
Object.defineProperty(convert[model], 'labels', {value: labels});
}
}
@@ -455,48 +815,41 @@ convert.rgb.hsl = function (rgb) {
};
convert.rgb.hsv = function (rgb) {
- var rdif;
- var gdif;
- var bdif;
+ var r = rgb[0];
+ var g = rgb[1];
+ var b = rgb[2];
+ var min = Math.min(r, g, b);
+ var max = Math.max(r, g, b);
+ var delta = max - min;
var h;
var s;
+ var v;
- var r = rgb[0] / 255;
- var g = rgb[1] / 255;
- var b = rgb[2] / 255;
- var v = Math.max(r, g, b);
- var diff = v - Math.min(r, g, b);
- var diffc = function (c) {
- return (v - c) / 6 / diff + 1 / 2;
- };
-
- if (diff === 0) {
- h = s = 0;
+ if (max === 0) {
+ s = 0;
} else {
- s = diff / v;
- rdif = diffc(r);
- gdif = diffc(g);
- bdif = diffc(b);
-
- if (r === v) {
- h = bdif - gdif;
- } else if (g === v) {
- h = (1 / 3) + rdif - bdif;
- } else if (b === v) {
- h = (2 / 3) + gdif - rdif;
- }
- if (h < 0) {
- h += 1;
- } else if (h > 1) {
- h -= 1;
- }
+ s = (delta / max * 1000) / 10;
}
- return [
- h * 360,
- s * 100,
- v * 100
- ];
+ if (max === min) {
+ h = 0;
+ } else if (r === max) {
+ h = (g - b) / delta;
+ } else if (g === max) {
+ h = 2 + (b - r) / delta;
+ } else if (b === max) {
+ h = 4 + (r - g) / delta;
+ }
+
+ h = Math.min(h * 60, 360);
+
+ if (h < 0) {
+ h += 360;
+ }
+
+ v = ((max / 255) * 1000) / 10;
+
+ return [h, s, v];
};
convert.rgb.hwb = function (rgb) {
@@ -1229,7423 +1582,4722 @@ convert.rgb.gray = function (rgb) {
/***/ }),
-/* 3 */
-/***/ (function(module, exports) {
-
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
-/**
- * isArray
- */
+var require;/*global dallinger, require, settings */
+/*jshint esversion: 6 */
-var isArray = Array.isArray;
+(function (dallinger, require, settings) {
-/**
- * toString
- */
+var util = __webpack_require__(10);
+var grid = __webpack_require__(14);
+var position = __webpack_require__(27);
+var Mousetrap = __webpack_require__(29);
+var ReconnectingWebSocket = __webpack_require__(30);
+var $ = __webpack_require__(31);
+var gaussian = __webpack_require__(32);
+var Color = __webpack_require__(33);
+var Identicon = __webpack_require__(39);
+var md5 = __webpack_require__(6);
-var str = Object.prototype.toString;
-
-/**
- * Whether or not the given `val`
- * is an array.
- *
- * example:
- *
- * isArray([]);
- * // > true
- * isArray(arguments);
- * // > false
- * isArray('');
- * // > false
- *
- * @param {mixed} val
- * @return {bool}
- */
+function coordsToIdx(x, y, columns) {
+ return y * columns + x;
+}
-module.exports = isArray || function (val) {
- return !! val && '[object Array]' == str.call(val);
-};
+function animateColor(color) {
+ if (settings.background_animation) {
+ rand = Math.random() * 0.02;
+ } else {
+ rand = 0.01;
+ }
+ return [
+ color[0] * 0.95 + rand,
+ color[1] * 0.95 + rand,
+ color[2] * 0.95 + rand
+ ];
+}
+function positionsAreEqual(a, b) {
+ return a[0] === b[0] && a[1] === b[1];
+}
-/***/ }),
-/* 4 */
-/***/ (function(module, exports, __webpack_require__) {
+class Section {
+ // Represents the currently visible section (window) of the grid
-"use strict";
-/*!
- * is-number
- *
- * Copyright (c) 2014-2015, Jon Schlinkert.
- * Licensed under the MIT License.
- */
+ constructor(data, left, top) {
+ this.left = left;
+ this.top = top;
+ this.columns = settings.window_columns;
+ this.rows = settings.window_rows;
+ this.data = [];
+ this.textures = [];
+ // build data array for just this section
+ for (var j = 0; j < this.rows; j++) {
+ for (var i = 0; i < this.columns; i++) {
+ this.data.push(data[this.sectionCoordsToGridIdx(i, j)]);
+ this.textures.push(0);
+ }
+ }
+ }
+ gridCoordsToSectionIdx(x, y) {
+ // Convert grid coordinates to section data array index
+ return (y - this.top) * this.columns + (x - this.left);
+ }
+ sectionCoordsToGridIdx(x, y) {
+ // Convert section coordinates to grid data array index
+ return coordsToIdx(this.left + x, this.top + y, settings.columns);
+ }
-var typeOf = __webpack_require__(35);
+ plot(x, y, color, texture) {
+ // Set color at position (x, y) in full-grid coordinates.
+ if (x >= this.left && x < this.left + this.columns) {
+ if (y >= this.top && y < this.top + this.rows) {
+ this.data[this.gridCoordsToSectionIdx(x, y)] = color;
+ if (texture !== undefined ){
+ this.textures[this.gridCoordsToSectionIdx(x, y)] = texture;
+ }
+ background[coordsToIdx(x, y, settings.columns)] = color;
+ }
+ }
+ }
-module.exports = function isNumber(num) {
- var type = typeOf(num);
+ map(func) {
+ // For each cell, call func with (x, y, color) to get the new color
+ for (var j = 0; j < this.rows; j++) {
+ for (var i = 0; i < this.columns; i++) {
+ var idx = coordsToIdx(i, j, this.columns);
+ this.data[idx] = Reflect.apply(
+ func, this, [this.left + i, this.top + j, this.data[idx]]);
+ }
+ }
+ }
+}
- if (type === 'string') {
- if (!num.trim()) return false;
- } else if (type !== 'number') {
- return false;
+var background = [], color;
+for (var j = 0; j < settings.rows; j++) {
+ for (var i = 0; i < settings.columns; i++) {
+ color = [0, 0, 0];
+ for (var k = 0; k < 15; k++) {
+ color = animateColor(color);
+ }
+ background.push(color);
}
+}
- return (num - num + 1) >= 0;
-};
+var initialSection = new Section(background, 0, 0);
+var GREEN = [0.51, 0.69, 0.61];
+var WHITE = [1.00, 1.00, 1.00];
+var INVISIBLE_COLOR = [0.66, 0.66, 0.66];
+var CHANNEL = "griduniverse";
+var CONTROL_CHANNEL = "griduniverse_ctrl";
-/***/ }),
-/* 5 */
-/***/ (function(module, exports, __webpack_require__) {
+var pixels = grid(initialSection.data, initialSection.textures, {
+ rows: settings.window_rows,
+ columns: settings.window_columns,
+ size: settings.block_size,
+ padding: settings.padding,
+ background: [0.1, 0.1, 0.1],
+ formatted: true
+});
-"use strict";
+var mouse = position(pixels.canvas);
+var isSpectator = false;
+var start = performance.now();
+var items = [];
+var itemsConsumed = [];
+var walls = [];
+var wall_map = {};
+var row, column, rand;
-var strValue = String.prototype.valueOf;
-var tryStringObject = function tryStringObject(value) {
- try {
- strValue.call(value);
- return true;
- } catch (e) {
- return false;
- }
+var name2idx = function (name) {
+ var names = settings.player_color_names;
+ for (var idx=0; idx < names.length; idx++) {
+ if (names[idx] === name) {
+ return idx;
+ }
+ }
};
-var toStr = Object.prototype.toString;
-var strClass = '[object String]';
-var hasToStringTag = __webpack_require__(32)();
-module.exports = function isString(value) {
- if (typeof value === 'string') {
- return true;
- }
- if (typeof value !== 'object') {
- return false;
- }
- return hasToStringTag ? tryStringObject(value) : toStr.call(value) === strClass;
+var color2idx = function (color) {
+ var colors = settings.player_colors;
+ var value = color.join(',');
+ for (var idx=0; idx < colors.length; idx++) {
+ if (colors[idx].join(',') === value) {
+ return idx;
+ }
+ }
};
+var color2name = function (color) {
+ var idx = color2idx(color);
+ return settings.player_color_names[idx];
+}
-/***/ }),
-/* 6 */
-/***/ (function(module, exports, __webpack_require__) {
+function hexToRgbPercentages(hexColor) {
+ if (hexColor.startsWith("#")) {
+ hexColor = hexColor.substring(1);
+ }
-var convert = __webpack_require__(39);
+ // Check if the hex color has a valid length (either 3 or 6 characters)
+ if (hexColor.length !== 3 && hexColor.length !== 6) {
+ throw new Error("Invalid hex color format. It should be either 3 or 6 characters long.");
+ }
-module.exports = function (cstr) {
- var m, conv, parts, alpha;
- if (m = /^((?:rgb|hs[lv]|cmyk|xyz|lab)a?)\s*\(([^\)]*)\)/.exec(cstr)) {
- var name = m[1];
- var base = name.replace(/a$/, '');
- var size = base === 'cmyk' ? 4 : 3;
- conv = convert[base];
-
- parts = m[2].replace(/^\s+|\s+$/g, '')
- .split(/\s*,\s*/)
- .map(function (x, i) {
- if (/%$/.test(x) && i === size) {
- return parseFloat(x) / 100;
- }
- else if (/%$/.test(x)) {
- return parseFloat(x);
- }
- return parseFloat(x);
- })
- ;
- if (name === base) parts.push(1);
- alpha = parts[size] === undefined ? 1 : parts[size];
- parts = parts.slice(0, size);
-
- conv[base] = function () { return parts };
- }
- else if (/^#[A-Fa-f0-9]+$/.test(cstr)) {
- var base = cstr.replace(/^#/,'');
- var size = base.length;
- conv = convert.rgb;
- parts = base.split(size === 3 ? /(.)/ : /(..)/);
- parts = parts.filter(Boolean)
- .map(function (x) {
- if (size === 3) {
- return parseInt(x + x, 16);
- }
- else {
- return parseInt(x, 16)
- }
- })
- ;
- alpha = 1;
- conv.rgb = function () { return parts };
- if (!parts[0]) parts[0] = 0;
- if (!parts[1]) parts[1] = 0;
- if (!parts[2]) parts[2] = 0;
- }
- else {
- conv = convert.keyword;
- conv.keyword = function () { return cstr };
- parts = cstr;
- alpha = 1;
- }
-
- var res = {
- rgb: undefined,
- hsl: undefined,
- hsv: undefined,
- cmyk: undefined,
- keyword: undefined,
- hex: undefined
- };
- try { res.rgb = conv.rgb(parts) } catch (e) {}
- try { res.hsl = conv.hsl(parts) } catch (e) {}
- try { res.hsv = conv.hsv(parts) } catch (e) {}
- try { res.cmyk = conv.cmyk(parts) } catch (e) {}
- try { res.keyword = conv.keyword(parts) } catch (e) {}
-
- if (res.rgb) res.hex = '#' + res.rgb.map(function (x) {
- var s = x.toString(16);
- if (s.length === 1) return '0' + s;
- return s;
- }).join('');
-
- if (res.rgb) res.rgba = res.rgb.concat(alpha);
- if (res.hsl) res.hsla = res.hsl.concat(alpha);
- if (res.hsv) res.hsva = res.hsv.concat(alpha);
- if (res.cmyk) res.cmyka = res.cmyk.concat(alpha);
-
- return res;
-};
+ // If the hex color is 3 characters long, expand it to 6 characters by
+ // duplicating each character
+ if (hexColor.length === 3) {
+ hexColor = hexColor
+ .split("")
+ .map((char) => char + char)
+ .join("");
+ }
+ // Convert the hex color to RGB percentage values
+ const red = parseInt(hexColor.substring(0, 2), 16) / 255;
+ const green = parseInt(hexColor.substring(2, 4), 16) / 255;
+ const blue = parseInt(hexColor.substring(4, 6), 16) / 255;
-/***/ }),
-/* 7 */
-/***/ (function(module, exports, __webpack_require__) {
+ return [red, green, blue];
+}
-var parse = __webpack_require__(6);
-var isnumber = __webpack_require__(4);
-var isstring = __webpack_require__(5);
-var isarray = __webpack_require__(3);
-var convert = __webpack_require__(19);
-var layout = __webpack_require__(20);
-var texcoord = __webpack_require__(24);
-var range = __webpack_require__(23);
-var pixdenticon = __webpack_require__(21);
-var md5 = __webpack_require__(1);
+function rgbOnScale(startColor, endColor, percentage) {
-function Pixels(data, textures, opts) {
- if (!(this instanceof Pixels)) return new Pixels(data, textures, opts);
- var self = this;
- opts = opts || {};
- this.opts = opts;
- var num_identicons = 100;
+ const result = [];
+ for (let i = 0; i < 3; i++) {
+ result[i] = endColor[i] + percentage * (startColor[i] - endColor[i]);
+ }
- opts.background = opts.background || [ 0.5, 0.5, 0.5 ];
- opts.size = isnumber(opts.size) ? opts.size : 10;
- opts.padding = isnumber(opts.padding) ? opts.padding : 2;
+ console.log("Item color: " + result);
+ return result;
+}
+/**
+ * Representation of a game item, which for the moment is limited to a
+ * simple Food type.
+ */
+class Item {
+ constructor(id, itemId, position, maturity) {
+ this.id = id;
+ this.itemId = itemId;
+ this.position = position;
+ this.maturity = maturity;
+ // XXX Maybe we can avoid this copy of every shared value
+ // to every instance, but going with it for now.
+ Object.assign(this, settings.item_config[this.itemId]);
+ }
- if (isstring(opts.background))
- opts.background = parse(opts.background).rgb.map(function(c) {
- return c / 255;
- });
+ /**
+ * Calculate a color based on sprite definition and maturity
+ */
+ get color() {
+ let immature, mature;
- if (isarray(data[0]) && data[0].length !== 3) {
- opts.rows = data.length;
- opts.columns = data[0].length;
- }
+ if (this.sprite.includes(",")) {
+ [immature, mature] = this.sprite.split(",");
+ // For now, assume these are hex colors
+ } else {
+ immature = mature = this.sprite;
+ }
- if (!opts.rows || !opts.columns) {
- opts.rows = opts.columns = Math.round(Math.sqrt(data.length));
+ return rgbOnScale(
+ hexToRgbPercentages(immature),
+ hexToRgbPercentages(mature),
+ this.maturity
+ );
}
- var width = opts.columns * opts.size + (opts.columns + 1) * opts.padding;
- var height = opts.rows * opts.size + (opts.rows + 1) * opts.padding;
-
- var canvas = document.createElement("canvas");
- canvas.width = width;
- canvas.height = height;
- if (opts.root) opts.root.appendChild(canvas);
- var colors = opts.formatted ? data : convert(data);
- var texcoords = texcoord(
- opts.rows,
- opts.columns,
- textures,
- num_identicons
- );
- var positions = layout(
- opts.rows,
- opts.columns,
- 2 * opts.padding / width,
- 2 * opts.size / width,
- width / height
- );
+}
- var regl = __webpack_require__(41)(canvas);
+var Wall = function (settings) {
+ if (!(this instanceof Wall)) {
+ return new Wall();
+ }
+ this.position = settings.position;
+ this.color = settings.color;
+ return this;
+};
- var initial_texture = [];
- for (row = 0; row < opts.size; row++) {
- rowdata = []
- for (col = 0; col < opts.size; col++) {
- rowdata.push([255, 255, 255]);
- }
- initial_texture.push(rowdata);
+var Player = function (settings, dimness) {
+ if (!(this instanceof Player)) {
+ return new Player();
}
- var salt = $("#grid").data("identicon-salt");
- for (var i = 0; i < num_identicons; i++) {
- texture = new pixdenticon(md5(salt + i), opts.size).render().buffer;
- for (row = 0; row < opts.size; row++) {
- initial_texture.push(texture[row]);
- }
+ this.id = settings.id;
+ this.position = settings.position;
+ this.positionInSync = true;
+ this.color = settings.color;
+ this.motion_auto = settings.motion_auto;
+ this.motion_direction = settings.motion_direction;
+ this.motion_speed_limit = settings.motion_speed_limit;
+ this.motion_timestamp = settings.motion_timestamp;
+ this.score = settings.score;
+ this.payoff = settings.payoff;
+ this.name = settings.name;
+ this.identity_visible = settings.identity_visible;
+ this.dimness = dimness;
+ return this;
+};
+
+Player.prototype.move = function(direction) {
+ function _hasWall(position) {
+ return wall_map[[position[1], position[0]]] !== undefined;
}
- var texture = regl.texture(initial_texture);
+ this.motion_direction = direction;
- var squares = regl({
- vert: `
- precision mediump float;
- attribute vec2 position;
- attribute vec2 texcoords;
- attribute vec3 color;
- varying vec3 vcolor;
- varying vec2 v_texcoords;
- void main() {
- gl_PointSize = float(${opts.size});
- gl_Position = vec4(position.x, position.y, 0.0, 1.0);
- v_texcoords = texcoords;
- vcolor = color;
- }
- `,
- frag: `
- precision mediump float;
- varying vec3 vcolor;
- varying vec2 v_texcoords;
- uniform sampler2D vtexture;
- void main() {
- vec4 texture;
- texture = texture2D(vtexture, v_texcoords);
- gl_FragColor = texture * vec4(vcolor.r, vcolor.g, vcolor.b, 1.0);
- }
- `,
- attributes: { position: regl.prop("position"), texcoords: regl.prop("texcoords"), color: regl.prop("color")},
- primitive: "triangles",
- count: colors.length * 6,
- uniforms: { vtexture: texture }
- });
+ var ts = performance.now() - start,
+ waitTime = 1000 / this.motion_speed_limit;
- var expanded_colors = [];
- for(var i = 0; i < colors.length; ++i){
- for(var n = 0; n < 6; ++n) {
- expanded_colors.push(colors[i]);
- }
- }
+ if (ts > this.motion_timestamp + waitTime) {
+ var newPosition = this.position.slice();
- var buffer = { position: regl.buffer(positions), texcoords: regl.buffer(texcoords), color: regl.buffer(expanded_colors)};
+ switch (direction) {
+ case "up":
+ if (this.position[0] > 0) {
+ newPosition[0] -= 1;
+ }
+ break;
- var draw = function(positions, texcoords, colors) {
- regl.clear({ color: opts.background.concat([ 1 ]) });
- squares({ position: positions, texcoords: texcoords, color: colors });
- };
+ case "down":
+ if (this.position[0] < settings.rows - 1) {
+ newPosition[0] += 1;
+ }
+ break;
- draw(buffer.position, buffer.texcoords, buffer.color);
+ case "left":
+ if (this.position[1] > 0) {
+ newPosition[1] -= 1;
+ }
+ break;
- self._buffer = buffer;
- self._draw = draw;
- self._formatted = opts.formatted;
- self.canvas = canvas;
- self.frame = regl.frame;
-}
+ case "right":
+ if (this.position[1] < settings.columns - 1) {
+ newPosition[1] += 1;
+ }
+ break;
-Pixels.prototype.update = function(data, textures) {
- var self = this;
- var colors = self._formatted ? data : convert(data);
- var expanded_colors = [];
+ default:
+ console.log("Direction not recognized.");
+ }
- for(var i = 0; i < colors.length; ++i){
- for(var n = 0; n < 6; ++n) {
- expanded_colors.push(colors[i]);
+ if (!_hasWall(newPosition) && (!players.isPlayerAt(newPosition) || settings.player_overlap)) {
+ this.position = newPosition;
+ this.motion_timestamp = ts;
+ return true;
}
}
-
- var opts = this.opts;
- var num_identicons = 100;
-
- var texcoords = texcoord(
- opts.rows,
- opts.columns,
- textures,
- num_identicons
- );
-
- self._draw(self._buffer.position, self._buffer.texcoords(texcoords), self._buffer.color(expanded_colors));
+ return false;
};
-module.exports = Pixels;
-
+var playerSet = (function () {
-/***/ }),
-/* 8 */
-/***/ (function(module, exports, __webpack_require__) {
+ var PlayerSet = function (settings) {
+ if (!(this instanceof PlayerSet)) {
+ return new PlayerSet(settings);
+ }
-/**
- * Identicon.js 2.3.1
- * http://github.com/stewartlord/identicon.js
- *
- * PNGLib required for PNG output
- * http://www.xarg.org/download/pnglib.js
- *
- * Copyright 2017, Stewart Lord
- * Released under the BSD license
- * http://www.opensource.org/licenses/bsd-license.php
- */
+ this._players = {};
+ this.ego_id = settings.ego_id;
+ };
-(function() {
- var PNGlib;
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- PNGlib = __webpack_require__(22);
- } else {
- PNGlib = window.PNGlib;
- }
+ PlayerSet.prototype.isPlayerAt = function (position) {
+ var id, player;
- var Identicon = function(hash, options){
- if (typeof(hash) !== 'string' || hash.length < 15) {
- throw 'A hash of at least 15 characters is required.';
+ for (id in this._players) {
+ if (this._players.hasOwnProperty(id)) {
+ player = this._players[id];
+ if (positionsAreEqual(position, player.position)) {
+ return true;
+ }
}
+ }
+ return false;
+ };
- this.defaults = {
- background: [240, 240, 240, 255],
- margin: 0.08,
- size: 64,
- saturation: 0.7,
- brightness: 0.5,
- format: 'png'
- };
-
- this.options = typeof(options) === 'object' ? options : this.defaults;
-
- // backward compatibility with old constructor (hash, size, margin)
- if (typeof(arguments[1]) === 'number') { this.options.size = arguments[1]; }
- if (arguments[2]) { this.options.margin = arguments[2]; }
-
- this.hash = hash
- this.background = this.options.background || this.defaults.background;
- this.size = this.options.size || this.defaults.size;
- this.format = this.options.format || this.defaults.format;
- this.margin = this.options.margin !== undefined ? this.options.margin : this.defaults.margin;
-
- // foreground defaults to last 7 chars as hue at 70% saturation, 50% brightness
- var hue = parseInt(this.hash.substr(-7), 16) / 0xfffffff;
- var saturation = this.options.saturation || this.defaults.saturation;
- var brightness = this.options.brightness || this.defaults.brightness;
- this.foreground = this.options.foreground || this.hsl2rgb(hue, saturation, brightness);
- };
-
- Identicon.prototype = {
- background: null,
- foreground: null,
- hash: null,
- margin: null,
- size: null,
- format: null,
-
- image: function(){
- return this.isSvg()
- ? new Svg(this.size, this.foreground, this.background)
- : new PNGlib(this.size, this.size, 256);
- },
-
- render: function(){
- var image = this.image(),
- size = this.size,
- baseMargin = Math.floor(size * this.margin),
- cell = Math.floor((size - (baseMargin * 2)) / 5),
- margin = Math.floor((size - cell * 5) / 2),
- bg = image.color.apply(image, this.background),
- fg = image.color.apply(image, this.foreground);
+ PlayerSet.prototype.drawToGrid = function (grid) {
+ var positions = [],
+ idx,
+ player,
+ id,
+ minScore,
+ maxScore,
+ d,
+ color,
+ player_color;
+ if (settings.score_visible) {
+ minScore = this.minScore();
+ maxScore = this.maxScore();
+ }
- // the first 15 characters of the hash control the pixels (even/odd)
- // they are drawn down the middle first, then mirrored outwards
- var i, color;
- for (i = 0; i < 15; i++) {
- color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg;
- if (i < 5) {
- this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image);
- } else if (i < 10) {
- this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
- this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
- } else if (i < 15) {
- this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
- this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
- }
+ for (id in this._players) {
+ if (this._players.hasOwnProperty(id)) {
+ player = this._players[id];
+ /* It's unlikely that auto motion will keep identical pace to server-side auto-motion */
+ /* this should be implemented either all on server or all on client */
+ if (player.motion_auto) {
+ player.move(player.motion_direction);
+ }
+ if (id === this.ego_id || settings.others_visible) {
+ player_color = settings.player_colors[name2idx(player.color)];
+ if (player.identity_visible) {
+ color = player_color;
+ } else {
+ color = (id === this.ego_id) ? Color.rgb(player_color).desaturate(0.6).rgb().array() : INVISIBLE_COLOR;
}
-
- return image;
- },
-
- rectangle: function(x, y, w, h, color, image){
- if (this.isSvg()) {
- image.rectangles.push({x: x, y: y, w: w, h: h, color: color});
+ if (settings.score_visible) {
+ if (maxScore-minScore > 0) {
+ d = 0.75 * (1 - (player.score-minScore)/(maxScore-minScore));
+ } else {
+ d = 0.375;
+ }
+ color = Color.rgb(player_color).desaturate(d).rgb().array();
} else {
- var i, j;
- for (i = x; i < x + w; i++) {
- for (j = y; j < y + h; j++) {
- image.buffer[image.index(i, j)] = color;
- }
- }
+ color = player_color;
}
- },
+ var texture = 0;
+ if (settings.use_identicons) {
+ texture = parseInt(id, 10);
+ }
+ grid.plot(player.position[1], player.position[0], color, texture);
+ if (id === this.ego_id) {
+ store.set("color", color2name(color));
+ }
+ }
+ }
+ }
+ };
- // adapted from: https://gist.github.com/aemkei/1325937
- hsl2rgb: function(h, s, b){
- h *= 6;
- s = [
- b += s *= b < .5 ? b : 1 - b,
- b - h % 1 * s * 2,
- b -= s *= 2,
- b,
- b + h % 1 * s,
- b + s
- ];
+ PlayerSet.prototype.nearest = function (row, column) {
+ var distances = [],
+ distance,
+ player,
+ id;
- return[
- s[ ~~h % 6 ] * 255, // red
- s[ (h|16) % 6 ] * 255, // green
- s[ (h|8) % 6 ] * 255 // blue
- ];
- },
+ for (id in this._players) {
+ if (this._players.hasOwnProperty(id)) {
+ player = this._players[id];
+ if (player.hasOwnProperty('position')) {
+ distance = Math.abs(row - player.position[0]) + Math.abs(column - player.position[1]);
+ distances.push({"player": player, "distance": distance});
+ }
+ }
+ }
- toString: function(raw){
- // backward compatibility with old toString, default to base64
- if (raw) {
- return this.render().getDump();
- } else {
- return this.render().getBase64();
- }
- },
+ distances.sort(function (a, b) {
+ return a.distance - b.distance;
+ });
- isSvg: function(){
- return this.format.match(/svg/i)
- }
+ return distances[0].player;
};
- var Svg = function(size, foreground, background){
- this.size = size;
- this.foreground = this.color.apply(this, foreground);
- this.background = this.color.apply(this, background);
- this.rectangles = [];
+ PlayerSet.prototype.ego = function () {
+ return this.get(this.ego_id);
};
- Svg.prototype = {
- size: null,
- foreground: null,
- background: null,
- rectangles: null,
-
- color: function(r, g, b, a){
- var values = [r, g, b].map(Math.round);
- values.push((a >= 0) && (a <= 255) ? a/255 : 1);
- return 'rgba(' + values.join(',') + ')';
- },
+ PlayerSet.prototype.get = function (id) {
+ return this._players[id];
+ };
- getDump: function(){
- var i,
- xml,
- rect,
- fg = this.foreground,
- bg = this.background,
- stroke = this.size * 0.005;
+ PlayerSet.prototype.count = function () {
+ return Object.keys(this._players).length;
+ };
- xml = ""
- + "";
+ PlayerSet.prototype.update = function (allPlayersData) {
+ var freshPlayerData,
+ existingPlayer,
+ i;
- for (i = 0; i < this.rectangles.length; i++) {
- rect = this.rectangles[i];
- if (rect.color == bg) continue;
- xml += " ";
- }
- xml += " "
+ for (i = 0; i < allPlayersData.length; i++) {
+ freshPlayerData = allPlayersData[i];
+ existingPlayer = this._players[freshPlayerData.id];
+ if (existingPlayer && existingPlayer.id === this.ego_id) {
- return xml;
- },
+ /* Don't override current player motion timestamp */
+ freshPlayerData.motion_timestamp = existingPlayer.motion_timestamp;
- getBase64: function(){
- return btoa(this.getDump());
+ // Only override position from server if tremble is enabled,
+ // or if we know the Player's position is out of sync with the server.
+ // Otherwise, the ego player's motion is constantly jittery.
+ if (settings.motion_tremble_rate === 0 && existingPlayer.positionInSync) {
+ freshPlayerData.position = existingPlayer.position;
+ } else {
+ console.log("Overriding position from server!");
+ }
+ }
+ var last_dimness = 1;
+ if (this._players[freshPlayerData.id] !== undefined) {
+ last_dimness = this._players[freshPlayerData.id].dimness;
}
+ this._players[freshPlayerData.id] = new Player(freshPlayerData, last_dimness);
+ }
};
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = Identicon;
- } else {
- window.Identicon = Identicon;
- }
-})();
-
-/***/ }),
-/* 9 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
+ PlayerSet.prototype.startScheduledAutosyncOfEgoPosition = function () {
+ var self = this;
+ setInterval(function () {
+ var ego = self.ego();
+ if (ego) {
+ ego.positionInSync = false;
+ console.log("Scheduled marking of (" + ego.id + ") as out of sync with server.");
+ }
+ }, 5000);
+ };
+ PlayerSet.prototype.maxScore = function () {
+ var id,
+ maxScore = 0;
+ for (id in this._players) {
+ if (this._players[id].score > maxScore) {
+ maxScore = this._players[id].score;
+ }
+ }
+ return maxScore;
+ };
-var colorString = __webpack_require__(28);
-var convert = __webpack_require__(25);
+ PlayerSet.prototype.minScore = function () {
+ var id,
+ minScore = Infinity;
+ for (id in this._players) {
+ if (this._players[id].score < minScore) {
+ minScore = this._players[id].score;
+ }
+ }
+ return minScore;
+ };
-var _slice = [].slice;
+ PlayerSet.prototype.each = function (callback) {
+ var i = 0;
+ for (var id in this._players) {
+ if (this._players.hasOwnProperty(id)) {
+ callback(i, this._players[id]);
+ i++;
+ }
+ }
+ };
-var skippedModels = [
- // to be honest, I don't really feel like keyword belongs in color convert, but eh.
- 'keyword',
+ PlayerSet.prototype.group_scores = function () {
+ var group_scores = {};
- // gray conflicts with some method names, and has its own method defined.
- 'gray',
+ this.each(function (i, player) {
+ var color_name = player.color;
+ var cur_score = group_scores[color_name] || 0;
+ group_scores[color_name] = cur_score + Math.round(player.score);
+ });
- // shouldn't really be in color-convert either...
- 'hex'
-];
+ var group_order = Object.keys(group_scores).sort(function (a, b) {
+ return group_scores[a] > group_scores[b] ? -1 : (group_scores[a] < group_scores[b] ? 1 : 0);
+ });
-var hashedModelKeys = {};
-Object.keys(convert).forEach(function (model) {
- hashedModelKeys[_slice.call(convert[model].labels).sort().join('')] = model;
-});
+ return group_order.map(function(color_name) {
+ return {name: color_name, score: group_scores[color_name]};
+ });
+ };
-var limiters = {};
+ PlayerSet.prototype.player_scores = function () {
+ var player_order = [];
-function Color(obj, model) {
- if (!(this instanceof Color)) {
- return new Color(obj, model);
- }
+ this.each(function(i, player) {
+ player_order.push({id: player.id, name: player.name, score:player.score});
+ });
- if (model && model in skippedModels) {
- model = null;
- }
+ player_order = player_order.sort(function (a, b) {
+ return a.score > b.score ? -1 : (a.score < b.score ? 1 : 0);
+ });
- if (model && !(model in convert)) {
- throw new Error('Unknown model: ' + model);
- }
+ return player_order;
+ };
- var i;
- var channels;
+ return PlayerSet;
+}());
- if (obj == null) { // eslint-disable-line no-eq-null,eqeqeq
- this.model = 'rgb';
- this.color = [0, 0, 0];
- this.valpha = 1;
- } else if (obj instanceof Color) {
- this.model = obj.model;
- this.color = obj.color.slice();
- this.valpha = obj.valpha;
- } else if (typeof obj === 'string') {
- var result = colorString.get(obj);
- if (result === null) {
- throw new Error('Unable to parse color from string: ' + obj);
- }
+var GUSocket = (function () {
+ var makeSocket = function (endpoint, channel, tolerance) {
+ var ws_scheme = (window.location.protocol === "https:") ? 'wss://' : 'ws://',
+ app_root = ws_scheme + location.host + '/',
+ socket;
- this.model = result.model;
- channels = convert[this.model].channels;
- this.color = result.value.slice(0, channels);
- this.valpha = typeof result.value[channels] === 'number' ? result.value[channels] : 1;
- } else if (obj.length) {
- this.model = model || 'rgb';
- channels = convert[this.model].channels;
- var newArr = _slice.call(obj, 0, channels);
- this.color = zeroArray(newArr, channels);
- this.valpha = typeof obj[channels] === 'number' ? obj[channels] : 1;
- } else if (typeof obj === 'number') {
- // this is always RGB - can be converted later on.
- obj &= 0xFFFFFF;
- this.model = 'rgb';
- this.color = [
- (obj >> 16) & 0xFF,
- (obj >> 8) & 0xFF,
- obj & 0xFF
- ];
- this.valpha = 1;
- } else {
- this.valpha = 1;
+ socket = new ReconnectingWebSocket(
+ app_root + endpoint + "?channel=" + channel + "&tolerance=" + tolerance
+ );
+ socket.debug = true;
- var keys = Object.keys(obj);
- if ('alpha' in obj) {
- keys.splice(keys.indexOf('alpha'), 1);
- this.valpha = typeof obj.alpha === 'number' ? obj.alpha : 0;
- }
+ return socket;
+ };
- var hashedKeys = keys.sort().join('');
- if (!(hashedKeys in hashedModelKeys)) {
- throw new Error('Unable to parse color from object: ' + JSON.stringify(obj));
- }
+ var dispatch = function (self, event) {
+ var marker = self.broadcastChannel + ':';
+ if (event.data.indexOf(marker) !== 0) {
+ console.log(
+ "Message was not on channel " + self.broadcastChannel + ". Ignoring.");
+ return;
+ }
+ var msg = JSON.parse(event.data.substring(marker.length));
- this.model = hashedModelKeys[hashedKeys];
+ var callback = self.callbackMap[msg.type];
+ if (typeof callback !== 'undefined') {
+ callback(msg);
+ } else {
+ console.log("Unrecognized message type " + msg.type + ' from backend.');
+ }
+ };
- var labels = convert[this.model].labels;
- var color = [];
- for (i = 0; i < labels.length; i++) {
- color.push(obj[labels[i]]);
- }
+ /*
+ * Public API
+ */
+ var Socket = function (settings) {
+ if (!(this instanceof Socket)) {
+ return new Socket(settings);
+ }
- this.color = zeroArray(color);
- }
+ var self = this,
+ isOpen = $.Deferred(),
+ tolerance = typeof(settings.lagTolerance) !== 'undefined' ? settings.lagTolerance : 0.1;
- // perform limitations (clamping, etc.)
- if (limiters[this.model]) {
- channels = convert[this.model].channels;
- for (i = 0; i < channels; i++) {
- var limit = limiters[this.model][i];
- if (limit) {
- this.color[i] = limit(this.color[i]);
- }
- }
- }
+ this.broadcastChannel = settings.broadcast;
+ this.controlChannel = settings.control;
+ this.callbackMap = settings.callbackMap;
- this.valpha = Math.max(0, Math.min(1, this.valpha));
- if (Object.freeze) {
- Object.freeze(this);
- }
-}
+ this.socket = makeSocket(
+ settings.endpoint, this.broadcastChannel, tolerance);
-Color.prototype = {
- toString: function () {
- return this.string();
- },
+ this.socket.onmessage = function (event) {
+ dispatch(self, event);
+ };
+ };
- toJSON: function () {
- return this[this.model]();
- },
+ Socket.prototype.open = function () {
+ var isOpen = $.Deferred();
- string: function (places) {
- var self = this.model in colorString.to ? this : this.rgb();
- self = self.round(typeof places === 'number' ? places : 1);
- var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);
- return colorString.to[self.model](args);
- },
+ this.socket.onopen = function (event) {
+ isOpen.resolve();
+ };
- percentString: function (places) {
- var self = this.rgb().round(typeof places === 'number' ? places : 1);
- var args = self.valpha === 1 ? self.color : self.color.concat(this.valpha);
- return colorString.to.rgb.percent(args);
- },
+ return isOpen;
+ };
- array: function () {
- return this.valpha === 1 ? this.color.slice() : this.color.concat(this.valpha);
- },
+ Socket.prototype.send = function (data) {
+ var msg = JSON.stringify(data),
+ channel = this.controlChannel;
- object: function () {
- var result = {};
- var channels = convert[this.model].channels;
- var labels = convert[this.model].labels;
+ console.log("Sending message to the " + channel + " channel: " + msg);
+ this.socket.send(channel + ':' + msg);
+ };
- for (var i = 0; i < channels; i++) {
- result[labels[i]] = this.color[i];
- }
+ Socket.prototype.broadcast = function (data) {
+ var msg = JSON.stringify(data),
+ channel = this.broadcastChannel;
- if (this.valpha !== 1) {
- result.alpha = this.valpha;
- }
+ console.log("Broadcasting message to the " + channel + " channel: " + msg);
+ this.socket.send(channel + ':' + msg);
+ };
- return result;
- },
+ return Socket;
+}());
- unitArray: function () {
- var rgb = this.rgb().color;
- rgb[0] /= 255;
- rgb[1] /= 255;
- rgb[2] /= 255;
+// ego will be updated on page load
+var players = playerSet({'ego_id': undefined});
- if (this.valpha !== 1) {
- rgb.push(this.valpha);
- }
-
- return rgb;
- },
-
- unitObject: function () {
- var rgb = this.rgb().object();
- rgb.r /= 255;
- rgb.g /= 255;
- rgb.b /= 255;
+pixels.canvas.style.marginLeft = window.innerWidth * 0.03 / 2 + "px";
+pixels.canvas.style.marginTop = window.innerHeight * 0.04 / 2 + "px";
+document.body.style.transition = "0.3s all";
+document.body.style.background = "#ffffff";
- if (this.valpha !== 1) {
- rgb.alpha = this.valpha;
- }
+var startTime = performance.now();
- return rgb;
- },
+pixels.frame(function() {
+ // Update the background.
+ var ego = players.ego(),
+ w = getWindowPosition(),
+ dimness,
+ rescaling,
+ i, j, x, y;
- round: function (places) {
- places = Math.max(places || 0, 0);
- return new Color(this.color.map(roundToPlace(places)).concat(this.valpha), this.model);
- },
+ var section = new Section(background, w.left, w.top);
- alpha: function (val) {
- if (arguments.length) {
- return new Color(this.color.concat(Math.max(0, Math.min(1, val))), this.model);
- }
+ // Animate background for each visible cell
+ section.map(function(x, y, color) {
+ var newColor = animateColor(color);
+ background[coordsToIdx(x, y, settings.columns)] = newColor;
+ return newColor;
+ });
- return this.valpha;
- },
+ for (i = 0; i < items.length; i++) {
+ var currentItem = items[i];
+ if (players.isPlayerAt(currentItem.position)) {
+ if (! currentItem.interactive) {
+ // Non-interactive items get consumed immediately
+ itemsConsumed.push(items.splice(i, 1)); // XXX this push does nothing, AFAICT (Jesse)
+ }
+ // Else: show info about the item in some way
+ } else {
+ section.plot(currentItem.position[1], currentItem.position[0], currentItem.color);
+ }
+ }
- // rgb
- red: getset('rgb', 0, maxfn(255)),
- green: getset('rgb', 1, maxfn(255)),
- blue: getset('rgb', 2, maxfn(255)),
+ // Draw the players:
+ players.drawToGrid(section);
- hue: getset(['hsl', 'hsv', 'hsl', 'hwb', 'hcg'], 0, function (val) { return ((val % 360) + 360) % 360; }), // eslint-disable-line brace-style
+ // Add the Gaussian mask.
+ var elapsedTime = performance.now() - startTime;
+ var visibilityNow = clamp(
+ (settings.visibility * elapsedTime) / (1000 * settings.visibility_ramp_time),
+ 3,
+ settings.visibility
+ );
+ if (settings.highlightEgo) {
+ visibilityNow = Math.min(visibilityNow, 4);
+ }
+ var g = gaussian(0, Math.pow(visibilityNow, 2));
+ rescaling = 1 / g.pdf(0);
- saturationl: getset('hsl', 1, maxfn(100)),
- lightness: getset('hsl', 2, maxfn(100)),
+ if (typeof ego !== "undefined") {
+ x = ego.position[1];
+ y = ego.position[0];
+ } else {
+ x = 1e100;
+ y = 1e100;
+ }
+ section.map(function(i, j, color) {
+ var newColor;
+ // Draw walls
+ if (settings.walls_visible) {
+ color = wall_map[[i,j]] || color;
+ }
+ // Add Blur
+ players.each(function (i, player) {
+ dimness = g.pdf(distance(y, x, player.position[0], player.position[1])) * rescaling;
+ player["dimness"] = dimness;
+ });
+ newColor = color;
+ if (!isSpectator) {
+ dimness = g.pdf(distance(x, y, i, j)) * rescaling;
+ newColor = [
+ color[0] * dimness,
+ color[1] * dimness,
+ color[2] * dimness
+ ];
+ }
+ return newColor;
+ });
+ pixels.update(section.data, section.textures);
+});
- saturationv: getset('hsv', 1, maxfn(100)),
- value: getset('hsv', 2, maxfn(100)),
+function clamp(val, min, max) {
+ return Math.max(min, Math.min(max, val));
+}
- chroma: getset('hcg', 1, maxfn(100)),
- gray: getset('hcg', 2, maxfn(100)),
+function distance(x, y, xx, yy) {
+ return Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));
+}
- white: getset('hwb', 1, maxfn(100)),
- wblack: getset('hwb', 2, maxfn(100)),
+function arraysEqual(arr1, arr2) {
+ for (var i = arr1.length; i--; ) {
+ if (arr1[i] !== arr2[i]) {
+ return false;
+ }
+ }
+ return true;
+}
- cyan: getset('cmyk', 0, maxfn(100)),
- magenta: getset('cmyk', 1, maxfn(100)),
- yellow: getset('cmyk', 2, maxfn(100)),
- black: getset('cmyk', 3, maxfn(100)),
+function arraySearch(arr, val) {
+ for (var i = 0; i < arr.length; i++) {
+ if (arraysEqual(arr[i], val)) {
+ return i;
+ }
+ }
+ return false;
+}
- x: getset('xyz', 0, maxfn(100)),
- y: getset('xyz', 1, maxfn(100)),
- z: getset('xyz', 2, maxfn(100)),
+function getRandomInt(min, max) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}
- l: getset('lab', 0, maxfn(100)),
- a: getset('lab', 1),
- b: getset('lab', 2),
+function getWindowPosition() {
+ var ego = players.ego(),
+ w = {
+ left: 0,
+ top: 0,
+ columns: settings.window_columns,
+ rows: settings.window_rows
+ };
- keyword: function (val) {
- if (arguments.length) {
- return new Color(val);
- }
+ if (typeof ego !== 'undefined') {
+ w.left = clamp(
+ ego.position[1] - Math.floor(settings.window_columns / 2),
+ 0, settings.columns - settings.window_columns);
+ w.top = clamp(
+ ego.position[0] - Math.floor(settings.window_rows / 2),
+ 0, settings.rows - settings.window_rows);
+ }
+ return w;
+}
- return convert[this.model].keyword(this.color);
- },
+function bindGameKeys(socket) {
+ var directions = ["up", "down", "left", "right"],
+ repeatDelayMS = 1000 / settings.motion_speed_limit,
+ lastDirection = null,
+ repeatIntervalId = null,
+ highlightEgo = false;
- hex: function (val) {
- if (arguments.length) {
- return new Color(val);
- }
+ function moveInDir(direction) {
+ var ego = players.ego();
+ if (ego.move(direction) ) {
+ var msg = {
+ type: "move",
+ player_id: ego.id,
+ move: direction,
+ timestamp: ego.motion_timestamp
+ };
+ socket.send(msg);
+ }
+ }
- return colorString.to.hex(this.rgb().round().color);
- },
+ directions.forEach(function(direction) {
+ Mousetrap.bind(
+ direction,
+ function(e) {
+ e.preventDefault();
+ if (direction === lastDirection) {
+ return;
+ }
- rgbNumber: function () {
- var rgb = this.rgb().color;
- return ((rgb[0] & 0xFF) << 16) | ((rgb[1] & 0xFF) << 8) | (rgb[2] & 0xFF);
- },
+ // New direction may be pressed before previous dir key is released
+ if (repeatIntervalId) {
+ clearInterval(repeatIntervalId);
+ }
- luminosity: function () {
- // http://www.w3.org/TR/WCAG20/#relativeluminancedef
- var rgb = this.rgb().color;
+ moveInDir(direction); // Move once immediately so there's no lag
+ lastDirection = direction;
+ repeatIntervalId = setInterval(moveInDir, repeatDelayMS, direction);
+ },
+ 'keydown'
+ );
- var lum = [];
- for (var i = 0; i < rgb.length; i++) {
- var chan = rgb[i] / 255;
- lum[i] = (chan <= 0.03928) ? chan / 12.92 : Math.pow(((chan + 0.055) / 1.055), 2.4);
- }
+ Mousetrap.bind(
+ direction,
+ function(e) {
+ e.preventDefault();
+ if (direction) {
+ clearInterval(repeatIntervalId);
+ lastDirection = null;
+ }
+ },
+ "keyup"
+ );
- return 0.2126 * lum[0] + 0.7152 * lum[1] + 0.0722 * lum[2];
- },
+ });
- contrast: function (color2) {
- // http://www.w3.org/TR/WCAG20/#contrast-ratiodef
- var lum1 = this.luminosity();
- var lum2 = color2.luminosity();
+ Mousetrap.bind("space", function () {
+ var msg = {
+ type: "plant_food",
+ player_id: players.ego().id,
+ position: players.ego().position
+ };
+ socket.send(msg);
+ });
- if (lum1 > lum2) {
- return (lum1 + 0.05) / (lum2 + 0.05);
- }
+ if (settings.mutable_colors) {
+ Mousetrap.bind('c', function () {
+ var keys = settings.player_color_names,
+ index = arraySearch(keys, players.ego().color),
+ nextItem = keys[(index + 1) % keys.length],
+ msg;
- return (lum2 + 0.05) / (lum1 + 0.05);
- },
-
- level: function (color2) {
- var contrastRatio = this.contrast(color2);
- if (contrastRatio >= 7.1) {
- return 'AAA';
- }
+ players.ego().color = nextItem;
+ msg = {
+ type: "change_color",
+ player_id: players.ego().id,
+ color: players.ego().color
+ };
+ socket.send(msg);
+ });
+ }
- return (contrastRatio >= 4.5) ? 'AA' : '';
- },
+ if (settings.identity_signaling) {
+ Mousetrap.bind("v", function () {
+ var ego = players.ego(),
+ msg;
- isDark: function () {
- // YIQ equation from http://24ways.org/2010/calculating-color-contrast
- var rgb = this.rgb().color;
- var yiq = (rgb[0] * 299 + rgb[1] * 587 + rgb[2] * 114) / 1000;
- return yiq < 128;
- },
+ ego.identity_visible = !ego.identity_visible;
+ msg = {
+ type: "toggle_visible",
+ player_id: ego.id,
+ identity_visible: ego.identity_visible
+ };
+ socket.send(msg);
+ });
+ }
- isLight: function () {
- return !this.isDark();
- },
+ if (settings.build_walls) {
+ Mousetrap.bind("w", function () {
+ var msg = {
+ type: "build_wall",
+ player_id: players.ego().id,
+ position: players.ego().position
+ };
+ socket.send(msg);
+ });
+ }
- negate: function () {
- var rgb = this.rgb();
- for (var i = 0; i < 3; i++) {
- rgb.color[i] = 255 - rgb.color[i];
- }
- return rgb;
- },
+ Mousetrap.bind("h", function () {
+ settings.highlightEgo = !settings.highlightEgo;
+ });
+}
- lighten: function (ratio) {
- var hsl = this.hsl();
- hsl.color[2] += hsl.color[2] * ratio;
- return hsl;
- },
+function chatName(player_id) {
+ var ego = players.ego(),
+ entry = "",
+ id = parseInt(player_id) - 1,
+ salt = $("#grid").data("identicon-salt"),
+ fg = settings.player_colors[name2idx(players.get(player_id).color)].concat(1),
+ bg,
+ identicon,
+ name,
+ options;
- darken: function (ratio) {
- var hsl = this.hsl();
- hsl.color[2] -= hsl.color[2] * ratio;
- return hsl;
- },
+ if (id === ego) {
+ name = "You";
+ } else if (settings.pseudonyms) {
+ name = players.get(player_id).name;
+ } else if (player_id % 1 === 0) {
+ name = "Player " + player_id;
+ } else {
+ // Non-integer player_id
+ return '' + player_id + ' ';
+ }
- saturate: function (ratio) {
- var hsl = this.hsl();
- hsl.color[1] += hsl.color[1] * ratio;
- return hsl;
- },
+ fg = fg.map(function(x) { return x * 255; });
+ bg = fg.map(function(x) { return (x * 0.66); });
+ bg[3] = 255;
+ options = {
+ size: 10,
+ foreground: fg,
+ background: bg,
+ format: 'svg'
+ };
- desaturate: function (ratio) {
- var hsl = this.hsl();
- hsl.color[1] -= hsl.color[1] * ratio;
- return hsl;
- },
+ identicon = new Identicon(md5(salt + id), options).toString();
+ if (settings.use_identicons) {
+ entry = entry + " ";
+ }
+ entry = entry + " " + name + " ";
+ return entry;
+}
- whiten: function (ratio) {
- var hwb = this.hwb();
- hwb.color[1] += hwb.color[1] * ratio;
- return hwb;
- },
+function onChatMessage(msg) {
+ var entry = chatName(msg.player_id);
+ if (settings.spatial_chat && players.get(msg.player_id).dimness < settings.chat_visibility_threshold) {
+ return;
+ }
+ $("#messages").append(($("").text(": " + msg.contents)).prepend(entry));
+ $("#chatlog").scrollTop($("#chatlog")[0].scrollHeight);
+}
- blacken: function (ratio) {
- var hwb = this.hwb();
- hwb.color[2] += hwb.color[2] * ratio;
- return hwb;
- },
+function onColorChanged(msg) {
+ store.set("color", msg.new_color);
+ if (settings.spatial_chat && players.get(msg.player_id).dimness < settings.chat_visibility_threshold) {
+ return;
+ }
+ pushMessage("Moderator: " + chatName(msg.player_id) + ' changed from team ' + msg.old_color + ' to team ' + msg.new_color + '.');
+}
- grayscale: function () {
- // http://en.wikipedia.org/wiki/Grayscale#Converting_color_to_grayscale
- var rgb = this.rgb().color;
- var val = rgb[0] * 0.3 + rgb[1] * 0.59 + rgb[2] * 0.11;
- return Color.rgb(val, val, val);
- },
+function onMoveRejected(msg) {
+ var offendingPlayerId = msg.player_id,
+ ego = players.ego();
- fade: function (ratio) {
- return this.alpha(this.valpha - (this.valpha * ratio));
- },
+ if (ego && offendingPlayerId === ego.id) {
+ ego.positionInSync = false;
+ console.log("Marking your player (" + ego.id + ") as out of sync with server. Should sync on next state update");
+ }
+}
- opaquer: function (ratio) {
- return this.alpha(this.valpha + (this.valpha * ratio));
- },
+function onDonationProcessed(msg) {
+ var donor = players.get(msg.donor_id),
+ recipient_id = msg.recipient_id,
+ team_idx,
+ donor_name,
+ recipient_name,
+ donated_points,
+ received_points,
+ entry;
- rotate: function (degrees) {
- var hsl = this.hsl();
- var hue = hsl.color[0];
- hue = (hue + degrees) % 360;
- hue = hue < 0 ? 360 + hue : hue;
- hsl.color[0] = hue;
- return hsl;
- },
+ donor_name = chatName(msg.donor_id);
- mix: function (mixinColor, weight) {
- // ported from sass implementation in C
- // https://github.com/sass/libsass/blob/0e6b4a2850092356aa3ece07c6b249f0221caced/functions.cpp#L209
- if (!mixinColor || !mixinColor.rgb) {
- throw new Error('Argument to "mix" was not a Color instance, but rather an instance of ' + typeof mixinColor);
- }
- var color1 = mixinColor.rgb();
- var color2 = this.rgb();
- var p = weight === undefined ? 0.5 : weight;
+ if (recipient_id === 'all') {
+ recipient_name = 'All players ';
+ } else if (recipient_id.indexOf('group:') === 0) {
+ team_idx = +recipient_id.substring(6);
+ recipient_name = 'Everyone in ' + settings.player_color_names[team_idx] + ' ';
+ } else {
+ recipient_name = chatName(recipient_id);
+ }
- var w = 2 * p - 1;
- var a = color1.alpha() - color2.alpha();
+ if (msg.amount === 1) {
+ donated_points = msg.amount + ' point.';
+ } else {
+ donated_points = msg.amount + ' points.';
+ }
- var w1 = (((w * a === -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
- var w2 = 1 - w1;
+ if (msg.received === 1) {
+ received_points = msg.received + ' point.';
+ } else {
+ received_points = msg.received + ' points.';
+ }
- return Color.rgb(
- w1 * color1.red() + w2 * color2.red(),
- w1 * color1.green() + w2 * color2.green(),
- w1 * color1.blue() + w2 * color2.blue(),
- color1.alpha() * p + color2.alpha() * (1 - p));
- }
-};
+ entry = donor_name + " contributed " + donated_points + " " + recipient_name + " received " + received_points;
-// model conversion methods and static constructors
-Object.keys(convert).forEach(function (model) {
- if (skippedModels.indexOf(model) !== -1) {
- return;
- }
+ $("#messages").append($(" ").html(entry));
+ $("#chatlog").scrollTop($("#chatlog")[0].scrollHeight);
+ $('#individual-donate, #group-donate').addClass('button-outline');
+ $('#donate label').text($('#donate label').data('orig-text'));
+ settings.donation_type = null;
+}
- var channels = convert[model].channels;
+function updateDonationStatus(donation_is_active) {
+ // If alternating donation/consumption rounds, announce round type
+ if (settings.alternate_consumption_donation && (settings.donation_active !== donation_is_active)) {
+ if (donation_is_active) {
+ pushMessage("Moderator: Starting a donation round. Players cannot move, only donate.");
+ } else {
+ pushMessage("Moderator: Starting a consumption round. Players have to consume as much food as possible.");
+ }
+ }
+ // Update donation status
+ settings.donation_active = donation_is_active;
+}
- // conversion methods
- Color.prototype[model] = function () {
- if (this.model === model) {
- return new Color(this);
- }
+function onGameStateChange(msg) {
+ var $donationButtons = $('#individual-donate, #group-donate, #public-donate, #ingroup-donate'),
+ $timeElement = $("#time"),
+ $loading = $('.grid-loading'),
+ cur_wall,
+ ego,
+ state,
+ j,
+ k;
- if (arguments.length) {
- return new Color(arguments, model);
- }
+ performance.mark('state_start');
+ if ($loading.is(':visible')) $loading.fadeOut();
- var newAlpha = typeof arguments[channels] === 'number' ? channels : this.valpha;
- return new Color(assertArray(convert[this.model][model].raw(this.color)).concat(newAlpha), model);
- };
-
- // 'static' construction methods
- Color[model] = function (color) {
- if (typeof color === 'number') {
- color = zeroArray(_slice.call(arguments), channels);
- }
- return new Color(color, model);
- };
-});
-
-function roundTo(num, places) {
- return Number(num.toFixed(places));
-}
-
-function roundToPlace(places) {
- return function (num) {
- return roundTo(num, places);
- };
-}
+ if (settings.paused_game) {
+ $timeElement.html(0);
+ return;
+ }
-function getset(model, channel, modifier) {
- model = Array.isArray(model) ? model : [model];
+ // Update remaining time.
+ $timeElement.html(Math.max(Math.round(msg.remaining_time), 0));
- model.forEach(function (m) {
- (limiters[m] || (limiters[m] = []))[channel] = modifier;
- });
+ // Update round.
+ if (settings.num_rounds > 1) {
+ $("#round").html(msg.round + 1);
+ }
- model = model[0];
+ // Update players.
+ state = JSON.parse(msg.grid);
+ players.update(state.players);
+ ego = players.ego();
- return function (val) {
- var result;
+ updateDonationStatus(state.donation_active);
- if (arguments.length) {
- if (modifier) {
- val = modifier(val);
- }
+ // Update items
+ if (state.items !== undefined && state.items !== null) {
+ items = [];
+ for (j = 0; j < state.items.length; j++) {
+ items.push(
+ new Item(
+ state.items[j].id,
+ state.items[j].item_id,
+ state.items[j].position,
+ state.items[j].maturity,
+ )
+ );
+ }
+ }
- result = this[model]();
- result.color[channel] = val;
- return result;
- }
+ // Update walls if they haven't been created yet.
+ if (state.walls !== undefined && walls.length === 0) {
+ for (k = 0; k < state.walls.length; k++) {
+ cur_wall = state.walls[k];
+ if (cur_wall instanceof Array) {
+ cur_wall = {
+ position: cur_wall,
+ color: [0.5, 0.5, 0.5]
+ };
+ }
+ walls.push(
+ new Wall({
+ position: cur_wall.position,
+ color: cur_wall.color
+ })
+ );
+ wall_map[[cur_wall.position[1], cur_wall.position[0]]] = cur_wall.color;
+ }
+ }
- result = this[model]().color[channel];
- if (modifier) {
- result = modifier(result);
- }
+ // If new walls have been added, draw them
+ if (state.walls !== undefined && walls.length < state.walls.length) {
+ for (k = walls.length; k < state.walls.length; k++) {
+ cur_wall = state.walls[k];
+ walls.push(
+ new Wall({
+ position: cur_wall.position,
+ color: cur_wall.color
+ })
+ );
+ wall_map[[cur_wall.position[1], cur_wall.position[0]]] = cur_wall.color;
+ }
+ }
- return result;
- };
+ // Update displayed score, set donation info.
+ if (ego !== undefined) {
+ $("#score").html(Math.round(ego.score));
+ $("#dollars").html(ego.payoff.toFixed(2));
+ window.state = msg.grid;
+ window.ego = ego.id;
+ if (settings.donation_active &&
+ ego.score >= settings.donation_amount &&
+ players.count() > 1
+ ) {
+ $donationButtons.prop('disabled', false);
+ } else {
+ $('#donation-instructions').text('');
+ $donationButtons.prop('disabled', true);
+ }
+ }
}
-function maxfn(max) {
- return function (v) {
- return Math.max(0, Math.min(max, v));
- };
+function addWall(msg) {
+ var wall = msg.wall;
+ if (wall) {
+ walls.push(
+ new Wall({
+ position: wall.position,
+ color: wall.color
+ })
+ );
+ wall_map[[wall.position[1], wall.position[0]]] = wall.color;
+ }
}
-function assertArray(val) {
- return Array.isArray(val) ? val : [val];
+function pushMessage(html) {
+ $("#messages").append(($(" ").html(html)));
+ $("#chatlog").scrollTop($("#chatlog")[0].scrollHeight);
}
-function zeroArray(arr, length) {
- for (var i = 0; i < length; i++) {
- if (typeof arr[i] !== 'number') {
- arr[i] = 0;
- }
- }
+function displayLeaderboards(msg, callback) {
+ if (!settings.leaderboard_group && !settings.leaderboard_individual) {
+ if (callback) {
+ callback();
+ }
+ return;
+ }
+ var i;
+ if (msg.type === 'new_round') {
+ pushMessage("Moderator: the round " + msg.round + ' standings are…');
+ } else {
+ pushMessage("Moderator: the final standings are …");
+ }
+ if (settings.leaderboard_group) {
+ if (settings.leaderboard_individual) {
+ pushMessage('Group ');
+ }
+ var group_scores = players.group_scores();
+ var rgb_map = function (e) { return Math.round(e * 255); };
+ for (i = 0; i < group_scores.length; i++) {
+ var group = group_scores[i];
+ var color = settings.player_colors[name2idx(group.name)].map(rgb_map);
+ pushMessage('' + group.score + ' ');
+ }
+ }
+ if (settings.leaderboard_individual) {
+ if (settings.leaderboard_group) {
+ pushMessage('Individual ');
+ }
+ var player_scores = players.player_scores();
+ var ego_id = players.ego_id;
+ for (i = 0; i < player_scores.length; i++) {
+ var player = player_scores[i];
+ var player_name = chatName(player.id);
+ pushMessage('' + Math.round(player.score) + ' ' + player_name + ' ');
+ }
+ }
+ if (settings.leaderboard_time) {
+ settings.paused_game = true;
+ setTimeout(function () {
+ settings.paused_game = false;
+ if (callback) {
+ callback();
+ }
+ }, 1000 * settings.leaderboard_time);
+ } else if (callback) {
+ callback();
+ }
+}
- return arr;
+function gameOverHandler(player_id) {
+ var callback;
+ if (!isSpectator) {
+ callback = function () {
+ $("#dashboard").hide();
+ $("#instructions").hide();
+ $("#chat").hide();
+ if (player_id) {
+ window.location.href = "/questionnaire?participant_id=" + player_id;
+ }
+ };
+ pixels.canvas.style.display = "none";
+ }
+ return function (msg) {
+ $("#game-over").show();
+ return displayLeaderboards(msg, callback);
+ };
}
-module.exports = Color;
+$(document).ready(function() {
+ var player_id = dallinger.getUrlParameter('participant_id');
+ isSpectator = typeof player_id === 'undefined';
+ var socketSettings = {
+ 'endpoint': 'chat',
+ 'broadcast': CHANNEL,
+ 'control': CONTROL_CHANNEL,
+ 'lagTolerance': 0.001,
+ 'callbackMap': {
+ 'chat': onChatMessage,
+ 'donation_processed': onDonationProcessed,
+ 'color_changed': onColorChanged,
+ 'state': onGameStateChange,
+ 'new_round': displayLeaderboards,
+ 'stop': gameOverHandler(player_id),
+ 'wall_built': addWall,
+ 'move_rejection': onMoveRejected
+ }
+ };
+ var socket = new GUSocket(socketSettings);
+ socket.open().done(function () {
+ var data = {
+ type: 'connect',
+ player_id: isSpectator ? 'spectator' : player_id
+ };
+ socket.send(data);
+ });
-/***/ }),
-/* 10 */
-/***/ (function(module, exports, __webpack_require__) {
+ players.ego_id = player_id;
+ players.startScheduledAutosyncOfEgoPosition();
+ $('#donate label').data('orig-text', $('#donate label').text());
-(function(exports) {
+ setInterval(function () {
+ var delays = [],
+ start_marks = performance.getEntriesByName('state_start', 'mark');
+ for (var i = 0; i < start_marks.length; i++) {
+ if (start_marks.length > i + 2) {
+ delays.push(start_marks[i+1].startTime - start_marks[i].startTime);
+ }
+ }
+ if (delays.length) {
+ var average_delay = delays.reduce(function(sum, value){
+ return sum + value;
+ }, 0) / delays.length;
+ console.log('Average delay between state updates: ' + average_delay + 'ms.');
+ }
+ }, 5000);
- const generateGaussian = __webpack_require__(30);
+ // Append the canvas.
+ $("#grid").append(pixels.canvas);
- // Complementary error function
- // From Numerical Recipes in C 2e p221
- var erfc = function(x) {
- var z = Math.abs(x);
- var t = 1 / (1 + z / 2);
- var r = t * Math.exp(-z * z - 1.26551223 + t * (1.00002368 +
- t * (0.37409196 + t * (0.09678418 + t * (-0.18628806 +
- t * (0.27886807 + t * (-1.13520398 + t * (1.48851587 +
- t * (-0.82215223 + t * 0.17087277)))))))))
- return x >= 0 ? r : 2 - r;
- };
-
- // Inverse complementary error function
- // From Numerical Recipes 3e p265
- var ierfc = function(x) {
- if (x >= 2) { return -100; }
- if (x <= 0) { return 100; }
+ // Opt out of the experiment.
+ $("#opt-out").click(function() {
+ window.location.href = "/questionnaire?participant_id=" + player_id;
+ });
- var xx = (x < 1) ? x : 2 - x;
- var t = Math.sqrt(-2 * Math.log(xx / 2));
+ if (isSpectator) {
+ $(".for-players").hide();
+ }
- var r = -0.70711 * ((2.30753 + t * 0.27061) /
- (1 + t * (0.99229 + t * 0.04481)) - t);
+ // Consent to the experiment.
+ $("#go-to-experiment").click(function() {
+ window.location.href = "/exp";
+ });
- for (var j = 0; j < 2; j++) {
- var err = erfc(r) - xx;
- r += err / (1.12837916709551257 * Math.exp(-(r * r)) - r * err);
- }
+ // Submit the questionnaire.
+ $("#submit-questionnaire").click(function() {
+ dallinger.submitResponses();
+ });
- return (x < 1) ? r : -r;
- };
+ if (settings.show_grid) {
+ pixels.canvas.style.display = "inline";
+ }
- // Models the normal distribution
- var Gaussian = function(mean, variance) {
- if (variance <= 0) {
- throw new Error('Variance must be > 0 (but was ' + variance + ')');
- }
- this.mean = mean;
- this.variance = variance;
- this.standardDeviation = Math.sqrt(variance);
+ if (settings.show_chatroom) {
+ $("#chat form").show();
}
- // Probability density function
- Gaussian.prototype.pdf = function(x) {
- var m = this.standardDeviation * Math.sqrt(2 * Math.PI);
- var e = Math.exp(-Math.pow(x - this.mean, 2) / (2 * this.variance));
- return e / m;
- };
+ var donateToClicked = function() {
+ var w = getWindowPosition(),
+ row = w.top + pixels2cells(mouse[1]),
+ column = w.left + pixels2cells(mouse[0]),
+ recipient = players.nearest(row, column),
+ donor = players.ego(),
+ amt = settings.donation_amount,
+ recipient_id,
+ msg;
- // Cumulative density function
- Gaussian.prototype.cdf = function(x) {
- return 0.5 * erfc(-(x - this.mean) / (this.standardDeviation * Math.sqrt(2)));
- };
+ if (!settings.donation_active) {
+ return;
+ }
- // Percent point function
- Gaussian.prototype.ppf = function(x) {
- return this.mean - this.standardDeviation * Math.sqrt(2) * ierfc(2 * x);
- };
+ if (amt > donor.score) {
+ return;
+ }
- // Product distribution of this and d (scale for constant)
- Gaussian.prototype.mul = function(d) {
- if (typeof(d) === "number") {
- return this.scale(d);
+ if (settings.donation_type === 'individual') {
+ recipient_id = recipient.id;
+ } else if (settings.donation_type === 'group') {
+ recipient_id = 'group:' + name2idx(recipient.color).toString();
+ } else {
+ return;
}
- var precision = 1 / this.variance;
- var dprecision = 1 / d.variance;
- return fromPrecisionMean(
- precision + dprecision,
- precision * this.mean + dprecision * d.mean);
- };
- // Quotient distribution of this and d (scale for constant)
- Gaussian.prototype.div = function(d) {
- if (typeof(d) === "number") {
- return this.scale(1 / d);
+ if (recipient_id !== donor.id) {
+ msg = {
+ type: "donation_submitted",
+ recipient_id: recipient_id,
+ donor_id: donor.id,
+ amount: amt
+ };
+ socket.send(msg);
}
- var precision = 1 / this.variance;
- var dprecision = 1 / d.variance;
- return fromPrecisionMean(
- precision - dprecision,
- precision * this.mean - dprecision * d.mean);
};
- // Addition of this and d
- Gaussian.prototype.add = function(d) {
- return gaussian(this.mean + d.mean, this.variance + d.variance);
+ var donateToAll = function() {
+ var donor = players.ego(),
+ amt = settings.donation_amount,
+ msg;
+
+ msg = {
+ type: "donation_submitted",
+ recipient_id: 'all',
+ donor_id: donor.id,
+ amount: amt
+ };
+ socket.send(msg);
};
- // Subtraction of this and d
- Gaussian.prototype.sub = function(d) {
- return gaussian(this.mean - d.mean, this.variance + d.variance);
+ var donateToInGroup = function () {
+ var donor = players.ego(),
+ amt = settings.donation_amount,
+ recipientId = 'group:' + name2idx(donor.color).toString(),
+ msg;
+
+ msg = {
+ type: "donation_submitted",
+ recipient_id: recipientId,
+ donor_id: donor.id,
+ amount: amt
+ };
+ socket.send(msg);
};
- // Scale this by constant c
- Gaussian.prototype.scale = function(c) {
- return gaussian(this.mean * c, this.variance * c * c);
+ var pixels2cells = function(pix) {
+ return Math.floor(pix / (settings.block_size + settings.padding));
};
+ $("form").submit(function() {
+ var chatmessage = $("#message").val().trim(),
+ msg;
- /**
- * Generate [num] random samples
- * @param {number} num
- * @param randFn - an optional function that returns a float between 0 (inclusive) and 1
- * (exclusive). Use this if you want to pass in a random number generator other than
- * Math.random().
- * @returns {number[]}
- */
- Gaussian.prototype.random = function(num, randFn = null){
- let mean = this.mean;
- let std = this.standardDeviation;
- return Array(num).fill(0).map(() => {
- return generateGaussian(mean,std, randFn)
- })
- };
+ if (! chatmessage) {
+ return false;
+ }
- var gaussian = function(mean, variance) {
- return new Gaussian(mean, variance);
- };
+ try {
+ msg = {
+ type: 'chat',
+ contents: chatmessage,
+ player_id: players.ego().id,
+ timestamp: performance.now() - start,
+ broadcast: true
+ };
+ // send directly to all clients
+ socket.broadcast(msg);
+ // Also send to the server for logging
+ socket.send(msg);
+ } catch(err) {
+ console.error(err);
+ } finally {
+ $("#message").val("");
+ return false;
+ }
+ });
- var fromPrecisionMean = function(precision, precisionmean) {
- return gaussian(precisionmean / precision, 1 / precision);
- };
+ if (!isSpectator) {
+ // Main game keys:
+ bindGameKeys(socket);
+ // Donation click events:
+ $(pixels.canvas).click(function (e) {
+ donateToClicked();
+ });
+ $('#public-donate').click(donateToAll);
+ $('#ingroup-donate').click(donateToInGroup);
+ $('#group-donate').click(function () {
+ if (settings.donation_group) {
+ $('#donate label').text('Click on a color');
+ settings.donation_type = 'group';
+ $(this).prop('disabled', false);
+ $(this).removeClass('button-outline');
+ $('#individual-donate').addClass('button-outline');
+ }
+ });
+ $('#individual-donate').click(function () {
+ if (settings.donation_individual) {
+ $('#donate label').text('Click on a player');
+ settings.donation_type = 'individual';
+ $(this).removeClass('button-outline');
+ $('#group-donate').addClass('button-outline');
+ }
+ });
+ }
- exports(gaussian);
-})
-( true
- ? function(e) { module.exports = e; }
- // istanbul ignore next
- : function(e) { this["gaussian"] = e; });
+});
+}(dallinger, require, window.settings));
/***/ }),
-/* 11 */
+/* 10 */
/***/ (function(module, exports, __webpack_require__) {
-var Emitter = __webpack_require__(37)
-
-module.exports = attach
+/* WEBPACK VAR INJECTION */(function(global, process) {// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
-function attach(element, listener) {
- var position = new Emitter
+var formatRegExp = /%[sdj%]/g;
+exports.format = function(f) {
+ if (!isString(f)) {
+ var objects = [];
+ for (var i = 0; i < arguments.length; i++) {
+ objects.push(inspect(arguments[i]));
+ }
+ return objects.join(' ');
+ }
- position[0] = 0
- position[1] = 0
- position.prev = [0, 0]
- position.flush = flush
- position.dispose = dispose
-
- if (typeof window === 'undefined') {
- return position
+ var i = 1;
+ var args = arguments;
+ var len = args.length;
+ var str = String(f).replace(formatRegExp, function(x) {
+ if (x === '%%') return '%';
+ if (i >= len) return x;
+ switch (x) {
+ case '%s': return String(args[i++]);
+ case '%d': return Number(args[i++]);
+ case '%j':
+ try {
+ return JSON.stringify(args[i++]);
+ } catch (_) {
+ return '[Circular]';
+ }
+ default:
+ return x;
+ }
+ });
+ for (var x = args[i]; i < len; x = args[++i]) {
+ if (isNull(x) || !isObject(x)) {
+ str += ' ' + x;
+ } else {
+ str += ' ' + inspect(x);
+ }
}
+ return str;
+};
- listener = listener || element || window
- element = element || document.body
- var handler = (element === document.body || element === window
- ) ? function(e) {
- position.prev[0] = position[0]
- position.prev[1] = position[1]
- position[0] = e.clientX
- position[1] = e.clientY
- position.emit('move', e)
- }
- : function(e) {
- position.prev[0] = position[0]
- position.prev[1] = position[1]
- var bounds = element.getBoundingClientRect()
- position[0] = e.clientX - bounds.left
- position[1] = e.clientY - bounds.top
- position.emit('move', e)
- }
- listener.addEventListener('mousemove', handler, false)
- return position
+// Mark that a method should not be used.
+// Returns a modified function which warns once by default.
+// If --no-deprecation is set, then it is a no-op.
+exports.deprecate = function(fn, msg) {
+ // Allow for deprecating things in the process of starting up.
+ if (isUndefined(global.process)) {
+ return function() {
+ return exports.deprecate(fn, msg).apply(this, arguments);
+ };
+ }
- function flush() {
- position.prev[0] = position[0]
- position.prev[1] = position[1]
+ if (process.noDeprecation === true) {
+ return fn;
}
- function dispose() {
- position.removeAllListeners('move')
- listener.removeEventListener('mousemove', handler)
+ var warned = false;
+ function deprecated() {
+ if (!warned) {
+ if (process.throwDeprecation) {
+ throw new Error(msg);
+ } else if (process.traceDeprecation) {
+ console.trace(msg);
+ } else {
+ console.error(msg);
+ }
+ warned = true;
+ }
+ return fn.apply(this, arguments);
}
-}
+ return deprecated;
+};
-/***/ }),
-/* 12 */
-/***/ (function(module, exports, __webpack_require__) {
+var debugs = {};
+var debugEnviron;
+exports.debuglog = function(set) {
+ if (isUndefined(debugEnviron))
+ debugEnviron = process.env.NODE_DEBUG || '';
+ set = set.toUpperCase();
+ if (!debugs[set]) {
+ if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
+ var pid = process.pid;
+ debugs[set] = function() {
+ var msg = exports.format.apply(exports, arguments);
+ console.error('%s %d: %s', set, pid, msg);
+ };
+ } else {
+ debugs[set] = function() {};
+ }
+ }
+ return debugs[set];
+};
+
-var __WEBPACK_AMD_DEFINE_RESULT__;/*global define:false */
/**
- * Copyright 2012-2017 Craig Campbell
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Mousetrap is a simple keyboard shortcut library for Javascript with
- * no external dependencies
+ * Echos the value of a value. Trys to print the value out
+ * in the best way possible given the different types.
*
- * @version 1.6.5
- * @url craig.is/killing/mice
+ * @param {Object} obj The object to print out.
+ * @param {Object} opts Optional options object that alters the output.
*/
-(function(window, document, undefined) {
+/* legacy: obj, showHidden, depth, colors*/
+function inspect(obj, opts) {
+ // default options
+ var ctx = {
+ seen: [],
+ stylize: stylizeNoColor
+ };
+ // legacy...
+ if (arguments.length >= 3) ctx.depth = arguments[2];
+ if (arguments.length >= 4) ctx.colors = arguments[3];
+ if (isBoolean(opts)) {
+ // legacy...
+ ctx.showHidden = opts;
+ } else if (opts) {
+ // got an "options" object
+ exports._extend(ctx, opts);
+ }
+ // set default options
+ if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
+ if (isUndefined(ctx.depth)) ctx.depth = 2;
+ if (isUndefined(ctx.colors)) ctx.colors = false;
+ if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
+ if (ctx.colors) ctx.stylize = stylizeWithColor;
+ return formatValue(ctx, obj, ctx.depth);
+}
+exports.inspect = inspect;
- // Check if mousetrap is used inside browser, if not, return
- if (!window) {
- return;
- }
- /**
- * mapping of special keycodes to their corresponding keys
- *
- * everything in this dictionary cannot use keypress events
- * so it has to be here to map to the correct keycodes for
- * keyup/keydown events
- *
- * @type {Object}
- */
- var _MAP = {
- 8: 'backspace',
- 9: 'tab',
- 13: 'enter',
- 16: 'shift',
- 17: 'ctrl',
- 18: 'alt',
- 20: 'capslock',
- 27: 'esc',
- 32: 'space',
- 33: 'pageup',
- 34: 'pagedown',
- 35: 'end',
- 36: 'home',
- 37: 'left',
- 38: 'up',
- 39: 'right',
- 40: 'down',
- 45: 'ins',
- 46: 'del',
- 91: 'meta',
- 93: 'meta',
- 224: 'meta'
- };
+// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
+inspect.colors = {
+ 'bold' : [1, 22],
+ 'italic' : [3, 23],
+ 'underline' : [4, 24],
+ 'inverse' : [7, 27],
+ 'white' : [37, 39],
+ 'grey' : [90, 39],
+ 'black' : [30, 39],
+ 'blue' : [34, 39],
+ 'cyan' : [36, 39],
+ 'green' : [32, 39],
+ 'magenta' : [35, 39],
+ 'red' : [31, 39],
+ 'yellow' : [33, 39]
+};
- /**
- * mapping for special characters so they can support
- *
- * this dictionary is only used incase you want to bind a
- * keyup or keydown event to one of these keys
- *
- * @type {Object}
- */
- var _KEYCODE_MAP = {
- 106: '*',
- 107: '+',
- 109: '-',
- 110: '.',
- 111 : '/',
- 186: ';',
- 187: '=',
- 188: ',',
- 189: '-',
- 190: '.',
- 191: '/',
- 192: '`',
- 219: '[',
- 220: '\\',
- 221: ']',
- 222: '\''
- };
+// Don't use 'blue' not visible on cmd.exe
+inspect.styles = {
+ 'special': 'cyan',
+ 'number': 'yellow',
+ 'boolean': 'yellow',
+ 'undefined': 'grey',
+ 'null': 'bold',
+ 'string': 'green',
+ 'date': 'magenta',
+ // "name": intentionally not styling
+ 'regexp': 'red'
+};
- /**
- * this is a mapping of keys that require shift on a US keypad
- * back to the non shift equivelents
- *
- * this is so you can use keyup events with these keys
- *
- * note that this will only work reliably on US keyboards
- *
- * @type {Object}
- */
- var _SHIFT_MAP = {
- '~': '`',
- '!': '1',
- '@': '2',
- '#': '3',
- '$': '4',
- '%': '5',
- '^': '6',
- '&': '7',
- '*': '8',
- '(': '9',
- ')': '0',
- '_': '-',
- '+': '=',
- ':': ';',
- '\"': '\'',
- '<': ',',
- '>': '.',
- '?': '/',
- '|': '\\'
- };
- /**
- * this is a list of special strings you can use to map
- * to modifier keys when you specify your keyboard shortcuts
- *
- * @type {Object}
- */
- var _SPECIAL_ALIASES = {
- 'option': 'alt',
- 'command': 'meta',
- 'return': 'enter',
- 'escape': 'esc',
- 'plus': '+',
- 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
- };
+function stylizeWithColor(str, styleType) {
+ var style = inspect.styles[styleType];
- /**
- * variable to store the flipped version of _MAP from above
- * needed to check if we should use keypress or not when no action
- * is specified
- *
- * @type {Object|undefined}
- */
- var _REVERSE_MAP;
+ if (style) {
+ return '\u001b[' + inspect.colors[style][0] + 'm' + str +
+ '\u001b[' + inspect.colors[style][1] + 'm';
+ } else {
+ return str;
+ }
+}
- /**
- * loop through the f keys, f1 to f19 and add them to the map
- * programatically
- */
- for (var i = 1; i < 20; ++i) {
- _MAP[111 + i] = 'f' + i;
- }
- /**
- * loop through to map numbers on the numeric keypad
- */
- for (i = 0; i <= 9; ++i) {
+function stylizeNoColor(str, styleType) {
+ return str;
+}
- // This needs to use a string cause otherwise since 0 is falsey
- // mousetrap will never fire for numpad 0 pressed as part of a keydown
- // event.
- //
- // @see https://github.com/ccampbell/mousetrap/pull/258
- _MAP[i + 96] = i.toString();
- }
- /**
- * cross browser add event method
- *
- * @param {Element|HTMLDocument} object
- * @param {string} type
- * @param {Function} callback
- * @returns void
- */
- function _addEvent(object, type, callback) {
- if (object.addEventListener) {
- object.addEventListener(type, callback, false);
- return;
- }
+function arrayToHash(array) {
+ var hash = {};
- object.attachEvent('on' + type, callback);
- }
+ array.forEach(function(val, idx) {
+ hash[val] = true;
+ });
- /**
- * takes the event and returns the key character
- *
- * @param {Event} e
- * @return {string}
- */
- function _characterFromEvent(e) {
+ return hash;
+}
- // for keypress events we should return the character as is
- if (e.type == 'keypress') {
- var character = String.fromCharCode(e.which);
- // if the shift key is not pressed then it is safe to assume
- // that we want the character to be lowercase. this means if
- // you accidentally have caps lock on then your key bindings
- // will continue to work
- //
- // the only side effect that might not be desired is if you
- // bind something like 'A' cause you want to trigger an
- // event when capital A is pressed caps lock will no longer
- // trigger the event. shift+a will though.
- if (!e.shiftKey) {
- character = character.toLowerCase();
- }
+function formatValue(ctx, value, recurseTimes) {
+ // Provide a hook for user-specified inspect functions.
+ // Check that value is an object with an inspect function on it
+ if (ctx.customInspect &&
+ value &&
+ isFunction(value.inspect) &&
+ // Filter out the util module, it's inspect function is special
+ value.inspect !== exports.inspect &&
+ // Also filter out any prototype objects using the circular check.
+ !(value.constructor && value.constructor.prototype === value)) {
+ var ret = value.inspect(recurseTimes, ctx);
+ if (!isString(ret)) {
+ ret = formatValue(ctx, ret, recurseTimes);
+ }
+ return ret;
+ }
- return character;
- }
+ // Primitive types cannot have properties
+ var primitive = formatPrimitive(ctx, value);
+ if (primitive) {
+ return primitive;
+ }
- // for non keypress events the special maps are needed
- if (_MAP[e.which]) {
- return _MAP[e.which];
- }
+ // Look up the keys of the object.
+ var keys = Object.keys(value);
+ var visibleKeys = arrayToHash(keys);
- if (_KEYCODE_MAP[e.which]) {
- return _KEYCODE_MAP[e.which];
- }
+ if (ctx.showHidden) {
+ keys = Object.getOwnPropertyNames(value);
+ }
- // if it is not in the special map
+ // IE doesn't make error fields non-enumerable
+ // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
+ if (isError(value)
+ && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
+ return formatError(value);
+ }
- // with keydown and keyup events the character seems to always
- // come in as an uppercase character whether you are pressing shift
- // or not. we should make sure it is always lowercase for comparisons
- return String.fromCharCode(e.which).toLowerCase();
+ // Some type of object without properties can be shortcutted.
+ if (keys.length === 0) {
+ if (isFunction(value)) {
+ var name = value.name ? ': ' + value.name : '';
+ return ctx.stylize('[Function' + name + ']', 'special');
}
-
- /**
- * checks if two arrays are equal
- *
- * @param {Array} modifiers1
- * @param {Array} modifiers2
- * @returns {boolean}
- */
- function _modifiersMatch(modifiers1, modifiers2) {
- return modifiers1.sort().join(',') === modifiers2.sort().join(',');
+ if (isRegExp(value)) {
+ return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+ }
+ if (isDate(value)) {
+ return ctx.stylize(Date.prototype.toString.call(value), 'date');
+ }
+ if (isError(value)) {
+ return formatError(value);
}
+ }
- /**
- * takes a key event and figures out what the modifiers are
- *
- * @param {Event} e
- * @returns {Array}
- */
- function _eventModifiers(e) {
- var modifiers = [];
+ var base = '', array = false, braces = ['{', '}'];
- if (e.shiftKey) {
- modifiers.push('shift');
- }
+ // Make Array say that they are Array
+ if (isArray(value)) {
+ array = true;
+ braces = ['[', ']'];
+ }
- if (e.altKey) {
- modifiers.push('alt');
- }
+ // Make functions say that they are functions
+ if (isFunction(value)) {
+ var n = value.name ? ': ' + value.name : '';
+ base = ' [Function' + n + ']';
+ }
- if (e.ctrlKey) {
- modifiers.push('ctrl');
- }
+ // Make RegExps say that they are RegExps
+ if (isRegExp(value)) {
+ base = ' ' + RegExp.prototype.toString.call(value);
+ }
- if (e.metaKey) {
- modifiers.push('meta');
- }
+ // Make dates with properties first say the date
+ if (isDate(value)) {
+ base = ' ' + Date.prototype.toUTCString.call(value);
+ }
- return modifiers;
- }
+ // Make error with message first say the error
+ if (isError(value)) {
+ base = ' ' + formatError(value);
+ }
- /**
- * prevents default for this event
- *
- * @param {Event} e
- * @returns void
- */
- function _preventDefault(e) {
- if (e.preventDefault) {
- e.preventDefault();
- return;
- }
+ if (keys.length === 0 && (!array || value.length == 0)) {
+ return braces[0] + base + braces[1];
+ }
- e.returnValue = false;
+ if (recurseTimes < 0) {
+ if (isRegExp(value)) {
+ return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
+ } else {
+ return ctx.stylize('[Object]', 'special');
}
+ }
- /**
- * stops propogation for this event
- *
- * @param {Event} e
- * @returns void
- */
- function _stopPropagation(e) {
- if (e.stopPropagation) {
- e.stopPropagation();
- return;
- }
+ ctx.seen.push(value);
- e.cancelBubble = true;
- }
+ var output;
+ if (array) {
+ output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
+ } else {
+ output = keys.map(function(key) {
+ return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
+ });
+ }
- /**
- * determines if the keycode specified is a modifier key or not
- *
- * @param {string} key
- * @returns {boolean}
- */
- function _isModifier(key) {
- return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
- }
+ ctx.seen.pop();
- /**
- * reverses the map lookup so that we can look for specific keys
- * to see what can and can't use keypress
- *
- * @return {Object}
- */
- function _getReverseMap() {
- if (!_REVERSE_MAP) {
- _REVERSE_MAP = {};
- for (var key in _MAP) {
+ return reduceToSingleString(output, base, braces);
+}
- // pull out the numeric keypad from here cause keypress should
- // be able to detect the keys from the character
- if (key > 95 && key < 112) {
- continue;
- }
- if (_MAP.hasOwnProperty(key)) {
- _REVERSE_MAP[_MAP[key]] = key;
- }
- }
- }
- return _REVERSE_MAP;
- }
+function formatPrimitive(ctx, value) {
+ if (isUndefined(value))
+ return ctx.stylize('undefined', 'undefined');
+ if (isString(value)) {
+ var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
+ .replace(/'/g, "\\'")
+ .replace(/\\"/g, '"') + '\'';
+ return ctx.stylize(simple, 'string');
+ }
+ if (isNumber(value))
+ return ctx.stylize('' + value, 'number');
+ if (isBoolean(value))
+ return ctx.stylize('' + value, 'boolean');
+ // For some reason typeof null is "object", so special case here.
+ if (isNull(value))
+ return ctx.stylize('null', 'null');
+}
- /**
- * picks the best action based on the key combination
- *
- * @param {string} key - character for key
- * @param {Array} modifiers
- * @param {string=} action passed in
- */
- function _pickBestAction(key, modifiers, action) {
- // if no action was picked in we should try to pick the one
- // that we think would work best for this key
- if (!action) {
- action = _getReverseMap()[key] ? 'keydown' : 'keypress';
- }
+function formatError(value) {
+ return '[' + Error.prototype.toString.call(value) + ']';
+}
- // modifier keys don't work as expected with keypress,
- // switch to keydown
- if (action == 'keypress' && modifiers.length) {
- action = 'keydown';
- }
- return action;
+function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
+ var output = [];
+ for (var i = 0, l = value.length; i < l; ++i) {
+ if (hasOwnProperty(value, String(i))) {
+ output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+ String(i), true));
+ } else {
+ output.push('');
+ }
+ }
+ keys.forEach(function(key) {
+ if (!key.match(/^\d+$/)) {
+ output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
+ key, true));
}
+ });
+ return output;
+}
- /**
- * Converts from a string key combination to an array
- *
- * @param {string} combination like "command+shift+l"
- * @return {Array}
- */
- function _keysFromString(combination) {
- if (combination === '+') {
- return ['+'];
- }
- combination = combination.replace(/\+{2}/g, '+plus');
- return combination.split('+');
+function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
+ var name, str, desc;
+ desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
+ if (desc.get) {
+ if (desc.set) {
+ str = ctx.stylize('[Getter/Setter]', 'special');
+ } else {
+ str = ctx.stylize('[Getter]', 'special');
+ }
+ } else {
+ if (desc.set) {
+ str = ctx.stylize('[Setter]', 'special');
}
+ }
+ if (!hasOwnProperty(visibleKeys, key)) {
+ name = '[' + key + ']';
+ }
+ if (!str) {
+ if (ctx.seen.indexOf(desc.value) < 0) {
+ if (isNull(recurseTimes)) {
+ str = formatValue(ctx, desc.value, null);
+ } else {
+ str = formatValue(ctx, desc.value, recurseTimes - 1);
+ }
+ if (str.indexOf('\n') > -1) {
+ if (array) {
+ str = str.split('\n').map(function(line) {
+ return ' ' + line;
+ }).join('\n').substr(2);
+ } else {
+ str = '\n' + str.split('\n').map(function(line) {
+ return ' ' + line;
+ }).join('\n');
+ }
+ }
+ } else {
+ str = ctx.stylize('[Circular]', 'special');
+ }
+ }
+ if (isUndefined(name)) {
+ if (array && key.match(/^\d+$/)) {
+ return str;
+ }
+ name = JSON.stringify('' + key);
+ if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
+ name = name.substr(1, name.length - 2);
+ name = ctx.stylize(name, 'name');
+ } else {
+ name = name.replace(/'/g, "\\'")
+ .replace(/\\"/g, '"')
+ .replace(/(^"|"$)/g, "'");
+ name = ctx.stylize(name, 'string');
+ }
+ }
- /**
- * Gets info for a specific key combination
- *
- * @param {string} combination key combination ("command+s" or "a" or "*")
- * @param {string=} action
- * @returns {Object}
- */
- function _getKeyInfo(combination, action) {
- var keys;
- var key;
- var i;
- var modifiers = [];
+ return name + ': ' + str;
+}
- // take the keys from this pattern and figure out what the actual
- // pattern is all about
- keys = _keysFromString(combination);
- for (i = 0; i < keys.length; ++i) {
- key = keys[i];
+function reduceToSingleString(output, base, braces) {
+ var numLinesEst = 0;
+ var length = output.reduce(function(prev, cur) {
+ numLinesEst++;
+ if (cur.indexOf('\n') >= 0) numLinesEst++;
+ return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
+ }, 0);
- // normalize key names
- if (_SPECIAL_ALIASES[key]) {
- key = _SPECIAL_ALIASES[key];
- }
+ if (length > 60) {
+ return braces[0] +
+ (base === '' ? '' : base + '\n ') +
+ ' ' +
+ output.join(',\n ') +
+ ' ' +
+ braces[1];
+ }
- // if this is not a keypress event then we should
- // be smart about using shift keys
- // this will only work for US keyboards however
- if (action && action != 'keypress' && _SHIFT_MAP[key]) {
- key = _SHIFT_MAP[key];
- modifiers.push('shift');
- }
+ return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+}
- // if this key is a modifier then add it to the list of modifiers
- if (_isModifier(key)) {
- modifiers.push(key);
- }
- }
- // depending on what the key combination is
- // we will try to pick the best event for it
- action = _pickBestAction(key, modifiers, action);
+// NOTE: These type checking functions intentionally don't use `instanceof`
+// because it is fragile and can be easily faked with `Object.create()`.
+function isArray(ar) {
+ return Array.isArray(ar);
+}
+exports.isArray = isArray;
- return {
- key: key,
- modifiers: modifiers,
- action: action
- };
- }
+function isBoolean(arg) {
+ return typeof arg === 'boolean';
+}
+exports.isBoolean = isBoolean;
- function _belongsTo(element, ancestor) {
- if (element === null || element === document) {
- return false;
- }
+function isNull(arg) {
+ return arg === null;
+}
+exports.isNull = isNull;
- if (element === ancestor) {
- return true;
- }
+function isNullOrUndefined(arg) {
+ return arg == null;
+}
+exports.isNullOrUndefined = isNullOrUndefined;
- return _belongsTo(element.parentNode, ancestor);
- }
+function isNumber(arg) {
+ return typeof arg === 'number';
+}
+exports.isNumber = isNumber;
- function Mousetrap(targetElement) {
- var self = this;
+function isString(arg) {
+ return typeof arg === 'string';
+}
+exports.isString = isString;
- targetElement = targetElement || document;
+function isSymbol(arg) {
+ return typeof arg === 'symbol';
+}
+exports.isSymbol = isSymbol;
- if (!(self instanceof Mousetrap)) {
- return new Mousetrap(targetElement);
- }
+function isUndefined(arg) {
+ return arg === void 0;
+}
+exports.isUndefined = isUndefined;
- /**
- * element to attach key events to
- *
- * @type {Element}
- */
- self.target = targetElement;
+function isRegExp(re) {
+ return isObject(re) && objectToString(re) === '[object RegExp]';
+}
+exports.isRegExp = isRegExp;
- /**
- * a list of all the callbacks setup via Mousetrap.bind()
- *
- * @type {Object}
- */
- self._callbacks = {};
+function isObject(arg) {
+ return typeof arg === 'object' && arg !== null;
+}
+exports.isObject = isObject;
- /**
- * direct map of string combinations to callbacks used for trigger()
- *
- * @type {Object}
- */
- self._directMap = {};
+function isDate(d) {
+ return isObject(d) && objectToString(d) === '[object Date]';
+}
+exports.isDate = isDate;
- /**
- * keeps track of what level each sequence is at since multiple
- * sequences can start out with the same sequence
- *
- * @type {Object}
- */
- var _sequenceLevels = {};
+function isError(e) {
+ return isObject(e) &&
+ (objectToString(e) === '[object Error]' || e instanceof Error);
+}
+exports.isError = isError;
- /**
- * variable to store the setTimeout call
- *
- * @type {null|number}
- */
- var _resetTimer;
+function isFunction(arg) {
+ return typeof arg === 'function';
+}
+exports.isFunction = isFunction;
- /**
- * temporary state where we will ignore the next keyup
- *
- * @type {boolean|string}
- */
- var _ignoreNextKeyup = false;
+function isPrimitive(arg) {
+ return arg === null ||
+ typeof arg === 'boolean' ||
+ typeof arg === 'number' ||
+ typeof arg === 'string' ||
+ typeof arg === 'symbol' || // ES6 symbol
+ typeof arg === 'undefined';
+}
+exports.isPrimitive = isPrimitive;
- /**
- * temporary state where we will ignore the next keypress
- *
- * @type {boolean}
- */
- var _ignoreNextKeypress = false;
+exports.isBuffer = __webpack_require__(12);
- /**
- * are we currently inside of a sequence?
- * type of action ("keyup" or "keydown" or "keypress") or false
- *
- * @type {boolean|string}
- */
- var _nextExpectedAction = false;
+function objectToString(o) {
+ return Object.prototype.toString.call(o);
+}
- /**
- * resets all sequence counters except for the ones passed in
- *
- * @param {Object} doNotReset
- * @returns void
- */
- function _resetSequences(doNotReset) {
- doNotReset = doNotReset || {};
- var activeSequences = false,
- key;
+function pad(n) {
+ return n < 10 ? '0' + n.toString(10) : n.toString(10);
+}
- for (key in _sequenceLevels) {
- if (doNotReset[key]) {
- activeSequences = true;
- continue;
- }
- _sequenceLevels[key] = 0;
- }
- if (!activeSequences) {
- _nextExpectedAction = false;
- }
- }
+var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
+ 'Oct', 'Nov', 'Dec'];
- /**
- * finds all callbacks that match based on the keycode, modifiers,
- * and action
- *
- * @param {string} character
- * @param {Array} modifiers
- * @param {Event|Object} e
- * @param {string=} sequenceName - name of the sequence we are looking for
- * @param {string=} combination
- * @param {number=} level
- * @returns {Array}
- */
- function _getMatches(character, modifiers, e, sequenceName, combination, level) {
- var i;
- var callback;
- var matches = [];
- var action = e.type;
+// 26 Feb 16:19:34
+function timestamp() {
+ var d = new Date();
+ var time = [pad(d.getHours()),
+ pad(d.getMinutes()),
+ pad(d.getSeconds())].join(':');
+ return [d.getDate(), months[d.getMonth()], time].join(' ');
+}
- // if there are no events related to this keycode
- if (!self._callbacks[character]) {
- return [];
- }
- // if a modifier key is coming up on its own we should allow it
- if (action == 'keyup' && _isModifier(character)) {
- modifiers = [character];
- }
+// log is just a thin wrapper to console.log that prepends a timestamp
+exports.log = function() {
+ console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
+};
- // loop through all callbacks for the key that was pressed
- // and see if any of them match
- for (i = 0; i < self._callbacks[character].length; ++i) {
- callback = self._callbacks[character][i];
- // if a sequence name is not specified, but this is a sequence at
- // the wrong level then move onto the next match
- if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
- continue;
- }
+/**
+ * Inherit the prototype methods from one constructor into another.
+ *
+ * The Function.prototype.inherits from lang.js rewritten as a standalone
+ * function (not on Function.prototype). NOTE: If this file is to be loaded
+ * during bootstrapping this function needs to be rewritten using some native
+ * functions as prototype setup using normal JavaScript does not work as
+ * expected during bootstrapping (see mirror.js in r114903).
+ *
+ * @param {function} ctor Constructor function which needs to inherit the
+ * prototype.
+ * @param {function} superCtor Constructor function to inherit prototype from.
+ */
+exports.inherits = __webpack_require__(13);
- // if the action we are looking for doesn't match the action we got
- // then we should keep going
- if (action != callback.action) {
- continue;
- }
+exports._extend = function(origin, add) {
+ // Don't do anything if add isn't an object
+ if (!add || !isObject(add)) return origin;
- // if this is a keypress event and the meta key and control key
- // are not pressed that means that we need to only look at the
- // character, otherwise check the modifiers as well
- //
- // chrome will not fire a keypress if meta or control is down
- // safari will fire a keypress if meta or meta+shift is down
- // firefox will fire a keypress if meta or control is down
- if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {
+ var keys = Object.keys(add);
+ var i = keys.length;
+ while (i--) {
+ origin[keys[i]] = add[keys[i]];
+ }
+ return origin;
+};
- // when you bind a combination or sequence a second time it
- // should overwrite the first one. if a sequenceName or
- // combination is specified in this call it does just that
- //
- // @todo make deleting its own method?
- var deleteCombo = !sequenceName && callback.combo == combination;
- var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
- if (deleteCombo || deleteSequence) {
- self._callbacks[character].splice(i, 1);
- }
+function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+}
- matches.push(callback);
- }
- }
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(1), __webpack_require__(11)))
- return matches;
- }
+/***/ }),
+/* 11 */
+/***/ (function(module, exports) {
- /**
- * actually calls the callback function
- *
- * if your callback function returns false this will use the jquery
- * convention - prevent default and stop propogation on the event
- *
- * @param {Function} callback
- * @param {Event} e
- * @returns void
- */
- function _fireCallback(callback, e, combo, sequence) {
+// shim for using process in browser
+var process = module.exports = {};
- // if this event should not happen stop here
- if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
- return;
- }
+// cached from whatever global is present so that test runners that stub it
+// don't break things. But we need to wrap it in a try catch in case it is
+// wrapped in strict mode code which doesn't define any globals. It's inside a
+// function because try/catches deoptimize in certain engines.
- if (callback(e, combo) === false) {
- _preventDefault(e);
- _stopPropagation(e);
- }
+var cachedSetTimeout;
+var cachedClearTimeout;
+
+function defaultSetTimout() {
+ throw new Error('setTimeout has not been defined');
+}
+function defaultClearTimeout () {
+ throw new Error('clearTimeout has not been defined');
+}
+(function () {
+ try {
+ if (typeof setTimeout === 'function') {
+ cachedSetTimeout = setTimeout;
+ } else {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ } catch (e) {
+ cachedSetTimeout = defaultSetTimout;
+ }
+ try {
+ if (typeof clearTimeout === 'function') {
+ cachedClearTimeout = clearTimeout;
+ } else {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+ } catch (e) {
+ cachedClearTimeout = defaultClearTimeout;
+ }
+} ())
+function runTimeout(fun) {
+ if (cachedSetTimeout === setTimeout) {
+ //normal enviroments in sane situations
+ return setTimeout(fun, 0);
+ }
+ // if setTimeout wasn't available but was latter defined
+ if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
+ cachedSetTimeout = setTimeout;
+ return setTimeout(fun, 0);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedSetTimeout(fun, 0);
+ } catch(e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedSetTimeout.call(null, fun, 0);
+ } catch(e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error
+ return cachedSetTimeout.call(this, fun, 0);
}
+ }
- /**
- * handles a character key event
- *
- * @param {string} character
- * @param {Array} modifiers
- * @param {Event} e
- * @returns void
- */
- self._handleKey = function(character, modifiers, e) {
- var callbacks = _getMatches(character, modifiers, e);
- var i;
- var doNotReset = {};
- var maxLevel = 0;
- var processedSequenceCallback = false;
- // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
- for (i = 0; i < callbacks.length; ++i) {
- if (callbacks[i].seq) {
- maxLevel = Math.max(maxLevel, callbacks[i].level);
- }
- }
+}
+function runClearTimeout(marker) {
+ if (cachedClearTimeout === clearTimeout) {
+ //normal enviroments in sane situations
+ return clearTimeout(marker);
+ }
+ // if clearTimeout wasn't available but was latter defined
+ if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
+ cachedClearTimeout = clearTimeout;
+ return clearTimeout(marker);
+ }
+ try {
+ // when when somebody has screwed with setTimeout but no I.E. maddness
+ return cachedClearTimeout(marker);
+ } catch (e){
+ try {
+ // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
+ return cachedClearTimeout.call(null, marker);
+ } catch (e){
+ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.
+ // Some versions of I.E. have different rules for clearTimeout vs setTimeout
+ return cachedClearTimeout.call(this, marker);
+ }
+ }
- // loop through matching callbacks for this key event
- for (i = 0; i < callbacks.length; ++i) {
- // fire for all sequence callbacks
- // this is because if for example you have multiple sequences
- // bound such as "g i" and "g t" they both need to fire the
- // callback for matching g cause otherwise you can only ever
- // match the first one
- if (callbacks[i].seq) {
- // only fire callbacks for the maxLevel to prevent
- // subsequences from also firing
- //
- // for example 'a option b' should not cause 'option b' to fire
- // even though 'option b' is part of the other sequence
- //
- // any sequences that do not match here will be discarded
- // below by the _resetSequences call
- if (callbacks[i].level != maxLevel) {
- continue;
- }
+}
+var queue = [];
+var draining = false;
+var currentQueue;
+var queueIndex = -1;
- processedSequenceCallback = true;
+function cleanUpNextTick() {
+ if (!draining || !currentQueue) {
+ return;
+ }
+ draining = false;
+ if (currentQueue.length) {
+ queue = currentQueue.concat(queue);
+ } else {
+ queueIndex = -1;
+ }
+ if (queue.length) {
+ drainQueue();
+ }
+}
- // keep a list of which sequences were matches for later
- doNotReset[callbacks[i].seq] = 1;
- _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
- continue;
- }
+function drainQueue() {
+ if (draining) {
+ return;
+ }
+ var timeout = runTimeout(cleanUpNextTick);
+ draining = true;
- // if there were no sequence matches but we are still here
- // that means this is a regular match so we should fire that
- if (!processedSequenceCallback) {
- _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
- }
+ var len = queue.length;
+ while(len) {
+ currentQueue = queue;
+ queue = [];
+ while (++queueIndex < len) {
+ if (currentQueue) {
+ currentQueue[queueIndex].run();
}
+ }
+ queueIndex = -1;
+ len = queue.length;
+ }
+ currentQueue = null;
+ draining = false;
+ runClearTimeout(timeout);
+}
- // if the key you pressed matches the type of sequence without
- // being a modifier (ie "keyup" or "keypress") then we should
- // reset all sequences that were not matched by this event
- //
- // this is so, for example, if you have the sequence "h a t" and you
- // type "h e a r t" it does not match. in this case the "e" will
- // cause the sequence to reset
- //
- // modifier keys are ignored because you can have a sequence
- // that contains modifiers such as "enter ctrl+space" and in most
- // cases the modifier key will be pressed before the next key
- //
- // also if you have a sequence such as "ctrl+b a" then pressing the
- // "b" key will trigger a "keypress" and a "keydown"
- //
- // the "keydown" is expected when there is a modifier, but the
- // "keypress" ends up matching the _nextExpectedAction since it occurs
- // after and that causes the sequence to reset
- //
- // we ignore keypresses in a sequence that directly follow a keydown
- // for the same character
- var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
- if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
- _resetSequences(doNotReset);
- }
+process.nextTick = function (fun) {
+ var args = new Array(arguments.length - 1);
+ if (arguments.length > 1) {
+ for (var i = 1; i < arguments.length; i++) {
+ args[i - 1] = arguments[i];
+ }
+ }
+ queue.push(new Item(fun, args));
+ if (queue.length === 1 && !draining) {
+ runTimeout(drainQueue);
+ }
+};
- _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
- };
+// v8 likes predictible objects
+function Item(fun, array) {
+ this.fun = fun;
+ this.array = array;
+}
+Item.prototype.run = function () {
+ this.fun.apply(null, this.array);
+};
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+process.version = ''; // empty string to avoid regexp issues
+process.versions = {};
- /**
- * handles a keydown event
- *
- * @param {Event} e
- * @returns void
- */
- function _handleKeyEvent(e) {
+function noop() {}
- // normalize e.which for key events
- // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
- if (typeof e.which !== 'number') {
- e.which = e.keyCode;
- }
+process.on = noop;
+process.addListener = noop;
+process.once = noop;
+process.off = noop;
+process.removeListener = noop;
+process.removeAllListeners = noop;
+process.emit = noop;
- var character = _characterFromEvent(e);
+process.binding = function (name) {
+ throw new Error('process.binding is not supported');
+};
- // no character found then stop
- if (!character) {
- return;
- }
+process.cwd = function () { return '/' };
+process.chdir = function (dir) {
+ throw new Error('process.chdir is not supported');
+};
+process.umask = function() { return 0; };
- // need to use === for the character check because the character can be 0
- if (e.type == 'keyup' && _ignoreNextKeyup === character) {
- _ignoreNextKeyup = false;
- return;
- }
-
- self.handleKey(character, _eventModifiers(e), e);
- }
-
- /**
- * called to set a 1 second timeout on the specified sequence
- *
- * this is so after each key press in the sequence you have 1 second
- * to press the next key before you have to start over
- *
- * @returns void
- */
- function _resetSequenceTimer() {
- clearTimeout(_resetTimer);
- _resetTimer = setTimeout(_resetSequences, 1000);
- }
- /**
- * binds a key sequence to an event
- *
- * @param {string} combo - combo specified in bind call
- * @param {Array} keys
- * @param {Function} callback
- * @param {string=} action
- * @returns void
- */
- function _bindSequence(combo, keys, callback, action) {
+/***/ }),
+/* 12 */
+/***/ (function(module, exports) {
- // start off by adding a sequence level record for this combination
- // and setting the level to 0
- _sequenceLevels[combo] = 0;
+module.exports = function isBuffer(arg) {
+ return arg && typeof arg === 'object'
+ && typeof arg.copy === 'function'
+ && typeof arg.fill === 'function'
+ && typeof arg.readUInt8 === 'function';
+}
- /**
- * callback to increase the sequence level for this sequence and reset
- * all other sequences that were active
- *
- * @param {string} nextAction
- * @returns {Function}
- */
- function _increaseSequence(nextAction) {
- return function() {
- _nextExpectedAction = nextAction;
- ++_sequenceLevels[combo];
- _resetSequenceTimer();
- };
- }
+/***/ }),
+/* 13 */
+/***/ (function(module, exports) {
- /**
- * wraps the specified callback inside of another function in order
- * to reset all sequence counters as soon as this sequence is done
- *
- * @param {Event} e
- * @returns void
- */
- function _callbackAndReset(e) {
- _fireCallback(callback, e, combo);
+if (typeof Object.create === 'function') {
+ // implementation from standard node.js 'util' module
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ ctor.prototype = Object.create(superCtor.prototype, {
+ constructor: {
+ value: ctor,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ };
+} else {
+ // old school shim for old browsers
+ module.exports = function inherits(ctor, superCtor) {
+ ctor.super_ = superCtor
+ var TempCtor = function () {}
+ TempCtor.prototype = superCtor.prototype
+ ctor.prototype = new TempCtor()
+ ctor.prototype.constructor = ctor
+ }
+}
- // we should ignore the next key up if the action is key down
- // or keypress. this is so if you finish a sequence and
- // release the key the final key will not trigger a keyup
- if (action !== 'keyup') {
- _ignoreNextKeyup = _characterFromEvent(e);
- }
- // weird race condition if a sequence ends with the key
- // another sequence begins with
- setTimeout(_resetSequences, 10);
- }
+/***/ }),
+/* 14 */
+/***/ (function(module, exports, __webpack_require__) {
- // loop through keys one at a time and bind the appropriate callback
- // function. for any key leading up to the final one it should
- // increase the sequence. after the final, it should reset all sequences
- //
- // if an action is specified in the original bind call then that will
- // be used throughout. otherwise we will pass the action that the
- // next key in the sequence should match. this allows a sequence
- // to mix and match keypress and keydown events depending on which
- // ones are better suited to the key provided
- for (var i = 0; i < keys.length; ++i) {
- var isFinal = i + 1 === keys.length;
- var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
- _bindSingle(keys[i], wrappedCallback, action, combo, i);
- }
- }
+var parse = __webpack_require__(2);
+var isnumber = __webpack_require__(3);
+var isstring = __webpack_require__(4);
+var isarray = __webpack_require__(5);
+var convert = __webpack_require__(19);
+var layout = __webpack_require__(22);
+var texcoord = __webpack_require__(23);
+var range = __webpack_require__(24);
+var pixdenticon = __webpack_require__(25);
+var md5 = __webpack_require__(6);
- /**
- * binds a single keyboard combination
- *
- * @param {string} combination
- * @param {Function} callback
- * @param {string=} action
- * @param {string=} sequenceName - name of sequence if part of sequence
- * @param {number=} level - what part of the sequence the command is
- * @returns void
- */
- function _bindSingle(combination, callback, action, sequenceName, level) {
+function Pixels(data, textures, opts) {
+ if (!(this instanceof Pixels)) return new Pixels(data, textures, opts);
+ var self = this;
+ opts = opts || {};
+ this.opts = opts;
+ var num_identicons = 100;
- // store a direct mapped reference for use with Mousetrap.trigger
- self._directMap[combination + ':' + action] = callback;
+ opts.background = opts.background || [ 0.5, 0.5, 0.5 ];
+ opts.size = isnumber(opts.size) ? opts.size : 10;
+ opts.padding = isnumber(opts.padding) ? opts.padding : 2;
- // make sure multiple spaces in a row become a single space
- combination = combination.replace(/\s+/g, ' ');
+ if (isstring(opts.background))
+ opts.background = parse(opts.background).rgb.map(function(c) {
+ return c / 255;
+ });
- var sequence = combination.split(' ');
- var info;
+ if (isarray(data[0]) && data[0].length !== 3) {
+ opts.rows = data.length;
+ opts.columns = data[0].length;
+ }
- // if this pattern is a sequence of keys then run through this method
- // to reprocess each pattern one key at a time
- if (sequence.length > 1) {
- _bindSequence(combination, sequence, callback, action);
- return;
- }
+ if (!opts.rows || !opts.columns) {
+ opts.rows = opts.columns = Math.round(Math.sqrt(data.length));
+ }
- info = _getKeyInfo(combination, action);
+ var width = opts.columns * opts.size + (opts.columns + 1) * opts.padding;
+ var height = opts.rows * opts.size + (opts.rows + 1) * opts.padding;
- // make sure to initialize array if this is the first time
- // a callback is added for this key
- self._callbacks[info.key] = self._callbacks[info.key] || [];
+ var canvas = document.createElement("canvas");
+ canvas.width = width;
+ canvas.height = height;
+ if (opts.root) opts.root.appendChild(canvas);
- // remove an existing match if there is one
- _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);
+ var colors = opts.formatted ? data : convert(data);
+ var texcoords = texcoord(
+ opts.rows,
+ opts.columns,
+ textures,
+ num_identicons
+ );
- // add this call back to the array
- // if it is a sequence put it at the beginning
- // if not put it at the end
- //
- // this is important because the way these are processed expects
- // the sequence ones to come first
- self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({
- callback: callback,
- modifiers: info.modifiers,
- action: info.action,
- seq: sequenceName,
- level: level,
- combo: combination
- });
- }
+ var positions = layout(
+ opts.rows,
+ opts.columns,
+ 2 * opts.padding / width,
+ 2 * opts.size / width,
+ width / height
+ );
- /**
- * binds multiple combinations to the same callback
- *
- * @param {Array} combinations
- * @param {Function} callback
- * @param {string|undefined} action
- * @returns void
- */
- self._bindMultiple = function(combinations, callback, action) {
- for (var i = 0; i < combinations.length; ++i) {
- _bindSingle(combinations[i], callback, action);
- }
- };
+ var regl = __webpack_require__(26)(canvas);
- // start!
- _addEvent(targetElement, 'keypress', _handleKeyEvent);
- _addEvent(targetElement, 'keydown', _handleKeyEvent);
- _addEvent(targetElement, 'keyup', _handleKeyEvent);
+ var initial_texture = [];
+ for (row = 0; row < opts.size; row++) {
+ rowdata = []
+ for (col = 0; col < opts.size; col++) {
+ rowdata.push([255, 255, 255]);
+ }
+ initial_texture.push(rowdata);
+ }
+ var salt = $("#grid").data("identicon-salt");
+ for (var i = 0; i < num_identicons; i++) {
+ texture = new pixdenticon(md5(salt + i), opts.size).render().buffer;
+ for (row = 0; row < opts.size; row++) {
+ initial_texture.push(texture[row]);
}
+ }
- /**
- * binds an event to mousetrap
- *
- * can be a single key, a combination of keys separated with +,
- * an array of keys, or a sequence of keys separated by spaces
- *
- * be sure to list the modifier keys first to make sure that the
- * correct key ends up getting bound (the last key in the pattern)
- *
- * @param {string|Array} keys
- * @param {Function} callback
- * @param {string=} action - 'keypress', 'keydown', or 'keyup'
- * @returns void
- */
- Mousetrap.prototype.bind = function(keys, callback, action) {
- var self = this;
- keys = keys instanceof Array ? keys : [keys];
- self._bindMultiple.call(self, keys, callback, action);
- return self;
- };
+ var texture = regl.texture(initial_texture);
- /**
- * unbinds an event to mousetrap
- *
- * the unbinding sets the callback function of the specified key combo
- * to an empty function and deletes the corresponding key in the
- * _directMap dict.
- *
- * TODO: actually remove this from the _callbacks dictionary instead
- * of binding an empty function
- *
- * the keycombo+action has to be exactly the same as
- * it was defined in the bind method
- *
- * @param {string|Array} keys
- * @param {string} action
- * @returns void
- */
- Mousetrap.prototype.unbind = function(keys, action) {
- var self = this;
- return self.bind.call(self, keys, function() {}, action);
- };
+ var squares = regl({
+ vert: `
+ precision mediump float;
+ attribute vec2 position;
+ attribute vec2 texcoords;
+ attribute vec3 color;
+ varying vec3 vcolor;
+ varying vec2 v_texcoords;
+ void main() {
+ gl_PointSize = float(${opts.size});
+ gl_Position = vec4(position.x, position.y, 0.0, 1.0);
+ v_texcoords = texcoords;
+ vcolor = color;
+ }
+ `,
+ frag: `
+ precision mediump float;
+ varying vec3 vcolor;
+ varying vec2 v_texcoords;
+ uniform sampler2D vtexture;
+ void main() {
+ vec4 texture;
+ texture = texture2D(vtexture, v_texcoords);
+ gl_FragColor = texture * vec4(vcolor.r, vcolor.g, vcolor.b, 1.0);
+ }
+ `,
+ attributes: { position: regl.prop("position"), texcoords: regl.prop("texcoords"), color: regl.prop("color")},
+ primitive: "triangles",
+ count: colors.length * 6,
+ uniforms: { vtexture: texture }
+ });
- /**
- * triggers an event that has already been bound
- *
- * @param {string} keys
- * @param {string=} action
- * @returns void
- */
- Mousetrap.prototype.trigger = function(keys, action) {
- var self = this;
- if (self._directMap[keys + ':' + action]) {
- self._directMap[keys + ':' + action]({}, keys);
- }
- return self;
- };
+ var expanded_colors = [];
+ for(var i = 0; i < colors.length; ++i){
+ for(var n = 0; n < 6; ++n) {
+ expanded_colors.push(colors[i]);
+ }
+ }
- /**
- * resets the library back to its initial state. this is useful
- * if you want to clear out the current keyboard shortcuts and bind
- * new ones - for example if you switch to another page
- *
- * @returns void
- */
- Mousetrap.prototype.reset = function() {
- var self = this;
- self._callbacks = {};
- self._directMap = {};
- return self;
- };
+ var buffer = { position: regl.buffer(positions), texcoords: regl.buffer(texcoords), color: regl.buffer(expanded_colors)};
- /**
- * should we stop this event before firing off callbacks
- *
- * @param {Event} e
- * @param {Element} element
- * @return {boolean}
- */
- Mousetrap.prototype.stopCallback = function(e, element) {
- var self = this;
+ var draw = function(positions, texcoords, colors) {
+ regl.clear({ color: opts.background.concat([ 1 ]) });
+ squares({ position: positions, texcoords: texcoords, color: colors });
+ };
- // if the element has the class "mousetrap" then no need to stop
- if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
- return false;
- }
+ draw(buffer.position, buffer.texcoords, buffer.color);
- if (_belongsTo(element, self.target)) {
- return false;
- }
+ self._buffer = buffer;
+ self._draw = draw;
+ self._formatted = opts.formatted;
+ self.canvas = canvas;
+ self.frame = regl.frame;
+}
- // Events originating from a shadow DOM are re-targetted and `e.target` is the shadow host,
- // not the initial event target in the shadow tree. Note that not all events cross the
- // shadow boundary.
- // For shadow trees with `mode: 'open'`, the initial event target is the first element in
- // the event’s composed path. For shadow trees with `mode: 'closed'`, the initial event
- // target cannot be obtained.
- if ('composedPath' in e && typeof e.composedPath === 'function') {
- // For open shadow trees, update `element` so that the following check works.
- var initialEventTarget = e.composedPath()[0];
- if (initialEventTarget !== e.target) {
- element = initialEventTarget;
- }
- }
+Pixels.prototype.update = function(data, textures) {
+ var self = this;
+ var colors = self._formatted ? data : convert(data);
+ var expanded_colors = [];
- // stop for input, select, and textarea
- return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
- };
+ for(var i = 0; i < colors.length; ++i){
+ for(var n = 0; n < 6; ++n) {
+ expanded_colors.push(colors[i]);
+ }
+ }
- /**
- * exposes _handleKey publicly so it can be overwritten by extensions
- */
- Mousetrap.prototype.handleKey = function() {
- var self = this;
- return self._handleKey.apply(self, arguments);
- };
+ var opts = this.opts;
+ var num_identicons = 100;
- /**
- * allow custom key mappings
- */
- Mousetrap.addKeycodes = function(object) {
- for (var key in object) {
- if (object.hasOwnProperty(key)) {
- _MAP[key] = object[key];
- }
- }
- _REVERSE_MAP = null;
- };
+ var texcoords = texcoord(
+ opts.rows,
+ opts.columns,
+ textures,
+ num_identicons
+ );
- /**
- * Init the global mousetrap functions
- *
- * This method is needed to allow the global mousetrap functions to work
- * now that mousetrap is a constructor function.
- */
- Mousetrap.init = function() {
- var documentMousetrap = Mousetrap(document);
- for (var method in documentMousetrap) {
- if (method.charAt(0) !== '_') {
- Mousetrap[method] = (function(method) {
- return function() {
- return documentMousetrap[method].apply(documentMousetrap, arguments);
- };
- } (method));
- }
- }
- };
+ self._draw(self._buffer.position, self._buffer.texcoords(texcoords), self._buffer.color(expanded_colors));
+};
- Mousetrap.init();
+module.exports = Pixels;
- // expose mousetrap to the global object
- window.Mousetrap = Mousetrap;
- // expose as a common js module
- if (typeof module !== 'undefined' && module.exports) {
- module.exports = Mousetrap;
- }
+/***/ }),
+/* 15 */
+/***/ (function(module, exports, __webpack_require__) {
- // expose mousetrap as an AMD module
- if (true) {
- !(__WEBPACK_AMD_DEFINE_RESULT__ = function() {
- return Mousetrap;
- }.call(exports, __webpack_require__, exports, module),
- __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
+var conversions = __webpack_require__(16);
+
+var convert = function() {
+ return new Converter();
+}
+
+for (var func in conversions) {
+ // export Raw versions
+ convert[func + "Raw"] = (function(func) {
+ // accept array or plain args
+ return function(arg) {
+ if (typeof arg == "number")
+ arg = Array.prototype.slice.call(arguments);
+ return conversions[func](arg);
}
-}) (typeof window !== 'undefined' ? window : null, typeof window !== 'undefined' ? document : null);
+ })(func);
+ var pair = /(\w+)2(\w+)/.exec(func),
+ from = pair[1],
+ to = pair[2];
-/***/ }),
-/* 13 */
-/***/ (function(module, exports, __webpack_require__) {
+ // export rgb2hsl and ["rgb"]["hsl"]
+ convert[from] = convert[from] || {};
-"use strict";
+ convert[from][to] = convert[func] = (function(func) {
+ return function(arg) {
+ if (typeof arg == "number")
+ arg = Array.prototype.slice.call(arguments);
+
+ var val = conversions[func](arg);
+ if (typeof val == "string" || val === undefined)
+ return val; // keyword
-;
-;
-;
-var isWebSocket = function (constructor) {
- return constructor && constructor.CLOSING === 2;
+ for (var i = 0; i < val.length; i++)
+ val[i] = Math.round(val[i]);
+ return val;
+ }
+ })(func);
+}
+
+
+/* Converter does lazy conversion and caching */
+var Converter = function() {
+ this.convs = {};
};
-var isGlobalWebSocket = function () {
- return typeof WebSocket !== 'undefined' && isWebSocket(WebSocket);
+
+/* Either get the values for a space or
+ set the values for a space, depending on args */
+Converter.prototype.routeSpace = function(space, args) {
+ var values = args[0];
+ if (values === undefined) {
+ // color.rgb()
+ return this.getValues(space);
+ }
+ // color.rgb(10, 10, 10)
+ if (typeof values == "number") {
+ values = Array.prototype.slice.call(args);
+ }
+
+ return this.setValues(space, values);
};
-var getDefaultOptions = function () { return ({
- constructor: isGlobalWebSocket() ? WebSocket : null,
- maxReconnectionDelay: 10000,
- minReconnectionDelay: 1500,
- reconnectionDelayGrowFactor: 1.3,
- connectionTimeout: 4000,
- maxRetries: Infinity,
- debug: false,
-}); };
-var bypassProperty = function (src, dst, name) {
- Object.defineProperty(dst, name, {
- get: function () { return src[name]; },
- set: function (value) { src[name] = value; },
- enumerable: true,
- configurable: true,
- });
-};
-var initReconnectionDelay = function (config) {
- return (config.minReconnectionDelay + Math.random() * config.minReconnectionDelay);
-};
-var updateReconnectionDelay = function (config, previousDelay) {
- var newDelay = previousDelay * config.reconnectionDelayGrowFactor;
- return (newDelay > config.maxReconnectionDelay)
- ? config.maxReconnectionDelay
- : newDelay;
-};
-var LEVEL_0_EVENTS = ['onopen', 'onclose', 'onmessage', 'onerror'];
-var reassignEventListeners = function (ws, oldWs, listeners) {
- Object.keys(listeners).forEach(function (type) {
- listeners[type].forEach(function (_a) {
- var listener = _a[0], options = _a[1];
- ws.addEventListener(type, listener, options);
- });
- });
- if (oldWs) {
- LEVEL_0_EVENTS.forEach(function (name) {
- ws[name] = oldWs[name];
- });
- }
+
+/* Set the values for a space, invalidating cache */
+Converter.prototype.setValues = function(space, values) {
+ this.space = space;
+ this.convs = {};
+ this.convs[space] = values;
+ return this;
};
-var ReconnectingWebsocket = function (url, protocols, options) {
- var _this = this;
- if (options === void 0) { options = {}; }
- var ws;
- var connectingTimeout;
- var reconnectDelay = 0;
- var retriesCount = 0;
- var shouldRetry = true;
- var savedOnClose = null;
- var listeners = {};
- // require new to construct
- if (!(this instanceof ReconnectingWebsocket)) {
- throw new TypeError("Failed to construct 'ReconnectingWebSocket': Please use the 'new' operator");
- }
- // Set config. Not using `Object.assign` because of IE11
- var config = getDefaultOptions();
- Object.keys(config)
- .filter(function (key) { return options.hasOwnProperty(key); })
- .forEach(function (key) { return config[key] = options[key]; });
- if (!isWebSocket(config.constructor)) {
- throw new TypeError('Invalid WebSocket constructor. Set `options.constructor`');
- }
- var log = config.debug ? function () {
- var params = [];
- for (var _i = 0; _i < arguments.length; _i++) {
- params[_i] = arguments[_i];
- }
- return console.log.apply(console, ['RWS:'].concat(params));
- } : function () { };
- /**
- * Not using dispatchEvent, otherwise we must use a DOM Event object
- * Deferred because we want to handle the close event before this
- */
- var emitError = function (code, msg) { return setTimeout(function () {
- var err = new Error(msg);
- err.code = code;
- if (Array.isArray(listeners.error)) {
- listeners.error.forEach(function (_a) {
- var fn = _a[0];
- return fn(err);
- });
- }
- if (ws.onerror) {
- ws.onerror(err);
- }
- }, 0); };
- var handleClose = function () {
- log('handleClose', { shouldRetry: shouldRetry });
- retriesCount++;
- log('retries count:', retriesCount);
- if (retriesCount > config.maxRetries) {
- emitError('EHOSTDOWN', 'Too many failed connection attempts');
- return;
- }
- if (!reconnectDelay) {
- reconnectDelay = initReconnectionDelay(config);
- }
- else {
- reconnectDelay = updateReconnectionDelay(config, reconnectDelay);
- }
- log('handleClose - reconnectDelay:', reconnectDelay);
- if (shouldRetry) {
- setTimeout(connect, reconnectDelay);
- }
- };
- var connect = function () {
- if (!shouldRetry) {
- return;
- }
- log('connect');
- var oldWs = ws;
- var wsUrl = (typeof url === 'function') ? url() : url;
- ws = new config.constructor(wsUrl, protocols);
- connectingTimeout = setTimeout(function () {
- log('timeout');
- ws.close();
- emitError('ETIMEDOUT', 'Connection timeout');
- }, config.connectionTimeout);
- log('bypass properties');
- for (var key in ws) {
- // @todo move to constant
- if (['addEventListener', 'removeEventListener', 'close', 'send'].indexOf(key) < 0) {
- bypassProperty(ws, _this, key);
- }
- }
- ws.addEventListener('open', function () {
- clearTimeout(connectingTimeout);
- log('open');
- reconnectDelay = initReconnectionDelay(config);
- log('reconnectDelay:', reconnectDelay);
- retriesCount = 0;
- });
- ws.addEventListener('close', handleClose);
- reassignEventListeners(ws, oldWs, listeners);
- // because when closing with fastClose=true, it is saved and set to null to avoid double calls
- ws.onclose = ws.onclose || savedOnClose;
- savedOnClose = null;
- };
- log('init');
- connect();
- this.close = function (code, reason, _a) {
- if (code === void 0) { code = 1000; }
- if (reason === void 0) { reason = ''; }
- var _b = _a === void 0 ? {} : _a, _c = _b.keepClosed, keepClosed = _c === void 0 ? false : _c, _d = _b.fastClose, fastClose = _d === void 0 ? true : _d, _e = _b.delay, delay = _e === void 0 ? 0 : _e;
- log('close - params:', { reason: reason, keepClosed: keepClosed, fastClose: fastClose, delay: delay, retriesCount: retriesCount, maxRetries: config.maxRetries });
- shouldRetry = !keepClosed && retriesCount <= config.maxRetries;
- if (delay) {
- reconnectDelay = delay;
- }
- ws.close(code, reason);
- if (fastClose) {
- var fakeCloseEvent_1 = {
- code: code,
- reason: reason,
- wasClean: true,
- };
- // execute close listeners soon with a fake closeEvent
- // and remove them from the WS instance so they
- // don't get fired on the real close.
- handleClose();
- ws.removeEventListener('close', handleClose);
- // run and remove level2
- if (Array.isArray(listeners.close)) {
- listeners.close.forEach(function (_a) {
- var listener = _a[0], options = _a[1];
- listener(fakeCloseEvent_1);
- ws.removeEventListener('close', listener, options);
- });
- }
- // run and remove level0
- if (ws.onclose) {
- savedOnClose = ws.onclose;
- ws.onclose(fakeCloseEvent_1);
- ws.onclose = null;
- }
- }
- };
- this.send = function (data) {
- ws.send(data);
- };
- this.addEventListener = function (type, listener, options) {
- if (Array.isArray(listeners[type])) {
- if (!listeners[type].some(function (_a) {
- var l = _a[0];
- return l === listener;
- })) {
- listeners[type].push([listener, options]);
- }
- }
- else {
- listeners[type] = [[listener, options]];
- }
- ws.addEventListener(type, listener, options);
- };
- this.removeEventListener = function (type, listener, options) {
- if (Array.isArray(listeners[type])) {
- listeners[type] = listeners[type].filter(function (_a) {
- var l = _a[0];
- return l !== listener;
- });
- }
- ws.removeEventListener(type, listener, options);
- };
+
+/* Get the values for a space. If there's already
+ a conversion for the space, fetch it, otherwise
+ compute it */
+Converter.prototype.getValues = function(space) {
+ var vals = this.convs[space];
+ if (!vals) {
+ var fspace = this.space,
+ from = this.convs[fspace];
+ vals = convert[fspace][space](from);
+
+ this.convs[space] = vals;
+ }
+ return vals;
};
-module.exports = ReconnectingWebsocket;
+["rgb", "hsl", "hsv", "cmyk", "keyword"].forEach(function(space) {
+ Converter.prototype[space] = function(vals) {
+ return this.routeSpace(space, arguments);
+ }
+});
+
+module.exports = convert;
/***/ }),
-/* 14 */
-/***/ (function(module, exports, __webpack_require__) {
+/* 16 */
+/***/ (function(module, exports) {
-/* WEBPACK VAR INJECTION */(function(process) {// Copyright Joyent, Inc. and other Node contributors.
-//
-// Permission is hereby granted, free of charge, to any person obtaining a
-// copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to permit
-// persons to whom the Software is furnished to do so, subject to the
-// following conditions:
-//
-// The above copyright notice and this permission notice shall be included
-// in all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
-// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
-// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
-// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
-// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
-// USE OR OTHER DEALINGS IN THE SOFTWARE.
+/* MIT license */
-var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors ||
- function getOwnPropertyDescriptors(obj) {
- var keys = Object.keys(obj);
- var descriptors = {};
- for (var i = 0; i < keys.length; i++) {
- descriptors[keys[i]] = Object.getOwnPropertyDescriptor(obj, keys[i]);
- }
- return descriptors;
- };
+module.exports = {
+ rgb2hsl: rgb2hsl,
+ rgb2hsv: rgb2hsv,
+ rgb2hwb: rgb2hwb,
+ rgb2cmyk: rgb2cmyk,
+ rgb2keyword: rgb2keyword,
+ rgb2xyz: rgb2xyz,
+ rgb2lab: rgb2lab,
+ rgb2lch: rgb2lch,
-var formatRegExp = /%[sdj%]/g;
-exports.format = function(f) {
- if (!isString(f)) {
- var objects = [];
- for (var i = 0; i < arguments.length; i++) {
- objects.push(inspect(arguments[i]));
- }
- return objects.join(' ');
- }
-
- var i = 1;
- var args = arguments;
- var len = args.length;
- var str = String(f).replace(formatRegExp, function(x) {
- if (x === '%%') return '%';
- if (i >= len) return x;
- switch (x) {
- case '%s': return String(args[i++]);
- case '%d': return Number(args[i++]);
- case '%j':
- try {
- return JSON.stringify(args[i++]);
- } catch (_) {
- return '[Circular]';
- }
- default:
- return x;
- }
- });
- for (var x = args[i]; i < len; x = args[++i]) {
- if (isNull(x) || !isObject(x)) {
- str += ' ' + x;
- } else {
- str += ' ' + inspect(x);
- }
- }
- return str;
-};
+ hsl2rgb: hsl2rgb,
+ hsl2hsv: hsl2hsv,
+ hsl2hwb: hsl2hwb,
+ hsl2cmyk: hsl2cmyk,
+ hsl2keyword: hsl2keyword,
+ hsv2rgb: hsv2rgb,
+ hsv2hsl: hsv2hsl,
+ hsv2hwb: hsv2hwb,
+ hsv2cmyk: hsv2cmyk,
+ hsv2keyword: hsv2keyword,
-// Mark that a method should not be used.
-// Returns a modified function which warns once by default.
-// If --no-deprecation is set, then it is a no-op.
-exports.deprecate = function(fn, msg) {
- if (typeof process !== 'undefined' && process.noDeprecation === true) {
- return fn;
- }
+ hwb2rgb: hwb2rgb,
+ hwb2hsl: hwb2hsl,
+ hwb2hsv: hwb2hsv,
+ hwb2cmyk: hwb2cmyk,
+ hwb2keyword: hwb2keyword,
- // Allow for deprecating things in the process of starting up.
- if (typeof process === 'undefined') {
- return function() {
- return exports.deprecate(fn, msg).apply(this, arguments);
- };
- }
+ cmyk2rgb: cmyk2rgb,
+ cmyk2hsl: cmyk2hsl,
+ cmyk2hsv: cmyk2hsv,
+ cmyk2hwb: cmyk2hwb,
+ cmyk2keyword: cmyk2keyword,
- var warned = false;
- function deprecated() {
- if (!warned) {
- if (process.throwDeprecation) {
- throw new Error(msg);
- } else if (process.traceDeprecation) {
- console.trace(msg);
- } else {
- console.error(msg);
- }
- warned = true;
- }
- return fn.apply(this, arguments);
- }
+ keyword2rgb: keyword2rgb,
+ keyword2hsl: keyword2hsl,
+ keyword2hsv: keyword2hsv,
+ keyword2hwb: keyword2hwb,
+ keyword2cmyk: keyword2cmyk,
+ keyword2lab: keyword2lab,
+ keyword2xyz: keyword2xyz,
- return deprecated;
-};
+ xyz2rgb: xyz2rgb,
+ xyz2lab: xyz2lab,
+ xyz2lch: xyz2lch,
+ lab2xyz: lab2xyz,
+ lab2rgb: lab2rgb,
+ lab2lch: lab2lch,
-var debugs = {};
-var debugEnviron;
-exports.debuglog = function(set) {
- if (isUndefined(debugEnviron))
- debugEnviron = process.env.NODE_DEBUG || '';
- set = set.toUpperCase();
- if (!debugs[set]) {
- if (new RegExp('\\b' + set + '\\b', 'i').test(debugEnviron)) {
- var pid = process.pid;
- debugs[set] = function() {
- var msg = exports.format.apply(exports, arguments);
- console.error('%s %d: %s', set, pid, msg);
- };
- } else {
- debugs[set] = function() {};
- }
- }
- return debugs[set];
-};
+ lch2lab: lch2lab,
+ lch2xyz: lch2xyz,
+ lch2rgb: lch2rgb
+}
-/**
- * Echos the value of a value. Trys to print the value out
- * in the best way possible given the different types.
- *
- * @param {Object} obj The object to print out.
- * @param {Object} opts Optional options object that alters the output.
- */
-/* legacy: obj, showHidden, depth, colors*/
-function inspect(obj, opts) {
- // default options
- var ctx = {
- seen: [],
- stylize: stylizeNoColor
- };
- // legacy...
- if (arguments.length >= 3) ctx.depth = arguments[2];
- if (arguments.length >= 4) ctx.colors = arguments[3];
- if (isBoolean(opts)) {
- // legacy...
- ctx.showHidden = opts;
- } else if (opts) {
- // got an "options" object
- exports._extend(ctx, opts);
- }
- // set default options
- if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
- if (isUndefined(ctx.depth)) ctx.depth = 2;
- if (isUndefined(ctx.colors)) ctx.colors = false;
- if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
- if (ctx.colors) ctx.stylize = stylizeWithColor;
- return formatValue(ctx, obj, ctx.depth);
-}
-exports.inspect = inspect;
+function rgb2hsl(rgb) {
+ var r = rgb[0]/255,
+ g = rgb[1]/255,
+ b = rgb[2]/255,
+ min = Math.min(r, g, b),
+ max = Math.max(r, g, b),
+ delta = max - min,
+ h, s, l;
+ if (max == min)
+ h = 0;
+ else if (r == max)
+ h = (g - b) / delta;
+ else if (g == max)
+ h = 2 + (b - r) / delta;
+ else if (b == max)
+ h = 4 + (r - g)/ delta;
-// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
-inspect.colors = {
- 'bold' : [1, 22],
- 'italic' : [3, 23],
- 'underline' : [4, 24],
- 'inverse' : [7, 27],
- 'white' : [37, 39],
- 'grey' : [90, 39],
- 'black' : [30, 39],
- 'blue' : [34, 39],
- 'cyan' : [36, 39],
- 'green' : [32, 39],
- 'magenta' : [35, 39],
- 'red' : [31, 39],
- 'yellow' : [33, 39]
-};
+ h = Math.min(h * 60, 360);
-// Don't use 'blue' not visible on cmd.exe
-inspect.styles = {
- 'special': 'cyan',
- 'number': 'yellow',
- 'boolean': 'yellow',
- 'undefined': 'grey',
- 'null': 'bold',
- 'string': 'green',
- 'date': 'magenta',
- // "name": intentionally not styling
- 'regexp': 'red'
-};
+ if (h < 0)
+ h += 360;
+ l = (min + max) / 2;
-function stylizeWithColor(str, styleType) {
- var style = inspect.styles[styleType];
+ if (max == min)
+ s = 0;
+ else if (l <= 0.5)
+ s = delta / (max + min);
+ else
+ s = delta / (2 - max - min);
- if (style) {
- return '\u001b[' + inspect.colors[style][0] + 'm' + str +
- '\u001b[' + inspect.colors[style][1] + 'm';
- } else {
- return str;
- }
+ return [h, s * 100, l * 100];
}
+function rgb2hsv(rgb) {
+ var r = rgb[0],
+ g = rgb[1],
+ b = rgb[2],
+ min = Math.min(r, g, b),
+ max = Math.max(r, g, b),
+ delta = max - min,
+ h, s, v;
-function stylizeNoColor(str, styleType) {
- return str;
-}
+ if (max == 0)
+ s = 0;
+ else
+ s = (delta/max * 1000)/10;
+ if (max == min)
+ h = 0;
+ else if (r == max)
+ h = (g - b) / delta;
+ else if (g == max)
+ h = 2 + (b - r) / delta;
+ else if (b == max)
+ h = 4 + (r - g) / delta;
-function arrayToHash(array) {
- var hash = {};
+ h = Math.min(h * 60, 360);
- array.forEach(function(val, idx) {
- hash[val] = true;
- });
+ if (h < 0)
+ h += 360;
- return hash;
+ v = ((max / 255) * 1000) / 10;
+
+ return [h, s, v];
}
+function rgb2hwb(rgb) {
+ var r = rgb[0],
+ g = rgb[1],
+ b = rgb[2],
+ h = rgb2hsl(rgb)[0],
+ w = 1/255 * Math.min(r, Math.min(g, b)),
+ b = 1 - 1/255 * Math.max(r, Math.max(g, b));
-function formatValue(ctx, value, recurseTimes) {
- // Provide a hook for user-specified inspect functions.
- // Check that value is an object with an inspect function on it
- if (ctx.customInspect &&
- value &&
- isFunction(value.inspect) &&
- // Filter out the util module, it's inspect function is special
- value.inspect !== exports.inspect &&
- // Also filter out any prototype objects using the circular check.
- !(value.constructor && value.constructor.prototype === value)) {
- var ret = value.inspect(recurseTimes, ctx);
- if (!isString(ret)) {
- ret = formatValue(ctx, ret, recurseTimes);
- }
- return ret;
- }
+ return [h, w * 100, b * 100];
+}
- // Primitive types cannot have properties
- var primitive = formatPrimitive(ctx, value);
- if (primitive) {
- return primitive;
- }
+function rgb2cmyk(rgb) {
+ var r = rgb[0] / 255,
+ g = rgb[1] / 255,
+ b = rgb[2] / 255,
+ c, m, y, k;
- // Look up the keys of the object.
- var keys = Object.keys(value);
- var visibleKeys = arrayToHash(keys);
+ k = Math.min(1 - r, 1 - g, 1 - b);
+ c = (1 - r - k) / (1 - k) || 0;
+ m = (1 - g - k) / (1 - k) || 0;
+ y = (1 - b - k) / (1 - k) || 0;
+ return [c * 100, m * 100, y * 100, k * 100];
+}
- if (ctx.showHidden) {
- keys = Object.getOwnPropertyNames(value);
- }
+function rgb2keyword(rgb) {
+ return reverseKeywords[JSON.stringify(rgb)];
+}
- // IE doesn't make error fields non-enumerable
- // http://msdn.microsoft.com/en-us/library/ie/dww52sbt(v=vs.94).aspx
- if (isError(value)
- && (keys.indexOf('message') >= 0 || keys.indexOf('description') >= 0)) {
- return formatError(value);
- }
+function rgb2xyz(rgb) {
+ var r = rgb[0] / 255,
+ g = rgb[1] / 255,
+ b = rgb[2] / 255;
- // Some type of object without properties can be shortcutted.
- if (keys.length === 0) {
- if (isFunction(value)) {
- var name = value.name ? ': ' + value.name : '';
- return ctx.stylize('[Function' + name + ']', 'special');
- }
- if (isRegExp(value)) {
- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
- }
- if (isDate(value)) {
- return ctx.stylize(Date.prototype.toString.call(value), 'date');
- }
- if (isError(value)) {
- return formatError(value);
- }
- }
+ // assume sRGB
+ r = r > 0.04045 ? Math.pow(((r + 0.055) / 1.055), 2.4) : (r / 12.92);
+ g = g > 0.04045 ? Math.pow(((g + 0.055) / 1.055), 2.4) : (g / 12.92);
+ b = b > 0.04045 ? Math.pow(((b + 0.055) / 1.055), 2.4) : (b / 12.92);
- var base = '', array = false, braces = ['{', '}'];
+ var x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
+ var y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
+ var z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
- // Make Array say that they are Array
- if (isArray(value)) {
- array = true;
- braces = ['[', ']'];
- }
+ return [x * 100, y *100, z * 100];
+}
- // Make functions say that they are functions
- if (isFunction(value)) {
- var n = value.name ? ': ' + value.name : '';
- base = ' [Function' + n + ']';
- }
+function rgb2lab(rgb) {
+ var xyz = rgb2xyz(rgb),
+ x = xyz[0],
+ y = xyz[1],
+ z = xyz[2],
+ l, a, b;
- // Make RegExps say that they are RegExps
- if (isRegExp(value)) {
- base = ' ' + RegExp.prototype.toString.call(value);
- }
+ x /= 95.047;
+ y /= 100;
+ z /= 108.883;
- // Make dates with properties first say the date
- if (isDate(value)) {
- base = ' ' + Date.prototype.toUTCString.call(value);
- }
+ x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
+ y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
+ z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
- // Make error with message first say the error
- if (isError(value)) {
- base = ' ' + formatError(value);
- }
+ l = (116 * y) - 16;
+ a = 500 * (x - y);
+ b = 200 * (y - z);
- if (keys.length === 0 && (!array || value.length == 0)) {
- return braces[0] + base + braces[1];
- }
+ return [l, a, b];
+}
- if (recurseTimes < 0) {
- if (isRegExp(value)) {
- return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
- } else {
- return ctx.stylize('[Object]', 'special');
- }
- }
+function rgb2lch(args) {
+ return lab2lch(rgb2lab(args));
+}
- ctx.seen.push(value);
+function hsl2rgb(hsl) {
+ var h = hsl[0] / 360,
+ s = hsl[1] / 100,
+ l = hsl[2] / 100,
+ t1, t2, t3, rgb, val;
- var output;
- if (array) {
- output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
- } else {
- output = keys.map(function(key) {
- return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
- });
+ if (s == 0) {
+ val = l * 255;
+ return [val, val, val];
}
- ctx.seen.pop();
+ if (l < 0.5)
+ t2 = l * (1 + s);
+ else
+ t2 = l + s - l * s;
+ t1 = 2 * l - t2;
- return reduceToSingleString(output, base, braces);
+ rgb = [0, 0, 0];
+ for (var i = 0; i < 3; i++) {
+ t3 = h + 1 / 3 * - (i - 1);
+ t3 < 0 && t3++;
+ t3 > 1 && t3--;
+
+ if (6 * t3 < 1)
+ val = t1 + (t2 - t1) * 6 * t3;
+ else if (2 * t3 < 1)
+ val = t2;
+ else if (3 * t3 < 2)
+ val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
+ else
+ val = t1;
+
+ rgb[i] = val * 255;
+ }
+
+ return rgb;
}
+function hsl2hsv(hsl) {
+ var h = hsl[0],
+ s = hsl[1] / 100,
+ l = hsl[2] / 100,
+ sv, v;
-function formatPrimitive(ctx, value) {
- if (isUndefined(value))
- return ctx.stylize('undefined', 'undefined');
- if (isString(value)) {
- var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
- .replace(/'/g, "\\'")
- .replace(/\\"/g, '"') + '\'';
- return ctx.stylize(simple, 'string');
+ if(l === 0) {
+ // no need to do calc on black
+ // also avoids divide by 0 error
+ return [0, 0, 0];
}
- if (isNumber(value))
- return ctx.stylize('' + value, 'number');
- if (isBoolean(value))
- return ctx.stylize('' + value, 'boolean');
- // For some reason typeof null is "object", so special case here.
- if (isNull(value))
- return ctx.stylize('null', 'null');
+
+ l *= 2;
+ s *= (l <= 1) ? l : 2 - l;
+ v = (l + s) / 2;
+ sv = (2 * s) / (l + s);
+ return [h, sv * 100, v * 100];
}
+function hsl2hwb(args) {
+ return rgb2hwb(hsl2rgb(args));
+}
-function formatError(value) {
- return '[' + Error.prototype.toString.call(value) + ']';
+function hsl2cmyk(args) {
+ return rgb2cmyk(hsl2rgb(args));
}
+function hsl2keyword(args) {
+ return rgb2keyword(hsl2rgb(args));
+}
-function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
- var output = [];
- for (var i = 0, l = value.length; i < l; ++i) {
- if (hasOwnProperty(value, String(i))) {
- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
- String(i), true));
- } else {
- output.push('');
- }
+
+function hsv2rgb(hsv) {
+ var h = hsv[0] / 60,
+ s = hsv[1] / 100,
+ v = hsv[2] / 100,
+ hi = Math.floor(h) % 6;
+
+ var f = h - Math.floor(h),
+ p = 255 * v * (1 - s),
+ q = 255 * v * (1 - (s * f)),
+ t = 255 * v * (1 - (s * (1 - f))),
+ v = 255 * v;
+
+ switch(hi) {
+ case 0:
+ return [v, t, p];
+ case 1:
+ return [q, v, p];
+ case 2:
+ return [p, v, t];
+ case 3:
+ return [p, q, v];
+ case 4:
+ return [t, p, v];
+ case 5:
+ return [v, p, q];
}
- keys.forEach(function(key) {
- if (!key.match(/^\d+$/)) {
- output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
- key, true));
- }
- });
- return output;
}
+function hsv2hsl(hsv) {
+ var h = hsv[0],
+ s = hsv[1] / 100,
+ v = hsv[2] / 100,
+ sl, l;
-function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
- var name, str, desc;
- desc = Object.getOwnPropertyDescriptor(value, key) || { value: value[key] };
- if (desc.get) {
- if (desc.set) {
- str = ctx.stylize('[Getter/Setter]', 'special');
- } else {
- str = ctx.stylize('[Getter]', 'special');
- }
- } else {
- if (desc.set) {
- str = ctx.stylize('[Setter]', 'special');
- }
- }
- if (!hasOwnProperty(visibleKeys, key)) {
- name = '[' + key + ']';
- }
- if (!str) {
- if (ctx.seen.indexOf(desc.value) < 0) {
- if (isNull(recurseTimes)) {
- str = formatValue(ctx, desc.value, null);
- } else {
- str = formatValue(ctx, desc.value, recurseTimes - 1);
- }
- if (str.indexOf('\n') > -1) {
- if (array) {
- str = str.split('\n').map(function(line) {
- return ' ' + line;
- }).join('\n').substr(2);
- } else {
- str = '\n' + str.split('\n').map(function(line) {
- return ' ' + line;
- }).join('\n');
- }
- }
- } else {
- str = ctx.stylize('[Circular]', 'special');
- }
- }
- if (isUndefined(name)) {
- if (array && key.match(/^\d+$/)) {
- return str;
- }
- name = JSON.stringify('' + key);
- if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
- name = name.substr(1, name.length - 2);
- name = ctx.stylize(name, 'name');
- } else {
- name = name.replace(/'/g, "\\'")
- .replace(/\\"/g, '"')
- .replace(/(^"|"$)/g, "'");
- name = ctx.stylize(name, 'string');
- }
- }
-
- return name + ': ' + str;
+ l = (2 - s) * v;
+ sl = s * v;
+ sl /= (l <= 1) ? l : 2 - l;
+ sl = sl || 0;
+ l /= 2;
+ return [h, sl * 100, l * 100];
}
+function hsv2hwb(args) {
+ return rgb2hwb(hsv2rgb(args))
+}
-function reduceToSingleString(output, base, braces) {
- var numLinesEst = 0;
- var length = output.reduce(function(prev, cur) {
- numLinesEst++;
- if (cur.indexOf('\n') >= 0) numLinesEst++;
- return prev + cur.replace(/\u001b\[\d\d?m/g, '').length + 1;
- }, 0);
-
- if (length > 60) {
- return braces[0] +
- (base === '' ? '' : base + '\n ') +
- ' ' +
- output.join(',\n ') +
- ' ' +
- braces[1];
- }
+function hsv2cmyk(args) {
+ return rgb2cmyk(hsv2rgb(args));
+}
- return braces[0] + base + ' ' + output.join(', ') + ' ' + braces[1];
+function hsv2keyword(args) {
+ return rgb2keyword(hsv2rgb(args));
}
+// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
+function hwb2rgb(hwb) {
+ var h = hwb[0] / 360,
+ wh = hwb[1] / 100,
+ bl = hwb[2] / 100,
+ ratio = wh + bl,
+ i, v, f, n;
-// NOTE: These type checking functions intentionally don't use `instanceof`
-// because it is fragile and can be easily faked with `Object.create()`.
-function isArray(ar) {
- return Array.isArray(ar);
-}
-exports.isArray = isArray;
+ // wh + bl cant be > 1
+ if (ratio > 1) {
+ wh /= ratio;
+ bl /= ratio;
+ }
-function isBoolean(arg) {
- return typeof arg === 'boolean';
-}
-exports.isBoolean = isBoolean;
+ i = Math.floor(6 * h);
+ v = 1 - bl;
+ f = 6 * h - i;
+ if ((i & 0x01) != 0) {
+ f = 1 - f;
+ }
+ n = wh + f * (v - wh); // linear interpolation
-function isNull(arg) {
- return arg === null;
-}
-exports.isNull = isNull;
+ switch (i) {
+ default:
+ case 6:
+ case 0: r = v; g = n; b = wh; break;
+ case 1: r = n; g = v; b = wh; break;
+ case 2: r = wh; g = v; b = n; break;
+ case 3: r = wh; g = n; b = v; break;
+ case 4: r = n; g = wh; b = v; break;
+ case 5: r = v; g = wh; b = n; break;
+ }
-function isNullOrUndefined(arg) {
- return arg == null;
+ return [r * 255, g * 255, b * 255];
}
-exports.isNullOrUndefined = isNullOrUndefined;
-function isNumber(arg) {
- return typeof arg === 'number';
+function hwb2hsl(args) {
+ return rgb2hsl(hwb2rgb(args));
}
-exports.isNumber = isNumber;
-function isString(arg) {
- return typeof arg === 'string';
+function hwb2hsv(args) {
+ return rgb2hsv(hwb2rgb(args));
}
-exports.isString = isString;
-function isSymbol(arg) {
- return typeof arg === 'symbol';
+function hwb2cmyk(args) {
+ return rgb2cmyk(hwb2rgb(args));
}
-exports.isSymbol = isSymbol;
-function isUndefined(arg) {
- return arg === void 0;
+function hwb2keyword(args) {
+ return rgb2keyword(hwb2rgb(args));
}
-exports.isUndefined = isUndefined;
-function isRegExp(re) {
- return isObject(re) && objectToString(re) === '[object RegExp]';
-}
-exports.isRegExp = isRegExp;
+function cmyk2rgb(cmyk) {
+ var c = cmyk[0] / 100,
+ m = cmyk[1] / 100,
+ y = cmyk[2] / 100,
+ k = cmyk[3] / 100,
+ r, g, b;
-function isObject(arg) {
- return typeof arg === 'object' && arg !== null;
+ r = 1 - Math.min(1, c * (1 - k) + k);
+ g = 1 - Math.min(1, m * (1 - k) + k);
+ b = 1 - Math.min(1, y * (1 - k) + k);
+ return [r * 255, g * 255, b * 255];
}
-exports.isObject = isObject;
-function isDate(d) {
- return isObject(d) && objectToString(d) === '[object Date]';
+function cmyk2hsl(args) {
+ return rgb2hsl(cmyk2rgb(args));
}
-exports.isDate = isDate;
-function isError(e) {
- return isObject(e) &&
- (objectToString(e) === '[object Error]' || e instanceof Error);
+function cmyk2hsv(args) {
+ return rgb2hsv(cmyk2rgb(args));
}
-exports.isError = isError;
-function isFunction(arg) {
- return typeof arg === 'function';
+function cmyk2hwb(args) {
+ return rgb2hwb(cmyk2rgb(args));
}
-exports.isFunction = isFunction;
-function isPrimitive(arg) {
- return arg === null ||
- typeof arg === 'boolean' ||
- typeof arg === 'number' ||
- typeof arg === 'string' ||
- typeof arg === 'symbol' || // ES6 symbol
- typeof arg === 'undefined';
+function cmyk2keyword(args) {
+ return rgb2keyword(cmyk2rgb(args));
}
-exports.isPrimitive = isPrimitive;
-exports.isBuffer = __webpack_require__(44);
-function objectToString(o) {
- return Object.prototype.toString.call(o);
-}
+function xyz2rgb(xyz) {
+ var x = xyz[0] / 100,
+ y = xyz[1] / 100,
+ z = xyz[2] / 100,
+ r, g, b;
+ r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
+ g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
+ b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
-function pad(n) {
- return n < 10 ? '0' + n.toString(10) : n.toString(10);
-}
+ // assume sRGB
+ r = r > 0.0031308 ? ((1.055 * Math.pow(r, 1.0 / 2.4)) - 0.055)
+ : r = (r * 12.92);
+ g = g > 0.0031308 ? ((1.055 * Math.pow(g, 1.0 / 2.4)) - 0.055)
+ : g = (g * 12.92);
-var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
- 'Oct', 'Nov', 'Dec'];
+ b = b > 0.0031308 ? ((1.055 * Math.pow(b, 1.0 / 2.4)) - 0.055)
+ : b = (b * 12.92);
-// 26 Feb 16:19:34
-function timestamp() {
- var d = new Date();
- var time = [pad(d.getHours()),
- pad(d.getMinutes()),
- pad(d.getSeconds())].join(':');
- return [d.getDate(), months[d.getMonth()], time].join(' ');
+ r = Math.min(Math.max(0, r), 1);
+ g = Math.min(Math.max(0, g), 1);
+ b = Math.min(Math.max(0, b), 1);
+
+ return [r * 255, g * 255, b * 255];
}
+function xyz2lab(xyz) {
+ var x = xyz[0],
+ y = xyz[1],
+ z = xyz[2],
+ l, a, b;
-// log is just a thin wrapper to console.log that prepends a timestamp
-exports.log = function() {
- console.log('%s - %s', timestamp(), exports.format.apply(exports, arguments));
-};
+ x /= 95.047;
+ y /= 100;
+ z /= 108.883;
+ x = x > 0.008856 ? Math.pow(x, 1/3) : (7.787 * x) + (16 / 116);
+ y = y > 0.008856 ? Math.pow(y, 1/3) : (7.787 * y) + (16 / 116);
+ z = z > 0.008856 ? Math.pow(z, 1/3) : (7.787 * z) + (16 / 116);
-/**
- * Inherit the prototype methods from one constructor into another.
- *
- * The Function.prototype.inherits from lang.js rewritten as a standalone
- * function (not on Function.prototype). NOTE: If this file is to be loaded
- * during bootstrapping this function needs to be rewritten using some native
- * functions as prototype setup using normal JavaScript does not work as
- * expected during bootstrapping (see mirror.js in r114903).
- *
- * @param {function} ctor Constructor function which needs to inherit the
- * prototype.
- * @param {function} superCtor Constructor function to inherit prototype from.
- */
-exports.inherits = __webpack_require__(43);
-
-exports._extend = function(origin, add) {
- // Don't do anything if add isn't an object
- if (!add || !isObject(add)) return origin;
-
- var keys = Object.keys(add);
- var i = keys.length;
- while (i--) {
- origin[keys[i]] = add[keys[i]];
- }
- return origin;
-};
+ l = (116 * y) - 16;
+ a = 500 * (x - y);
+ b = 200 * (y - z);
-function hasOwnProperty(obj, prop) {
- return Object.prototype.hasOwnProperty.call(obj, prop);
+ return [l, a, b];
}
-var kCustomPromisifiedSymbol = typeof Symbol !== 'undefined' ? Symbol('util.promisify.custom') : undefined;
+function xyz2lch(args) {
+ return lab2lch(xyz2lab(args));
+}
-exports.promisify = function promisify(original) {
- if (typeof original !== 'function')
- throw new TypeError('The "original" argument must be of type Function');
+function lab2xyz(lab) {
+ var l = lab[0],
+ a = lab[1],
+ b = lab[2],
+ x, y, z, y2;
- if (kCustomPromisifiedSymbol && original[kCustomPromisifiedSymbol]) {
- var fn = original[kCustomPromisifiedSymbol];
- if (typeof fn !== 'function') {
- throw new TypeError('The "util.promisify.custom" argument must be of type Function');
- }
- Object.defineProperty(fn, kCustomPromisifiedSymbol, {
- value: fn, enumerable: false, writable: false, configurable: true
- });
- return fn;
+ if (l <= 8) {
+ y = (l * 100) / 903.3;
+ y2 = (7.787 * (y / 100)) + (16 / 116);
+ } else {
+ y = 100 * Math.pow((l + 16) / 116, 3);
+ y2 = Math.pow(y / 100, 1/3);
}
- function fn() {
- var promiseResolve, promiseReject;
- var promise = new Promise(function (resolve, reject) {
- promiseResolve = resolve;
- promiseReject = reject;
- });
-
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
- args.push(arguments[i]);
- }
- args.push(function (err, value) {
- if (err) {
- promiseReject(err);
- } else {
- promiseResolve(value);
- }
- });
-
- try {
- original.apply(this, args);
- } catch (err) {
- promiseReject(err);
- }
-
- return promise;
- }
+ x = x / 95.047 <= 0.008856 ? x = (95.047 * ((a / 500) + y2 - (16 / 116))) / 7.787 : 95.047 * Math.pow((a / 500) + y2, 3);
- Object.setPrototypeOf(fn, Object.getPrototypeOf(original));
+ z = z / 108.883 <= 0.008859 ? z = (108.883 * (y2 - (b / 200) - (16 / 116))) / 7.787 : 108.883 * Math.pow(y2 - (b / 200), 3);
- if (kCustomPromisifiedSymbol) Object.defineProperty(fn, kCustomPromisifiedSymbol, {
- value: fn, enumerable: false, writable: false, configurable: true
- });
- return Object.defineProperties(
- fn,
- getOwnPropertyDescriptors(original)
- );
+ return [x, y, z];
}
-exports.promisify.custom = kCustomPromisifiedSymbol
+function lab2lch(lab) {
+ var l = lab[0],
+ a = lab[1],
+ b = lab[2],
+ hr, h, c;
-function callbackifyOnRejected(reason, cb) {
- // `!reason` guard inspired by bluebird (Ref: https://goo.gl/t5IS6M).
- // Because `null` is a special error value in callbacks which means "no error
- // occurred", we error-wrap so the callback consumer can distinguish between
- // "the promise rejected with null" or "the promise fulfilled with undefined".
- if (!reason) {
- var newReason = new Error('Promise was rejected with a falsy value');
- newReason.reason = reason;
- reason = newReason;
+ hr = Math.atan2(b, a);
+ h = hr * 360 / 2 / Math.PI;
+ if (h < 0) {
+ h += 360;
}
- return cb(reason);
+ c = Math.sqrt(a * a + b * b);
+ return [l, c, h];
}
-function callbackify(original) {
- if (typeof original !== 'function') {
- throw new TypeError('The "original" argument must be of type Function');
- }
-
- // We DO NOT return the promise as it gives the user a false sense that
- // the promise is actually somehow related to the callback's execution
- // and that the callback throwing will reject the promise.
- function callbackified() {
- var args = [];
- for (var i = 0; i < arguments.length; i++) {
- args.push(arguments[i]);
- }
-
- var maybeCb = args.pop();
- if (typeof maybeCb !== 'function') {
- throw new TypeError('The last argument must be of type Function');
- }
- var self = this;
- var cb = function() {
- return maybeCb.apply(self, arguments);
- };
- // In true node style we process the callback on `nextTick` with all the
- // implications (stack, `uncaughtException`, `async_hooks`)
- original.apply(this, args)
- .then(function(ret) { process.nextTick(cb, null, ret) },
- function(rej) { process.nextTick(callbackifyOnRejected, rej, cb) });
- }
-
- Object.setPrototypeOf(callbackified, Object.getPrototypeOf(original));
- Object.defineProperties(callbackified,
- getOwnPropertyDescriptors(original));
- return callbackified;
+function lab2rgb(args) {
+ return xyz2rgb(lab2xyz(args));
}
-exports.callbackify = callbackify;
-
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(40)))
-
-/***/ }),
-/* 15 */
-/***/ (function(module, exports) {
-
-module.exports = jQuery;
-
-/***/ }),
-/* 16 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var require;/*global dallinger, require, settings */
-/*jshint esversion: 6 */
-(function (dallinger, require, settings) {
-
-var util = __webpack_require__(14);
-var grid = __webpack_require__(7);
-var position = __webpack_require__(11);
-var Mousetrap = __webpack_require__(12);
-var ReconnectingWebSocket = __webpack_require__(13);
-var $ = __webpack_require__(15);
-var gaussian = __webpack_require__(10);
-var Color = __webpack_require__(9);
-var Identicon = __webpack_require__(8);
-var md5 = __webpack_require__(1);
+function lch2lab(lch) {
+ var l = lch[0],
+ c = lch[1],
+ h = lch[2],
+ a, b, hr;
-function coordsToIdx(x, y, columns) {
- return y * columns + x;
+ hr = h / 360 * 2 * Math.PI;
+ a = c * Math.cos(hr);
+ b = c * Math.sin(hr);
+ return [l, a, b];
}
-function animateColor(color) {
- if (settings.background_animation) {
- rand = Math.random() * 0.02;
- } else {
- rand = 0.01;
- }
- return [
- color[0] * 0.95 + rand,
- color[1] * 0.95 + rand,
- color[2] * 0.95 + rand
- ];
+function lch2xyz(args) {
+ return lab2xyz(lch2lab(args));
}
-function positionsAreEqual(a, b) {
- return a[0] === b[0] && a[1] === b[1];
+function lch2rgb(args) {
+ return lab2rgb(lch2lab(args));
}
-class Section {
- // Represents the currently visible section (window) of the grid
-
- constructor(data, left, top) {
- this.left = left;
- this.top = top;
- this.columns = settings.window_columns;
- this.rows = settings.window_rows;
- this.data = [];
- this.textures = [];
- // build data array for just this section
- for (var j = 0; j < this.rows; j++) {
- for (var i = 0; i < this.columns; i++) {
- this.data.push(data[this.sectionCoordsToGridIdx(i, j)]);
- this.textures.push(0);
- }
- }
- }
-
- gridCoordsToSectionIdx(x, y) {
- // Convert grid coordinates to section data array index
- return (y - this.top) * this.columns + (x - this.left);
- }
-
- sectionCoordsToGridIdx(x, y) {
- // Convert section coordinates to grid data array index
- return coordsToIdx(this.left + x, this.top + y, settings.columns);
- }
-
- plot(x, y, color, texture) {
- // Set color at position (x, y) in full-grid coordinates.
- if (x >= this.left && x < this.left + this.columns) {
- if (y >= this.top && y < this.top + this.rows) {
- this.data[this.gridCoordsToSectionIdx(x, y)] = color;
- if (texture !== undefined ){
- this.textures[this.gridCoordsToSectionIdx(x, y)] = texture;
- }
- background[coordsToIdx(x, y, settings.columns)] = color;
- }
- }
- }
-
- map(func) {
- // For each cell, call func with (x, y, color) to get the new color
- for (var j = 0; j < this.rows; j++) {
- for (var i = 0; i < this.columns; i++) {
- var idx = coordsToIdx(i, j, this.columns);
- this.data[idx] = Reflect.apply(
- func, this, [this.left + i, this.top + j, this.data[idx]]);
- }
- }
- }
+function keyword2rgb(keyword) {
+ return cssKeywords[keyword];
}
-var background = [], color;
-for (var j = 0; j < settings.rows; j++) {
- for (var i = 0; i < settings.columns; i++) {
- color = [0, 0, 0];
- for (var k = 0; k < 15; k++) {
- color = animateColor(color);
- }
- background.push(color);
- }
+function keyword2hsl(args) {
+ return rgb2hsl(keyword2rgb(args));
}
-var initialSection = new Section(background, 0, 0);
-
-var GREEN = [0.51, 0.69, 0.61];
-var WHITE = [1.00, 1.00, 1.00];
-var INVISIBLE_COLOR = [0.66, 0.66, 0.66];
-var CHANNEL = "griduniverse";
-var CONTROL_CHANNEL = "griduniverse_ctrl";
-
-var pixels = grid(initialSection.data, initialSection.textures, {
- rows: settings.window_rows,
- columns: settings.window_columns,
- size: settings.block_size,
- padding: settings.padding,
- background: [0.1, 0.1, 0.1],
- formatted: true
-});
-
-var mouse = position(pixels.canvas);
+function keyword2hsv(args) {
+ return rgb2hsv(keyword2rgb(args));
+}
-var isSpectator = false;
-var start = performance.now();
-var food = [];
-var foodConsumed = [];
-var walls = [];
-var wall_map = {};
-var row, column, rand;
+function keyword2hwb(args) {
+ return rgb2hwb(keyword2rgb(args));
+}
-var name2idx = function (name) {
- var names = settings.player_color_names;
- for (var idx=0; idx < names.length; idx++) {
- if (names[idx] === name) {
- return idx;
- }
- }
-};
+function keyword2cmyk(args) {
+ return rgb2cmyk(keyword2rgb(args));
+}
-var color2idx = function (color) {
- var colors = settings.player_colors;
- var value = color.join(',');
- for (var idx=0; idx < colors.length; idx++) {
- if (colors[idx].join(',') === value) {
- return idx;
- }
- }
-};
+function keyword2lab(args) {
+ return rgb2lab(keyword2rgb(args));
+}
-var color2name = function (color) {
- var idx = color2idx(color);
- return settings.player_color_names[idx];
-};
+function keyword2xyz(args) {
+ return rgb2xyz(keyword2rgb(args));
+}
-var Food = function (settings) {
- if (!(this instanceof Food)) {
- return new Food();
- }
- this.id = settings.id;
- this.position = settings.position;
- this.color = settings.color;
- return this;
-};
-
-var Wall = function (settings) {
- if (!(this instanceof Wall)) {
- return new Wall();
- }
- this.position = settings.position;
- this.color = settings.color;
- return this;
+var cssKeywords = {
+ aliceblue: [240,248,255],
+ antiquewhite: [250,235,215],
+ aqua: [0,255,255],
+ aquamarine: [127,255,212],
+ azure: [240,255,255],
+ beige: [245,245,220],
+ bisque: [255,228,196],
+ black: [0,0,0],
+ blanchedalmond: [255,235,205],
+ blue: [0,0,255],
+ blueviolet: [138,43,226],
+ brown: [165,42,42],
+ burlywood: [222,184,135],
+ cadetblue: [95,158,160],
+ chartreuse: [127,255,0],
+ chocolate: [210,105,30],
+ coral: [255,127,80],
+ cornflowerblue: [100,149,237],
+ cornsilk: [255,248,220],
+ crimson: [220,20,60],
+ cyan: [0,255,255],
+ darkblue: [0,0,139],
+ darkcyan: [0,139,139],
+ darkgoldenrod: [184,134,11],
+ darkgray: [169,169,169],
+ darkgreen: [0,100,0],
+ darkgrey: [169,169,169],
+ darkkhaki: [189,183,107],
+ darkmagenta: [139,0,139],
+ darkolivegreen: [85,107,47],
+ darkorange: [255,140,0],
+ darkorchid: [153,50,204],
+ darkred: [139,0,0],
+ darksalmon: [233,150,122],
+ darkseagreen: [143,188,143],
+ darkslateblue: [72,61,139],
+ darkslategray: [47,79,79],
+ darkslategrey: [47,79,79],
+ darkturquoise: [0,206,209],
+ darkviolet: [148,0,211],
+ deeppink: [255,20,147],
+ deepskyblue: [0,191,255],
+ dimgray: [105,105,105],
+ dimgrey: [105,105,105],
+ dodgerblue: [30,144,255],
+ firebrick: [178,34,34],
+ floralwhite: [255,250,240],
+ forestgreen: [34,139,34],
+ fuchsia: [255,0,255],
+ gainsboro: [220,220,220],
+ ghostwhite: [248,248,255],
+ gold: [255,215,0],
+ goldenrod: [218,165,32],
+ gray: [128,128,128],
+ green: [0,128,0],
+ greenyellow: [173,255,47],
+ grey: [128,128,128],
+ honeydew: [240,255,240],
+ hotpink: [255,105,180],
+ indianred: [205,92,92],
+ indigo: [75,0,130],
+ ivory: [255,255,240],
+ khaki: [240,230,140],
+ lavender: [230,230,250],
+ lavenderblush: [255,240,245],
+ lawngreen: [124,252,0],
+ lemonchiffon: [255,250,205],
+ lightblue: [173,216,230],
+ lightcoral: [240,128,128],
+ lightcyan: [224,255,255],
+ lightgoldenrodyellow: [250,250,210],
+ lightgray: [211,211,211],
+ lightgreen: [144,238,144],
+ lightgrey: [211,211,211],
+ lightpink: [255,182,193],
+ lightsalmon: [255,160,122],
+ lightseagreen: [32,178,170],
+ lightskyblue: [135,206,250],
+ lightslategray: [119,136,153],
+ lightslategrey: [119,136,153],
+ lightsteelblue: [176,196,222],
+ lightyellow: [255,255,224],
+ lime: [0,255,0],
+ limegreen: [50,205,50],
+ linen: [250,240,230],
+ magenta: [255,0,255],
+ maroon: [128,0,0],
+ mediumaquamarine: [102,205,170],
+ mediumblue: [0,0,205],
+ mediumorchid: [186,85,211],
+ mediumpurple: [147,112,219],
+ mediumseagreen: [60,179,113],
+ mediumslateblue: [123,104,238],
+ mediumspringgreen: [0,250,154],
+ mediumturquoise: [72,209,204],
+ mediumvioletred: [199,21,133],
+ midnightblue: [25,25,112],
+ mintcream: [245,255,250],
+ mistyrose: [255,228,225],
+ moccasin: [255,228,181],
+ navajowhite: [255,222,173],
+ navy: [0,0,128],
+ oldlace: [253,245,230],
+ olive: [128,128,0],
+ olivedrab: [107,142,35],
+ orange: [255,165,0],
+ orangered: [255,69,0],
+ orchid: [218,112,214],
+ palegoldenrod: [238,232,170],
+ palegreen: [152,251,152],
+ paleturquoise: [175,238,238],
+ palevioletred: [219,112,147],
+ papayawhip: [255,239,213],
+ peachpuff: [255,218,185],
+ peru: [205,133,63],
+ pink: [255,192,203],
+ plum: [221,160,221],
+ powderblue: [176,224,230],
+ purple: [128,0,128],
+ rebeccapurple: [102, 51, 153],
+ red: [255,0,0],
+ rosybrown: [188,143,143],
+ royalblue: [65,105,225],
+ saddlebrown: [139,69,19],
+ salmon: [250,128,114],
+ sandybrown: [244,164,96],
+ seagreen: [46,139,87],
+ seashell: [255,245,238],
+ sienna: [160,82,45],
+ silver: [192,192,192],
+ skyblue: [135,206,235],
+ slateblue: [106,90,205],
+ slategray: [112,128,144],
+ slategrey: [112,128,144],
+ snow: [255,250,250],
+ springgreen: [0,255,127],
+ steelblue: [70,130,180],
+ tan: [210,180,140],
+ teal: [0,128,128],
+ thistle: [216,191,216],
+ tomato: [255,99,71],
+ turquoise: [64,224,208],
+ violet: [238,130,238],
+ wheat: [245,222,179],
+ white: [255,255,255],
+ whitesmoke: [245,245,245],
+ yellow: [255,255,0],
+ yellowgreen: [154,205,50]
};
-var Player = function (settings, dimness) {
- if (!(this instanceof Player)) {
- return new Player();
- }
- this.id = settings.id;
- this.position = settings.position;
- this.positionInSync = true;
- this.color = settings.color;
- this.motion_auto = settings.motion_auto;
- this.motion_direction = settings.motion_direction;
- this.motion_speed_limit = settings.motion_speed_limit;
- this.motion_timestamp = settings.motion_timestamp;
- this.score = settings.score;
- this.payoff = settings.payoff;
- this.name = settings.name;
- this.identity_visible = settings.identity_visible;
- this.dimness = dimness;
- return this;
-};
+var reverseKeywords = {};
+for (var key in cssKeywords) {
+ reverseKeywords[JSON.stringify(cssKeywords[key])] = key;
+}
-Player.prototype.move = function(direction) {
- function _hasWall(position) {
- return wall_map[[position[1], position[0]]] !== undefined;
- }
- this.motion_direction = direction;
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
- var ts = performance.now() - start,
- waitTime = 1000 / this.motion_speed_limit;
+var isBuffer = __webpack_require__(18);
+var toString = Object.prototype.toString;
- if (ts > this.motion_timestamp + waitTime) {
- var newPosition = this.position.slice();
+/**
+ * Get the native `typeof` a value.
+ *
+ * @param {*} `val`
+ * @return {*} Native javascript type
+ */
- switch (direction) {
- case "up":
- if (this.position[0] > 0) {
- newPosition[0] -= 1;
- }
- break;
+module.exports = function kindOf(val) {
+ // primitivies
+ if (typeof val === 'undefined') {
+ return 'undefined';
+ }
+ if (val === null) {
+ return 'null';
+ }
+ if (val === true || val === false || val instanceof Boolean) {
+ return 'boolean';
+ }
+ if (typeof val === 'string' || val instanceof String) {
+ return 'string';
+ }
+ if (typeof val === 'number' || val instanceof Number) {
+ return 'number';
+ }
- case "down":
- if (this.position[0] < settings.rows - 1) {
- newPosition[0] += 1;
- }
- break;
+ // functions
+ if (typeof val === 'function' || val instanceof Function) {
+ return 'function';
+ }
- case "left":
- if (this.position[1] > 0) {
- newPosition[1] -= 1;
- }
- break;
+ // array
+ if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) {
+ return 'array';
+ }
- case "right":
- if (this.position[1] < settings.columns - 1) {
- newPosition[1] += 1;
- }
- break;
+ // check for instances of RegExp and Date before calling `toString`
+ if (val instanceof RegExp) {
+ return 'regexp';
+ }
+ if (val instanceof Date) {
+ return 'date';
+ }
- default:
- console.log("Direction not recognized.");
- }
+ // other objects
+ var type = toString.call(val);
- if (!_hasWall(newPosition) && (!players.isPlayerAt(newPosition) || settings.player_overlap)) {
- this.position = newPosition;
- this.motion_timestamp = ts;
- return true;
- }
+ if (type === '[object RegExp]') {
+ return 'regexp';
+ }
+ if (type === '[object Date]') {
+ return 'date';
+ }
+ if (type === '[object Arguments]') {
+ return 'arguments';
+ }
+ if (type === '[object Error]') {
+ return 'error';
}
- return false;
-};
-var playerSet = (function () {
+ // buffer
+ if (isBuffer(val)) {
+ return 'buffer';
+ }
- var PlayerSet = function (settings) {
- if (!(this instanceof PlayerSet)) {
- return new PlayerSet(settings);
- }
+ // es6: Map, WeakMap, Set, WeakSet
+ if (type === '[object Set]') {
+ return 'set';
+ }
+ if (type === '[object WeakSet]') {
+ return 'weakset';
+ }
+ if (type === '[object Map]') {
+ return 'map';
+ }
+ if (type === '[object WeakMap]') {
+ return 'weakmap';
+ }
+ if (type === '[object Symbol]') {
+ return 'symbol';
+ }
- this._players = {};
- this.ego_id = settings.ego_id;
- };
+ // typed arrays
+ if (type === '[object Int8Array]') {
+ return 'int8array';
+ }
+ if (type === '[object Uint8Array]') {
+ return 'uint8array';
+ }
+ if (type === '[object Uint8ClampedArray]') {
+ return 'uint8clampedarray';
+ }
+ if (type === '[object Int16Array]') {
+ return 'int16array';
+ }
+ if (type === '[object Uint16Array]') {
+ return 'uint16array';
+ }
+ if (type === '[object Int32Array]') {
+ return 'int32array';
+ }
+ if (type === '[object Uint32Array]') {
+ return 'uint32array';
+ }
+ if (type === '[object Float32Array]') {
+ return 'float32array';
+ }
+ if (type === '[object Float64Array]') {
+ return 'float64array';
+ }
- PlayerSet.prototype.isPlayerAt = function (position) {
- var id, player;
+ // must be a plain object
+ return 'object';
+};
- for (id in this._players) {
- if (this._players.hasOwnProperty(id)) {
- player = this._players[id];
- if (positionsAreEqual(position, player.position)) {
- return true;
- }
- }
- }
- return false;
- };
- PlayerSet.prototype.drawToGrid = function (grid) {
- var positions = [],
- idx,
- player,
- id,
- minScore,
- maxScore,
- d,
- color,
- player_color;
- if (settings.score_visible) {
- minScore = this.minScore();
- maxScore = this.maxScore();
- }
+/***/ }),
+/* 18 */
+/***/ (function(module, exports) {
- for (id in this._players) {
- if (this._players.hasOwnProperty(id)) {
- player = this._players[id];
- /* It's unlikely that auto motion will keep identical pace to server-side auto-motion */
- /* this should be implemented either all on server or all on client */
- if (player.motion_auto) {
- player.move(player.motion_direction);
- }
- if (id === this.ego_id || settings.others_visible) {
- player_color = settings.player_colors[name2idx(player.color)];
- if (player.identity_visible) {
- color = player_color;
- } else {
- color = (id === this.ego_id) ? Color.rgb(player_color).desaturate(0.6).rgb().array() : INVISIBLE_COLOR;
- }
- if (settings.score_visible) {
- if (maxScore-minScore > 0) {
- d = 0.75 * (1 - (player.score-minScore)/(maxScore-minScore));
- } else {
- d = 0.375;
- }
- color = Color.rgb(player_color).desaturate(d).rgb().array();
- } else {
- color = player_color;
- }
- var texture = 0;
- if (settings.use_identicons) {
- texture = parseInt(id, 10);
- }
- grid.plot(player.position[1], player.position[0], color, texture);
- if (id === this.ego_id) {
- store.set("color", color2name(color));
- }
- }
- }
- }
- };
+/*!
+ * Determine if an object is a Buffer
+ *
+ * @author Feross Aboukhadijeh
+ * @license MIT
+ */
- PlayerSet.prototype.nearest = function (row, column) {
- var distances = [],
- distance,
- player,
- id;
+// The _isBuffer check is for Safari 5-7 support, because it's missing
+// Object.prototype.constructor. Remove this eventually
+module.exports = function (obj) {
+ return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
+}
- for (id in this._players) {
- if (this._players.hasOwnProperty(id)) {
- player = this._players[id];
- if (player.hasOwnProperty('position')) {
- distance = Math.abs(row - player.position[0]) + Math.abs(column - player.position[1]);
- distances.push({"player": player, "distance": distance});
- }
- }
- }
+function isBuffer (obj) {
+ return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
+}
- distances.sort(function (a, b) {
- return a.distance - b.distance;
- });
+// For Node v0.10 support. Remove this eventually.
+function isSlowBuffer (obj) {
+ return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
+}
- return distances[0].player;
- };
- PlayerSet.prototype.ego = function () {
- return this.get(this.ego_id);
- };
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
- PlayerSet.prototype.get = function (id) {
- return this._players[id];
- };
+var lodash = __webpack_require__(20);
+var isarray = __webpack_require__(5);
+var isnumber = __webpack_require__(3);
+var isstring = __webpack_require__(4);
+var parse = __webpack_require__(2);
- PlayerSet.prototype.count = function () {
- return Object.keys(this._players).length;
- };
+function convert(data) {
+ data = isarray(data[0]) && data[0].length !== 3 ? lodash.flatten(data, 1) : data;
- PlayerSet.prototype.update = function (allPlayersData) {
- var freshPlayerData,
- existingPlayer,
- i;
+ if (isnumber(data[0])) {
+ data = data.map(function(d) {
+ return [ d, d, d ];
+ });
+ }
- for (i = 0; i < allPlayersData.length; i++) {
- freshPlayerData = allPlayersData[i];
- existingPlayer = this._players[freshPlayerData.id];
- if (existingPlayer && existingPlayer.id === this.ego_id) {
+ if (isstring(data[0])) {
+ data = data.map(function(d) {
+ return parse(d).rgb.map(function(c) {
+ return c / 255;
+ });
+ });
+ }
- /* Don't override current player motion timestamp */
- freshPlayerData.motion_timestamp = existingPlayer.motion_timestamp;
+ return data;
+}
- // Only override position from server if tremble is enabled,
- // or if we know the Player's position is out of sync with the server.
- // Otherwise, the ego player's motion is constantly jittery.
- if (settings.motion_tremble_rate === 0 && existingPlayer.positionInSync) {
- freshPlayerData.position = existingPlayer.position;
- } else {
- console.log("Overriding position from server!");
- }
- }
- var last_dimness = 1;
- if (this._players[freshPlayerData.id] !== undefined) {
- last_dimness = this._players[freshPlayerData.id].dimness;
- }
- this._players[freshPlayerData.id] = new Player(freshPlayerData, last_dimness);
- }
- };
+module.exports = convert;
- PlayerSet.prototype.startScheduledAutosyncOfEgoPosition = function () {
- var self = this;
- setInterval(function () {
- var ego = self.ego();
- if (ego) {
- ego.positionInSync = false;
- console.log("Scheduled marking of (" + ego.id + ") as out of sync with server.");
- }
- }, 5000);
- };
- PlayerSet.prototype.maxScore = function () {
- var id,
- maxScore = 0;
- for (id in this._players) {
- if (this._players[id].score > maxScore) {
- maxScore = this._players[id].score;
- }
- }
- return maxScore;
- };
+/***/ }),
+/* 20 */
+/***/ (function(module, exports, __webpack_require__) {
- PlayerSet.prototype.minScore = function () {
- var id,
- minScore = Infinity;
- for (id in this._players) {
- if (this._players[id].score < minScore) {
- minScore = this._players[id].score;
- }
- }
- return minScore;
- };
-
- PlayerSet.prototype.each = function (callback) {
- var i = 0;
- for (var id in this._players) {
- if (this._players.hasOwnProperty(id)) {
- callback(i, this._players[id]);
- i++;
- }
- }
- };
+/* WEBPACK VAR INJECTION */(function(global, module) {var __WEBPACK_AMD_DEFINE_RESULT__;/**
+ * @license
+ * Lodash
+ * Copyright JS Foundation and other contributors
+ * Released under MIT license
+ * Based on Underscore.js 1.8.3
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+;(function() {
- PlayerSet.prototype.group_scores = function () {
- var group_scores = {};
+ /** Used as a safe reference for `undefined` in pre-ES5 environments. */
+ var undefined;
- this.each(function (i, player) {
- var color_name = player.color;
- var cur_score = group_scores[color_name] || 0;
- group_scores[color_name] = cur_score + Math.round(player.score);
- });
+ /** Used as the semantic version number. */
+ var VERSION = '4.17.4';
- var group_order = Object.keys(group_scores).sort(function (a, b) {
- return group_scores[a] > group_scores[b] ? -1 : (group_scores[a] < group_scores[b] ? 1 : 0);
- });
+ /** Used as the size to enable large array optimizations. */
+ var LARGE_ARRAY_SIZE = 200;
- return group_order.map(function(color_name) {
- return {name: color_name, score: group_scores[color_name]};
- });
- };
+ /** Error message constants. */
+ var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',
+ FUNC_ERROR_TEXT = 'Expected a function';
- PlayerSet.prototype.player_scores = function () {
- var player_order = [];
+ /** Used to stand-in for `undefined` hash values. */
+ var HASH_UNDEFINED = '__lodash_hash_undefined__';
- this.each(function(i, player) {
- player_order.push({id: player.id, name: player.name, score:player.score});
- });
+ /** Used as the maximum memoize cache size. */
+ var MAX_MEMOIZE_SIZE = 500;
- player_order = player_order.sort(function (a, b) {
- return a.score > b.score ? -1 : (a.score < b.score ? 1 : 0);
- });
+ /** Used as the internal argument placeholder. */
+ var PLACEHOLDER = '__lodash_placeholder__';
- return player_order;
- };
+ /** Used to compose bitmasks for cloning. */
+ var CLONE_DEEP_FLAG = 1,
+ CLONE_FLAT_FLAG = 2,
+ CLONE_SYMBOLS_FLAG = 4;
- return PlayerSet;
-}());
+ /** Used to compose bitmasks for value comparisons. */
+ var COMPARE_PARTIAL_FLAG = 1,
+ COMPARE_UNORDERED_FLAG = 2;
-var GUSocket = (function () {
- var makeSocket = function (endpoint, channel, tolerance) {
- var ws_scheme = (window.location.protocol === "https:") ? 'wss://' : 'ws://',
- app_root = ws_scheme + location.host + '/',
- socket;
+ /** Used to compose bitmasks for function metadata. */
+ var WRAP_BIND_FLAG = 1,
+ WRAP_BIND_KEY_FLAG = 2,
+ WRAP_CURRY_BOUND_FLAG = 4,
+ WRAP_CURRY_FLAG = 8,
+ WRAP_CURRY_RIGHT_FLAG = 16,
+ WRAP_PARTIAL_FLAG = 32,
+ WRAP_PARTIAL_RIGHT_FLAG = 64,
+ WRAP_ARY_FLAG = 128,
+ WRAP_REARG_FLAG = 256,
+ WRAP_FLIP_FLAG = 512;
- socket = new ReconnectingWebSocket(
- app_root + endpoint + "?channel=" + channel + "&tolerance=" + tolerance
- );
- socket.debug = true;
+ /** Used as default options for `_.truncate`. */
+ var DEFAULT_TRUNC_LENGTH = 30,
+ DEFAULT_TRUNC_OMISSION = '...';
- return socket;
- };
+ /** Used to detect hot functions by number of calls within a span of milliseconds. */
+ var HOT_COUNT = 800,
+ HOT_SPAN = 16;
- var dispatch = function (self, event) {
- var marker = self.broadcastChannel + ':';
- if (event.data.indexOf(marker) !== 0) {
- console.log(
- "Message was not on channel " + self.broadcastChannel + ". Ignoring.");
- return;
- }
- var msg = JSON.parse(event.data.substring(marker.length));
+ /** Used to indicate the type of lazy iteratees. */
+ var LAZY_FILTER_FLAG = 1,
+ LAZY_MAP_FLAG = 2,
+ LAZY_WHILE_FLAG = 3;
- var callback = self.callbackMap[msg.type];
- if (typeof callback !== 'undefined') {
- callback(msg);
- } else {
- console.log("Unrecognized message type " + msg.type + ' from backend.');
- }
- };
+ /** Used as references for various `Number` constants. */
+ var INFINITY = 1 / 0,
+ MAX_SAFE_INTEGER = 9007199254740991,
+ MAX_INTEGER = 1.7976931348623157e+308,
+ NAN = 0 / 0;
- /*
- * Public API
- */
- var Socket = function (settings) {
- if (!(this instanceof Socket)) {
- return new Socket(settings);
- }
+ /** Used as references for the maximum length and index of an array. */
+ var MAX_ARRAY_LENGTH = 4294967295,
+ MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
+ HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
- var self = this,
- isOpen = $.Deferred(),
- tolerance = typeof(settings.lagTolerance) !== 'undefined' ? settings.lagTolerance : 0.1;
+ /** Used to associate wrap methods with their bit flags. */
+ var wrapFlags = [
+ ['ary', WRAP_ARY_FLAG],
+ ['bind', WRAP_BIND_FLAG],
+ ['bindKey', WRAP_BIND_KEY_FLAG],
+ ['curry', WRAP_CURRY_FLAG],
+ ['curryRight', WRAP_CURRY_RIGHT_FLAG],
+ ['flip', WRAP_FLIP_FLAG],
+ ['partial', WRAP_PARTIAL_FLAG],
+ ['partialRight', WRAP_PARTIAL_RIGHT_FLAG],
+ ['rearg', WRAP_REARG_FLAG]
+ ];
- this.broadcastChannel = settings.broadcast;
- this.controlChannel = settings.control;
- this.callbackMap = settings.callbackMap;
+ /** `Object#toString` result references. */
+ var argsTag = '[object Arguments]',
+ arrayTag = '[object Array]',
+ asyncTag = '[object AsyncFunction]',
+ boolTag = '[object Boolean]',
+ dateTag = '[object Date]',
+ domExcTag = '[object DOMException]',
+ errorTag = '[object Error]',
+ funcTag = '[object Function]',
+ genTag = '[object GeneratorFunction]',
+ mapTag = '[object Map]',
+ numberTag = '[object Number]',
+ nullTag = '[object Null]',
+ objectTag = '[object Object]',
+ promiseTag = '[object Promise]',
+ proxyTag = '[object Proxy]',
+ regexpTag = '[object RegExp]',
+ setTag = '[object Set]',
+ stringTag = '[object String]',
+ symbolTag = '[object Symbol]',
+ undefinedTag = '[object Undefined]',
+ weakMapTag = '[object WeakMap]',
+ weakSetTag = '[object WeakSet]';
+ var arrayBufferTag = '[object ArrayBuffer]',
+ dataViewTag = '[object DataView]',
+ float32Tag = '[object Float32Array]',
+ float64Tag = '[object Float64Array]',
+ int8Tag = '[object Int8Array]',
+ int16Tag = '[object Int16Array]',
+ int32Tag = '[object Int32Array]',
+ uint8Tag = '[object Uint8Array]',
+ uint8ClampedTag = '[object Uint8ClampedArray]',
+ uint16Tag = '[object Uint16Array]',
+ uint32Tag = '[object Uint32Array]';
- this.socket = makeSocket(
- settings.endpoint, this.broadcastChannel, tolerance);
+ /** Used to match empty string literals in compiled template source. */
+ var reEmptyStringLeading = /\b__p \+= '';/g,
+ reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+ reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
- this.socket.onmessage = function (event) {
- dispatch(self, event);
- };
- };
+ /** Used to match HTML entities and HTML characters. */
+ var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
+ reUnescapedHtml = /[&<>"']/g,
+ reHasEscapedHtml = RegExp(reEscapedHtml.source),
+ reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
- Socket.prototype.open = function () {
- var isOpen = $.Deferred();
+ /** Used to match template delimiters. */
+ var reEscape = /<%-([\s\S]+?)%>/g,
+ reEvaluate = /<%([\s\S]+?)%>/g,
+ reInterpolate = /<%=([\s\S]+?)%>/g;
- this.socket.onopen = function (event) {
- isOpen.resolve();
- };
+ /** Used to match property names within property paths. */
+ var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
+ reIsPlainProp = /^\w*$/,
+ reLeadingDot = /^\./,
+ rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
- return isOpen;
- };
+ /**
+ * Used to match `RegExp`
+ * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+ */
+ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
+ reHasRegExpChar = RegExp(reRegExpChar.source);
- Socket.prototype.send = function (data) {
- var msg = JSON.stringify(data),
- channel = this.controlChannel;
+ /** Used to match leading and trailing whitespace. */
+ var reTrim = /^\s+|\s+$/g,
+ reTrimStart = /^\s+/,
+ reTrimEnd = /\s+$/;
- console.log("Sending message to the " + channel + " channel: " + msg);
- this.socket.send(channel + ':' + msg);
- };
+ /** Used to match wrap detail comments. */
+ var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
+ reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
+ reSplitDetails = /,? & /;
- Socket.prototype.broadcast = function (data) {
- var msg = JSON.stringify(data),
- channel = this.broadcastChannel;
+ /** Used to match words composed of alphanumeric characters. */
+ var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
- console.log("Broadcasting message to the " + channel + " channel: " + msg);
- this.socket.send(channel + ':' + msg);
- };
+ /** Used to match backslashes in property paths. */
+ var reEscapeChar = /\\(\\)?/g;
- return Socket;
-}());
+ /**
+ * Used to match
+ * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).
+ */
+ var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
-// ego will be updated on page load
-var players = playerSet({'ego_id': undefined});
+ /** Used to match `RegExp` flags from their coerced string values. */
+ var reFlags = /\w*$/;
-pixels.canvas.style.marginLeft = window.innerWidth * 0.03 / 2 + "px";
-pixels.canvas.style.marginTop = window.innerHeight * 0.04 / 2 + "px";
-document.body.style.transition = "0.3s all";
-document.body.style.background = "#ffffff";
-
-var startTime = performance.now();
+ /** Used to detect bad signed hexadecimal string values. */
+ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
-pixels.frame(function() {
- // Update the background.
- var ego = players.ego(),
- w = getWindowPosition(),
- limitVisibility,
- dimness,
- rescaling,
- i, j, x, y;
+ /** Used to detect binary string values. */
+ var reIsBinary = /^0b[01]+$/i;
- var section = new Section(background, w.left, w.top);
+ /** Used to detect host constructors (Safari). */
+ var reIsHostCtor = /^\[object .+?Constructor\]$/;
- // Animate background for each visible cell
- section.map(function(x, y, color) {
- var newColor = animateColor(color);
- background[coordsToIdx(x, y, settings.columns)] = newColor;
- return newColor;
- });
+ /** Used to detect octal string values. */
+ var reIsOctal = /^0o[0-7]+$/i;
- for (i = 0; i < food.length; i++) {
- // Players digest the food.
- var cur_food = food[i];
- if (players.isPlayerAt(cur_food.position)) {
- foodConsumed.push(food.splice(i, 1));
- } else {
- if (settings.food_visible) {
- section.plot(cur_food.position[1], cur_food.position[0], cur_food.color);
- }
- }
- }
+ /** Used to detect unsigned integer values. */
+ var reIsUint = /^(?:0|[1-9]\d*)$/;
- // Draw the players:
- players.drawToGrid(section);
+ /** Used to match Latin Unicode letters (excluding mathematical operators). */
+ var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
- // Add the Gaussian mask.
- var elapsedTime = performance.now() - startTime;
- var visibilityNow = clamp(
- (settings.visibility * elapsedTime) / (1000 * settings.visibility_ramp_time),
- 3,
- settings.visibility
- );
- if (settings.highlightEgo) {
- visibilityNow = Math.min(visibilityNow, 4);
- }
- var g = gaussian(0, Math.pow(visibilityNow, 2));
- rescaling = 1 / g.pdf(0);
+ /** Used to ensure capturing order of template delimiters. */
+ var reNoMatch = /($^)/;
- if (typeof ego !== "undefined") {
- x = ego.position[1];
- y = ego.position[0];
- } else {
- x = 1e100;
- y = 1e100;
- }
- section.map(function(i, j, color) {
- var newColor;
- // Draw walls
- if (settings.walls_visible) {
- color = wall_map[[i,j]] || color;
- }
- // Add Blur
- players.each(function (i, player) {
- dimness = g.pdf(distance(y, x, player.position[0], player.position[1])) * rescaling;
- player["dimness"] = dimness;
- });
- newColor = color;
- if (!isSpectator) {
- dimness = g.pdf(distance(x, y, i, j)) * rescaling;
- newColor = [
- color[0] * dimness,
- color[1] * dimness,
- color[2] * dimness
- ];
- }
- return newColor;
- });
- pixels.update(section.data, section.textures);
-});
+ /** Used to match unescaped characters in compiled string literals. */
+ var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
-function clamp(val, min, max) {
- return Math.max(min, Math.min(max, val));
-}
+ /** Used to compose unicode character classes. */
+ var rsAstralRange = '\\ud800-\\udfff',
+ rsComboMarksRange = '\\u0300-\\u036f',
+ reComboHalfMarksRange = '\\ufe20-\\ufe2f',
+ rsComboSymbolsRange = '\\u20d0-\\u20ff',
+ rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
+ rsDingbatRange = '\\u2700-\\u27bf',
+ rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
+ rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
+ rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
+ rsPunctuationRange = '\\u2000-\\u206f',
+ rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000',
+ rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
+ rsVarRange = '\\ufe0e\\ufe0f',
+ rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
-function distance(x, y, xx, yy) {
- return Math.sqrt((xx - x) * (xx - x) + (yy - y) * (yy - y));
-}
+ /** Used to compose unicode capture groups. */
+ var rsApos = "['\u2019]",
+ rsAstral = '[' + rsAstralRange + ']',
+ rsBreak = '[' + rsBreakRange + ']',
+ rsCombo = '[' + rsComboRange + ']',
+ rsDigits = '\\d+',
+ rsDingbat = '[' + rsDingbatRange + ']',
+ rsLower = '[' + rsLowerRange + ']',
+ rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
+ rsFitz = '\\ud83c[\\udffb-\\udfff]',
+ rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
+ rsNonAstral = '[^' + rsAstralRange + ']',
+ rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
+ rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
+ rsUpper = '[' + rsUpperRange + ']',
+ rsZWJ = '\\u200d';
-function arraysEqual(arr1, arr2) {
- for (var i = arr1.length; i--; ) {
- if (arr1[i] !== arr2[i]) {
- return false;
- }
- }
- return true;
-}
+ /** Used to compose unicode regexes. */
+ var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',
+ rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',
+ rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
+ rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
+ reOptMod = rsModifier + '?',
+ rsOptVar = '[' + rsVarRange + ']?',
+ rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
+ rsOrdLower = '\\d*(?:(?:1st|2nd|3rd|(?![123])\\dth)\\b)',
+ rsOrdUpper = '\\d*(?:(?:1ST|2ND|3RD|(?![123])\\dTH)\\b)',
+ rsSeq = rsOptVar + reOptMod + rsOptJoin,
+ rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
+ rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
-function arraySearch(arr, val) {
- for (var i = 0; i < arr.length; i++) {
- if (arraysEqual(arr[i], val)) {
- return i;
- }
- }
- return false;
-}
+ /** Used to match apostrophes. */
+ var reApos = RegExp(rsApos, 'g');
-function getRandomInt(min, max) {
- return Math.floor(Math.random() * (max - min + 1)) + min;
-}
+ /**
+ * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
+ * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
+ */
+ var reComboMark = RegExp(rsCombo, 'g');
-function getWindowPosition() {
- var ego = players.ego(),
- w = {
- left: 0,
- top: 0,
- columns: settings.window_columns,
- rows: settings.window_rows
- };
+ /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
+ var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
- if (typeof ego !== 'undefined') {
- w.left = clamp(
- ego.position[1] - Math.floor(settings.window_columns / 2),
- 0, settings.columns - settings.window_columns);
- w.top = clamp(
- ego.position[0] - Math.floor(settings.window_rows / 2),
- 0, settings.rows - settings.window_rows);
- }
- return w;
-}
+ /** Used to match complex or compound words. */
+ var reUnicodeWord = RegExp([
+ rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',
+ rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')',
+ rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower,
+ rsUpper + '+' + rsOptContrUpper,
+ rsOrdUpper,
+ rsOrdLower,
+ rsDigits,
+ rsEmoji
+ ].join('|'), 'g');
-function bindGameKeys(socket) {
- var directions = ["up", "down", "left", "right"],
- repeatDelayMS = 1000 / settings.motion_speed_limit,
- lastDirection = null,
- repeatIntervalId = null,
- highlightEgo = false;
+ /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
+ var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
- function moveInDir(direction) {
- var ego = players.ego();
- if (ego.move(direction) ) {
- var msg = {
- type: "move",
- player_id: ego.id,
- move: direction,
- timestamp: ego.motion_timestamp
- };
- socket.send(msg);
- }
- }
+ /** Used to detect strings that need a more robust regexp to match words. */
+ var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
- directions.forEach(function(direction) {
- Mousetrap.bind(
- direction,
- function(e) {
- e.preventDefault();
- if (direction === lastDirection) {
- return;
- }
+ /** Used to assign default `context` object properties. */
+ var contextProps = [
+ 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',
+ 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',
+ 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array',
+ 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
+ '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'
+ ];
- // New direction may be pressed before previous dir key is released
- if (repeatIntervalId) {
- clearInterval(repeatIntervalId);
- }
+ /** Used to make template sourceURLs easier to identify. */
+ var templateCounter = -1;
- moveInDir(direction); // Move once immediately so there's no lag
- lastDirection = direction;
- repeatIntervalId = setInterval(moveInDir, repeatDelayMS, direction);
- },
- 'keydown'
- );
+ /** Used to identify `toStringTag` values of typed arrays. */
+ var typedArrayTags = {};
+ typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+ typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+ typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+ typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+ typedArrayTags[uint32Tag] = true;
+ typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+ typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+ typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
+ typedArrayTags[errorTag] = typedArrayTags[funcTag] =
+ typedArrayTags[mapTag] = typedArrayTags[numberTag] =
+ typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
+ typedArrayTags[setTag] = typedArrayTags[stringTag] =
+ typedArrayTags[weakMapTag] = false;
- Mousetrap.bind(
- direction,
- function(e) {
- e.preventDefault();
- if (direction) {
- clearInterval(repeatIntervalId);
- lastDirection = null;
- }
- },
- "keyup"
- );
+ /** Used to identify `toStringTag` values supported by `_.clone`. */
+ var cloneableTags = {};
+ cloneableTags[argsTag] = cloneableTags[arrayTag] =
+ cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
+ cloneableTags[boolTag] = cloneableTags[dateTag] =
+ cloneableTags[float32Tag] = cloneableTags[float64Tag] =
+ cloneableTags[int8Tag] = cloneableTags[int16Tag] =
+ cloneableTags[int32Tag] = cloneableTags[mapTag] =
+ cloneableTags[numberTag] = cloneableTags[objectTag] =
+ cloneableTags[regexpTag] = cloneableTags[setTag] =
+ cloneableTags[stringTag] = cloneableTags[symbolTag] =
+ cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
+ cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
+ cloneableTags[errorTag] = cloneableTags[funcTag] =
+ cloneableTags[weakMapTag] = false;
- });
+ /** Used to map Latin Unicode letters to basic Latin letters. */
+ var deburredLetters = {
+ // Latin-1 Supplement block.
+ '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
+ '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
+ '\xc7': 'C', '\xe7': 'c',
+ '\xd0': 'D', '\xf0': 'd',
+ '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
+ '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
+ '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
+ '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i',
+ '\xd1': 'N', '\xf1': 'n',
+ '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
+ '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
+ '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
+ '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
+ '\xdd': 'Y', '\xfd': 'y', '\xff': 'y',
+ '\xc6': 'Ae', '\xe6': 'ae',
+ '\xde': 'Th', '\xfe': 'th',
+ '\xdf': 'ss',
+ // Latin Extended-A block.
+ '\u0100': 'A', '\u0102': 'A', '\u0104': 'A',
+ '\u0101': 'a', '\u0103': 'a', '\u0105': 'a',
+ '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C',
+ '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c',
+ '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd',
+ '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E',
+ '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e',
+ '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G',
+ '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g',
+ '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h',
+ '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I',
+ '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i',
+ '\u0134': 'J', '\u0135': 'j',
+ '\u0136': 'K', '\u0137': 'k', '\u0138': 'k',
+ '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L',
+ '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l',
+ '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N',
+ '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n',
+ '\u014c': 'O', '\u014e': 'O', '\u0150': 'O',
+ '\u014d': 'o', '\u014f': 'o', '\u0151': 'o',
+ '\u0154': 'R', '\u0156': 'R', '\u0158': 'R',
+ '\u0155': 'r', '\u0157': 'r', '\u0159': 'r',
+ '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S',
+ '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's',
+ '\u0162': 'T', '\u0164': 'T', '\u0166': 'T',
+ '\u0163': 't', '\u0165': 't', '\u0167': 't',
+ '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U',
+ '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u',
+ '\u0174': 'W', '\u0175': 'w',
+ '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y',
+ '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z',
+ '\u017a': 'z', '\u017c': 'z', '\u017e': 'z',
+ '\u0132': 'IJ', '\u0133': 'ij',
+ '\u0152': 'Oe', '\u0153': 'oe',
+ '\u0149': "'n", '\u017f': 's'
+ };
- Mousetrap.bind("space", function () {
- var msg = {
- type: "plant_food",
- player_id: players.ego().id,
- position: players.ego().position
- };
- socket.send(msg);
- });
+ /** Used to map characters to HTML entities. */
+ var htmlEscapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": '''
+ };
- if (settings.mutable_colors) {
- Mousetrap.bind('c', function () {
- var keys = settings.player_color_names,
- index = arraySearch(keys, players.ego().color),
- nextItem = keys[(index + 1) % keys.length],
- msg;
+ /** Used to map HTML entities to characters. */
+ var htmlUnescapes = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ ''': "'"
+ };
- players.ego().color = nextItem;
- msg = {
- type: "change_color",
- player_id: players.ego().id,
- color: players.ego().color
- };
- socket.send(msg);
- });
- }
+ /** Used to escape characters for inclusion in compiled string literals. */
+ var stringEscapes = {
+ '\\': '\\',
+ "'": "'",
+ '\n': 'n',
+ '\r': 'r',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
- if (settings.identity_signaling) {
- Mousetrap.bind("v", function () {
- var ego = players.ego(),
- msg;
+ /** Built-in method references without a dependency on `root`. */
+ var freeParseFloat = parseFloat,
+ freeParseInt = parseInt;
- ego.identity_visible = !ego.identity_visible;
- msg = {
- type: "toggle_visible",
- player_id: ego.id,
- identity_visible: ego.identity_visible
- };
- socket.send(msg);
- });
- }
+ /** Detect free variable `global` from Node.js. */
+ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
- if (settings.build_walls) {
- Mousetrap.bind("w", function () {
- var msg = {
- type: "build_wall",
- player_id: players.ego().id,
- position: players.ego().position
- };
- socket.send(msg);
- });
- }
+ /** Detect free variable `self`. */
+ var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
- Mousetrap.bind("h", function () {
- settings.highlightEgo = !settings.highlightEgo;
- });
-}
+ /** Used as a reference to the global object. */
+ var root = freeGlobal || freeSelf || Function('return this')();
-function chatName(player_id) {
- var ego = players.ego(),
- entry = "",
- id = parseInt(player_id) - 1,
- salt = $("#grid").data("identicon-salt"),
- fg = settings.player_colors[name2idx(players.get(player_id).color)].concat(1),
- bg,
- identicon,
- name,
- options;
+ /** Detect free variable `exports`. */
+ var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
- if (id === ego) {
- name = "You";
- } else if (settings.pseudonyms) {
- name = players.get(player_id).name;
- } else if (player_id % 1 === 0) {
- name = "Player " + player_id;
- } else {
- // Non-integer player_id
- return '' + player_id + ' ';
- }
+ /** Detect free variable `module`. */
+ var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
- fg = fg.map(function(x) { return x * 255; });
- bg = fg.map(function(x) { return (x * 0.66); });
- bg[3] = 255;
- options = {
- size: 10,
- foreground: fg,
- background: bg,
- format: 'svg'
- };
+ /** Detect the popular CommonJS extension `module.exports`. */
+ var moduleExports = freeModule && freeModule.exports === freeExports;
- identicon = new Identicon(md5(salt + id), options).toString();
- if (settings.use_identicons) {
- entry = entry + " ";
- }
- entry = entry + " " + name + " ";
- return entry;
-}
+ /** Detect free variable `process` from Node.js. */
+ var freeProcess = moduleExports && freeGlobal.process;
-function onChatMessage(msg) {
- var entry = chatName(msg.player_id);
- if (settings.spatial_chat && players.get(msg.player_id).dimness < settings.chat_visibility_threshold) {
- return;
- }
- $("#messages").append(($("").text(": " + msg.contents)).prepend(entry));
- $("#chatlog").scrollTop($("#chatlog")[0].scrollHeight);
-}
+ /** Used to access faster Node.js helpers. */
+ var nodeUtil = (function() {
+ try {
+ return freeProcess && freeProcess.binding && freeProcess.binding('util');
+ } catch (e) {}
+ }());
-function onColorChanged(msg) {
- store.set("color", msg.new_color);
- if (settings.spatial_chat && players.get(msg.player_id).dimness < settings.chat_visibility_threshold) {
- return;
- }
- pushMessage("Moderator: " + chatName(msg.player_id) + ' changed from team ' + msg.old_color + ' to team ' + msg.new_color + '.');
-}
+ /* Node.js helper references. */
+ var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,
+ nodeIsDate = nodeUtil && nodeUtil.isDate,
+ nodeIsMap = nodeUtil && nodeUtil.isMap,
+ nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
+ nodeIsSet = nodeUtil && nodeUtil.isSet,
+ nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
-function onMoveRejected(msg) {
- var offendingPlayerId = msg.player_id,
- ego = players.ego();
+ /*--------------------------------------------------------------------------*/
- if (ego && offendingPlayerId === ego.id) {
- ego.positionInSync = false;
- console.log("Marking your player (" + ego.id + ") as out of sync with server. Should sync on next state update");
+ /**
+ * Adds the key-value `pair` to `map`.
+ *
+ * @private
+ * @param {Object} map The map to modify.
+ * @param {Array} pair The key-value pair to add.
+ * @returns {Object} Returns `map`.
+ */
+ function addMapEntry(map, pair) {
+ // Don't return `map.set` because it's not chainable in IE 11.
+ map.set(pair[0], pair[1]);
+ return map;
}
-}
-function onDonationProcessed(msg) {
- var donor = players.get(msg.donor_id),
- recipient_id = msg.recipient_id,
- team_idx,
- donor_name,
- recipient_name,
- donated_points,
- received_points,
- entry;
-
- donor_name = chatName(msg.donor_id);
+ /**
+ * Adds `value` to `set`.
+ *
+ * @private
+ * @param {Object} set The set to modify.
+ * @param {*} value The value to add.
+ * @returns {Object} Returns `set`.
+ */
+ function addSetEntry(set, value) {
+ // Don't return `set.add` because it's not chainable in IE 11.
+ set.add(value);
+ return set;
+ }
- if (recipient_id === 'all') {
- recipient_name = 'All players ';
- } else if (recipient_id.indexOf('group:') === 0) {
- team_idx = +recipient_id.substring(6);
- recipient_name = 'Everyone in ' + settings.player_color_names[team_idx] + ' ';
- } else {
- recipient_name = chatName(recipient_id);
+ /**
+ * A faster alternative to `Function#apply`, this function invokes `func`
+ * with the `this` binding of `thisArg` and the arguments of `args`.
+ *
+ * @private
+ * @param {Function} func The function to invoke.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} args The arguments to invoke `func` with.
+ * @returns {*} Returns the result of `func`.
+ */
+ function apply(func, thisArg, args) {
+ switch (args.length) {
+ case 0: return func.call(thisArg);
+ case 1: return func.call(thisArg, args[0]);
+ case 2: return func.call(thisArg, args[0], args[1]);
+ case 3: return func.call(thisArg, args[0], args[1], args[2]);
}
+ return func.apply(thisArg, args);
+ }
- if (msg.amount === 1) {
- donated_points = msg.amount + ' point.';
- } else {
- donated_points = msg.amount + ' points.';
- }
+ /**
+ * A specialized version of `baseAggregator` for arrays.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
+ */
+ function arrayAggregator(array, setter, iteratee, accumulator) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
- if (msg.received === 1) {
- received_points = msg.received + ' point.';
- } else {
- received_points = msg.received + ' points.';
+ while (++index < length) {
+ var value = array[index];
+ setter(accumulator, value, iteratee(value), array);
}
+ return accumulator;
+ }
- entry = donor_name + " contributed " + donated_points + " " + recipient_name + " received " + received_points;
-
- $("#messages").append($(" ").html(entry));
- $("#chatlog").scrollTop($("#chatlog")[0].scrollHeight);
- $('#individual-donate, #group-donate').addClass('button-outline');
- $('#donate label').text($('#donate label').data('orig-text'));
- settings.donation_type = null;
-}
+ /**
+ * A specialized version of `_.forEach` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayEach(array, iteratee) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
-function updateDonationStatus(donation_is_active) {
- // If alternating donation/consumption rounds, announce round type
- if (settings.alternate_consumption_donation && (settings.donation_active !== donation_is_active)) {
- if (donation_is_active) {
- pushMessage("Moderator: Starting a donation round. Players cannot move, only donate.");
- } else {
- pushMessage("Moderator: Starting a consumption round. Players have to consume as much food as possible.");
+ while (++index < length) {
+ if (iteratee(array[index], index, array) === false) {
+ break;
+ }
}
+ return array;
}
- // Update donation status
- settings.donation_active = donation_is_active;
-}
-
-function onGameStateChange(msg) {
- var $donationButtons = $('#individual-donate, #group-donate, #public-donate, #ingroup-donate'),
- $timeElement = $("#time"),
- $loading = $('.grid-loading'),
- cur_wall,
- ego,
- state,
- j,
- k;
- performance.mark('state_start');
- if ($loading.is(':visible')) $loading.fadeOut();
+ /**
+ * A specialized version of `_.forEachRight` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayEachRight(array, iteratee) {
+ var length = array == null ? 0 : array.length;
- if (settings.paused_game) {
- $timeElement.html(0);
- return;
+ while (length--) {
+ if (iteratee(array[length], length, array) === false) {
+ break;
+ }
+ }
+ return array;
}
- // Update remaining time.
- $timeElement.html(Math.max(Math.round(msg.remaining_time), 0));
+ /**
+ * A specialized version of `_.every` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
+ */
+ function arrayEvery(array, predicate) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
- // Update round.
- if (settings.num_rounds > 1) {
- $("#round").html(msg.round + 1);
+ while (++index < length) {
+ if (!predicate(array[index], index, array)) {
+ return false;
+ }
+ }
+ return true;
}
- // Update players.
- state = JSON.parse(msg.grid);
- players.update(state.players);
- ego = players.ego();
-
- updateDonationStatus(state.donation_active);
+ /**
+ * A specialized version of `_.filter` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ */
+ function arrayFilter(array, predicate) {
+ var index = -1,
+ length = array == null ? 0 : array.length,
+ resIndex = 0,
+ result = [];
- // Update food.
- if (state.food !== undefined && state.food !== null) {
- food = [];
- for (j = 0; j < state.food.length; j++) {
- food.push(
- new Food({
- id: state.food[j].id,
- position: state.food[j].position,
- color: state.food[j].color
- })
- );
+ while (++index < length) {
+ var value = array[index];
+ if (predicate(value, index, array)) {
+ result[resIndex++] = value;
+ }
}
+ return result;
}
- // Update walls if they haven't been created yet.
- if (state.walls !== undefined && walls.length === 0) {
- for (k = 0; k < state.walls.length; k++) {
- cur_wall = state.walls[k];
- if (cur_wall instanceof Array) {
- cur_wall = {
- position: cur_wall,
- color: [0.5, 0.5, 0.5]
- };
+ /**
+ * A specialized version of `_.includes` for arrays without support for
+ * specifying an index to search from.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+ function arrayIncludes(array, value) {
+ var length = array == null ? 0 : array.length;
+ return !!length && baseIndexOf(array, value, 0) > -1;
+ }
+
+ /**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+ function arrayIncludesWith(array, value, comparator) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
+
+ while (++index < length) {
+ if (comparator(value, array[index])) {
+ return true;
}
- walls.push(
- new Wall({
- position: cur_wall.position,
- color: cur_wall.color
- })
- );
- wall_map[[cur_wall.position[1], cur_wall.position[0]]] = cur_wall.color;
}
+ return false;
}
- // If new walls have been added, draw them
- if (state.walls !== undefined && walls.length < state.walls.length) {
- for (k = walls.length; k < state.walls.length; k++) {
- cur_wall = state.walls[k];
- walls.push(
- new Wall({
- position: cur_wall.position,
- color: cur_wall.color
- })
- );
- wall_map[[cur_wall.position[1], cur_wall.position[0]]] = cur_wall.color;
+ /**
+ * A specialized version of `_.map` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+ function arrayMap(array, iteratee) {
+ var index = -1,
+ length = array == null ? 0 : array.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = iteratee(array[index], index, array);
}
+ return result;
}
- // Update displayed score, set donation info.
- if (ego !== undefined) {
- $("#score").html(Math.round(ego.score));
- $("#dollars").html(ego.payoff.toFixed(2));
- window.state = msg.grid;
- window.ego = ego.id;
- if (settings.donation_active &&
- ego.score >= settings.donation_amount &&
- players.count() > 1
- ) {
- $donationButtons.prop('disabled', false);
- } else {
- $('#donation-instructions').text('');
- $donationButtons.prop('disabled', true);
- }
- }
-}
+ /**
+ * Appends the elements of `values` to `array`.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to append.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayPush(array, values) {
+ var index = -1,
+ length = values.length,
+ offset = array.length;
-function addWall(msg) {
- var wall = msg.wall;
- if (wall) {
- walls.push(
- new Wall({
- position: wall.position,
- color: wall.color
- })
- );
- wall_map[[wall.position[1], wall.position[0]]] = wall.color;
+ while (++index < length) {
+ array[offset + index] = values[index];
+ }
+ return array;
}
-}
-function pushMessage(html) {
- $("#messages").append(($(" ").html(html)));
- $("#chatlog").scrollTop($("#chatlog")[0].scrollHeight);
-}
+ /**
+ * A specialized version of `_.reduce` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initAccum] Specify using the first element of `array` as
+ * the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+ function arrayReduce(array, iteratee, accumulator, initAccum) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
-function displayLeaderboards(msg, callback) {
- if (!settings.leaderboard_group && !settings.leaderboard_individual) {
- if (callback) {
- callback();
- }
- return;
- }
- var i;
- if (msg.type === 'new_round') {
- pushMessage("Moderator: the round " + msg.round + ' standings are…');
- } else {
- pushMessage("Moderator: the final standings are …");
- }
- if (settings.leaderboard_group) {
- if (settings.leaderboard_individual) {
- pushMessage('Group ');
+ if (initAccum && length) {
+ accumulator = array[++index];
}
- var group_scores = players.group_scores();
- var rgb_map = function (e) { return Math.round(e * 255); };
- for (i = 0; i < group_scores.length; i++) {
- var group = group_scores[i];
- var color = settings.player_colors[name2idx(group.name)].map(rgb_map);
- pushMessage('' + group.score + ' ');
+ while (++index < length) {
+ accumulator = iteratee(accumulator, array[index], index, array);
}
+ return accumulator;
}
- if (settings.leaderboard_individual) {
- if (settings.leaderboard_group) {
- pushMessage('Individual ');
+
+ /**
+ * A specialized version of `_.reduceRight` for arrays without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @param {boolean} [initAccum] Specify using the last element of `array` as
+ * the initial value.
+ * @returns {*} Returns the accumulated value.
+ */
+ function arrayReduceRight(array, iteratee, accumulator, initAccum) {
+ var length = array == null ? 0 : array.length;
+ if (initAccum && length) {
+ accumulator = array[--length];
}
- var player_scores = players.player_scores();
- var ego_id = players.ego_id;
- for (i = 0; i < player_scores.length; i++) {
- var player = player_scores[i];
- var player_name = chatName(player.id);
- pushMessage('' + Math.round(player.score) + ' ' + player_name + ' ');
+ while (length--) {
+ accumulator = iteratee(accumulator, array[length], length, array);
}
+ return accumulator;
}
- if (settings.leaderboard_time) {
- settings.paused_game = true;
- setTimeout(function () {
- settings.paused_game = false;
- if (callback) {
- callback();
- }
- }, 1000 * settings.leaderboard_time);
- } else if (callback) {
- callback();
- }
-}
-
-function gameOverHandler(player_id) {
- var callback;
- if (!isSpectator) {
- callback = function () {
- $("#dashboard").hide();
- $("#instructions").hide();
- $("#chat").hide();
- if (player_id) {
- window.location.href = "/questionnaire?participant_id=" + player_id;
- }
- };
- pixels.canvas.style.display = "none";
- }
- return function (msg) {
- $("#game-over").show();
- return displayLeaderboards(msg, callback);
- };
-}
-
-$(document).ready(function() {
- var player_id = dallinger.getUrlParameter('participant_id');
- isSpectator = typeof player_id === 'undefined';
- var socketSettings = {
- 'endpoint': 'chat',
- 'broadcast': CHANNEL,
- 'control': CONTROL_CHANNEL,
- 'lagTolerance': 0.001,
- 'callbackMap': {
- 'chat': onChatMessage,
- 'donation_processed': onDonationProcessed,
- 'color_changed': onColorChanged,
- 'state': onGameStateChange,
- 'new_round': displayLeaderboards,
- 'stop': gameOverHandler(player_id),
- 'wall_built': addWall,
- 'move_rejection': onMoveRejected
- }
- };
- var socket = new GUSocket(socketSettings);
-
- socket.open().done(function () {
- var data = {
- type: 'connect',
- player_id: isSpectator ? 'spectator' : player_id
- };
- socket.send(data);
- });
- players.ego_id = player_id;
- players.startScheduledAutosyncOfEgoPosition();
- $('#donate label').data('orig-text', $('#donate label').text());
+ /**
+ * A specialized version of `_.some` for arrays without support for iteratee
+ * shorthands.
+ *
+ * @private
+ * @param {Array} [array] The array to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
+ */
+ function arraySome(array, predicate) {
+ var index = -1,
+ length = array == null ? 0 : array.length;
- setInterval(function () {
- var delays = [],
- start_marks = performance.getEntriesByName('state_start', 'mark');
- for (var i = 0; i < start_marks.length; i++) {
- if (start_marks.length > i + 2) {
- delays.push(start_marks[i+1].startTime - start_marks[i].startTime);
+ while (++index < length) {
+ if (predicate(array[index], index, array)) {
+ return true;
}
}
- if (delays.length) {
- var average_delay = delays.reduce(function(sum, value){
- return sum + value;
- }, 0) / delays.length;
- console.log('Average delay between state updates: ' + average_delay + 'ms.');
- }
- }, 5000);
-
- // Append the canvas.
- $("#grid").append(pixels.canvas);
-
- // Opt out of the experiment.
- $("#opt-out").click(function() {
- window.location.href = "/questionnaire?participant_id=" + player_id;
- });
-
- if (isSpectator) {
- $(".for-players").hide();
+ return false;
}
- // Consent to the experiment.
- $("#go-to-experiment").click(function() {
- window.location.href = "/exp";
- });
-
- // Submit the questionnaire.
- $("#submit-questionnaire").click(function() {
- dallinger.submitResponses();
- });
-
- if (settings.show_grid) {
- pixels.canvas.style.display = "inline";
- }
+ /**
+ * Gets the size of an ASCII `string`.
+ *
+ * @private
+ * @param {string} string The string inspect.
+ * @returns {number} Returns the string size.
+ */
+ var asciiSize = baseProperty('length');
- if (settings.show_chatroom) {
- $("#chat form").show();
+ /**
+ * Converts an ASCII `string` to an array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function asciiToArray(string) {
+ return string.split('');
}
- var donateToClicked = function() {
- var w = getWindowPosition(),
- row = w.top + pixels2cells(mouse[1]),
- column = w.left + pixels2cells(mouse[0]),
- recipient = players.nearest(row, column),
- donor = players.ego(),
- amt = settings.donation_amount,
- recipient_id,
- msg;
+ /**
+ * Splits an ASCII `string` into an array of its words.
+ *
+ * @private
+ * @param {string} The string to inspect.
+ * @returns {Array} Returns the words of `string`.
+ */
+ function asciiWords(string) {
+ return string.match(reAsciiWord) || [];
+ }
- if (!settings.donation_active) {
- return;
- }
+ /**
+ * The base implementation of methods like `_.findKey` and `_.findLastKey`,
+ * without support for iteratee shorthands, which iterates over `collection`
+ * using `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @returns {*} Returns the found element or its key, else `undefined`.
+ */
+ function baseFindKey(collection, predicate, eachFunc) {
+ var result;
+ eachFunc(collection, function(value, key, collection) {
+ if (predicate(value, key, collection)) {
+ result = key;
+ return false;
+ }
+ });
+ return result;
+ }
- if (amt > donor.score) {
- return;
- }
+ /**
+ * The base implementation of `_.findIndex` and `_.findLastIndex` without
+ * support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {number} fromIndex The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseFindIndex(array, predicate, fromIndex, fromRight) {
+ var length = array.length,
+ index = fromIndex + (fromRight ? 1 : -1);
- if (settings.donation_type === 'individual') {
- recipient_id = recipient.id;
- } else if (settings.donation_type === 'group') {
- recipient_id = 'group:' + name2idx(recipient.color).toString();
- } else {
- return;
+ while ((fromRight ? index-- : ++index < length)) {
+ if (predicate(array[index], index, array)) {
+ return index;
+ }
}
+ return -1;
+ }
- if (recipient_id !== donor.id) {
- msg = {
- type: "donation_submitted",
- recipient_id: recipient_id,
- donor_id: donor.id,
- amount: amt
- };
- socket.send(msg);
- }
- };
+ /**
+ * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseIndexOf(array, value, fromIndex) {
+ return value === value
+ ? strictIndexOf(array, value, fromIndex)
+ : baseFindIndex(array, baseIsNaN, fromIndex);
+ }
- var donateToAll = function() {
- var donor = players.ego(),
- amt = settings.donation_amount,
- msg;
+ /**
+ * This function is like `baseIndexOf` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function baseIndexOfWith(array, value, fromIndex, comparator) {
+ var index = fromIndex - 1,
+ length = array.length;
- msg = {
- type: "donation_submitted",
- recipient_id: 'all',
- donor_id: donor.id,
- amount: amt
- };
- socket.send(msg);
- };
+ while (++index < length) {
+ if (comparator(array[index], value)) {
+ return index;
+ }
+ }
+ return -1;
+ }
- var donateToInGroup = function () {
- var donor = players.ego(),
- amt = settings.donation_amount,
- recipientId = 'group:' + name2idx(donor.color).toString(),
- msg;
+ /**
+ * The base implementation of `_.isNaN` without support for number objects.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ */
+ function baseIsNaN(value) {
+ return value !== value;
+ }
- msg = {
- type: "donation_submitted",
- recipient_id: recipientId,
- donor_id: donor.id,
- amount: amt
+ /**
+ * The base implementation of `_.mean` and `_.meanBy` without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {number} Returns the mean.
+ */
+ function baseMean(array, iteratee) {
+ var length = array == null ? 0 : array.length;
+ return length ? (baseSum(array, iteratee) / length) : NAN;
+ }
+
+ /**
+ * The base implementation of `_.property` without support for deep paths.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @returns {Function} Returns the new accessor function.
+ */
+ function baseProperty(key) {
+ return function(object) {
+ return object == null ? undefined : object[key];
};
- socket.send(msg);
- };
+ }
- var pixels2cells = function(pix) {
- return Math.floor(pix / (settings.block_size + settings.padding));
- };
+ /**
+ * The base implementation of `_.propertyOf` without support for deep paths.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Function} Returns the new accessor function.
+ */
+ function basePropertyOf(object) {
+ return function(key) {
+ return object == null ? undefined : object[key];
+ };
+ }
- $("form").submit(function() {
- var chatmessage = $("#message").val().trim(),
- msg;
+ /**
+ * The base implementation of `_.reduce` and `_.reduceRight`, without support
+ * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {*} accumulator The initial value.
+ * @param {boolean} initAccum Specify using the first or last element of
+ * `collection` as the initial value.
+ * @param {Function} eachFunc The function to iterate over `collection`.
+ * @returns {*} Returns the accumulated value.
+ */
+ function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
+ eachFunc(collection, function(value, index, collection) {
+ accumulator = initAccum
+ ? (initAccum = false, value)
+ : iteratee(accumulator, value, index, collection);
+ });
+ return accumulator;
+ }
- if (! chatmessage) {
- return false;
- }
+ /**
+ * The base implementation of `_.sortBy` which uses `comparer` to define the
+ * sort order of `array` and replaces criteria objects with their corresponding
+ * values.
+ *
+ * @private
+ * @param {Array} array The array to sort.
+ * @param {Function} comparer The function to define sort order.
+ * @returns {Array} Returns `array`.
+ */
+ function baseSortBy(array, comparer) {
+ var length = array.length;
- try {
- msg = {
- type: 'chat',
- contents: chatmessage,
- player_id: players.ego().id,
- timestamp: performance.now() - start,
- broadcast: true
- };
- // send directly to all clients
- socket.broadcast(msg);
- // Also send to the server for logging
- socket.send(msg);
- } catch(err) {
- console.error(err);
- } finally {
- $("#message").val("");
- return false;
+ array.sort(comparer);
+ while (length--) {
+ array[length] = array[length].value;
}
- });
+ return array;
+ }
- if (!isSpectator) {
- // Main game keys:
- bindGameKeys(socket);
- // Donation click events:
- $(pixels.canvas).click(function (e) {
- donateToClicked();
- });
- $('#public-donate').click(donateToAll);
- $('#ingroup-donate').click(donateToInGroup);
- $('#group-donate').click(function () {
- if (settings.donation_group) {
- $('#donate label').text('Click on a color');
- settings.donation_type = 'group';
- $(this).prop('disabled', false);
- $(this).removeClass('button-outline');
- $('#individual-donate').addClass('button-outline');
- }
- });
- $('#individual-donate').click(function () {
- if (settings.donation_individual) {
- $('#donate label').text('Click on a player');
- settings.donation_type = 'individual';
- $(this).removeClass('button-outline');
- $('#group-donate').addClass('button-outline');
+ /**
+ * The base implementation of `_.sum` and `_.sumBy` without support for
+ * iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {number} Returns the sum.
+ */
+ function baseSum(array, iteratee) {
+ var result,
+ index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ var current = iteratee(array[index]);
+ if (current !== undefined) {
+ result = result === undefined ? current : (result + current);
}
- });
+ }
+ return result;
}
-});
-
-}(dallinger, require, window.settings));
-
-
-/***/ }),
-/* 17 */,
-/* 18 */,
-/* 19 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var lodash = __webpack_require__(36);
-var isarray = __webpack_require__(3);
-var isnumber = __webpack_require__(4);
-var isstring = __webpack_require__(5);
-var parse = __webpack_require__(6);
+ /**
+ * The base implementation of `_.times` without support for iteratee shorthands
+ * or max array length checks.
+ *
+ * @private
+ * @param {number} n The number of times to invoke `iteratee`.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the array of results.
+ */
+ function baseTimes(n, iteratee) {
+ var index = -1,
+ result = Array(n);
-function convert(data) {
- data = isarray(data[0]) && data[0].length !== 3 ? lodash.flatten(data, 1) : data;
+ while (++index < n) {
+ result[index] = iteratee(index);
+ }
+ return result;
+ }
- if (isnumber(data[0])) {
- data = data.map(function(d) {
- return [ d, d, d ];
+ /**
+ * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
+ * of key-value pairs for `object` corresponding to the property names of `props`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array} props The property names to get values for.
+ * @returns {Object} Returns the key-value pairs.
+ */
+ function baseToPairs(object, props) {
+ return arrayMap(props, function(key) {
+ return [key, object[key]];
});
}
- if (isstring(data[0])) {
- data = data.map(function(d) {
- return parse(d).rgb.map(function(c) {
- return c / 255;
- });
- });
+ /**
+ * The base implementation of `_.unary` without support for storing metadata.
+ *
+ * @private
+ * @param {Function} func The function to cap arguments for.
+ * @returns {Function} Returns the new capped function.
+ */
+ function baseUnary(func) {
+ return function(value) {
+ return func(value);
+ };
}
- return data;
-}
+ /**
+ * The base implementation of `_.values` and `_.valuesIn` which creates an
+ * array of `object` property values corresponding to the property names
+ * of `props`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {Array} props The property names to get values for.
+ * @returns {Object} Returns the array of property values.
+ */
+ function baseValues(object, props) {
+ return arrayMap(props, function(key) {
+ return object[key];
+ });
+ }
-module.exports = convert;
+ /**
+ * Checks if a `cache` value for `key` exists.
+ *
+ * @private
+ * @param {Object} cache The cache to query.
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function cacheHas(cache, key) {
+ return cache.has(key);
+ }
+ /**
+ * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
+ * that is not found in the character symbols.
+ *
+ * @private
+ * @param {Array} strSymbols The string symbols to inspect.
+ * @param {Array} chrSymbols The character symbols to find.
+ * @returns {number} Returns the index of the first unmatched string symbol.
+ */
+ function charsStartIndex(strSymbols, chrSymbols) {
+ var index = -1,
+ length = strSymbols.length;
-/***/ }),
-/* 20 */
-/***/ (function(module, exports) {
+ while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+ return index;
+ }
-function layout(rows, columns, padding, size, aspect) {
- var grid = [];
+ /**
+ * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
+ * that is not found in the character symbols.
+ *
+ * @private
+ * @param {Array} strSymbols The string symbols to inspect.
+ * @param {Array} chrSymbols The character symbols to find.
+ * @returns {number} Returns the index of the last unmatched string symbol.
+ */
+ function charsEndIndex(strSymbols, chrSymbols) {
+ var index = strSymbols.length;
- for (var i = 0; i < rows; i++) {
- for (var j = 0; j < columns; j++) {
- var x = -1 + aspect * (i * (padding + size) + padding);
- var y = 1 - (j * (padding + size) + padding);
- grid.push([ y, x ]);
- var x_next = x + size - padding;
- grid.push([ y, x_next ]);
- var y_next = y - size + padding;
- grid.push([ y_next, x ]);
+ while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+ return index;
+ }
- grid.push([ y_next, x ]);
- grid.push([ y, x_next ]);
- grid.push([ y_next, x_next ]);
+ /**
+ * Gets the number of `placeholder` occurrences in `array`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} placeholder The placeholder to search for.
+ * @returns {number} Returns the placeholder count.
+ */
+ function countHolders(array, placeholder) {
+ var length = array.length,
+ result = 0;
+ while (length--) {
+ if (array[length] === placeholder) {
+ ++result;
+ }
}
+ return result;
}
- return grid.reverse();
-}
-
-
-module.exports = layout;
+ /**
+ * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
+ * letters to basic Latin letters.
+ *
+ * @private
+ * @param {string} letter The matched letter to deburr.
+ * @returns {string} Returns the deburred letter.
+ */
+ var deburrLetter = basePropertyOf(deburredLetters);
+ /**
+ * Used by `_.escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ var escapeHtmlChar = basePropertyOf(htmlEscapes);
-/***/ }),
-/* 21 */
-/***/ (function(module, exports) {
+ /**
+ * Used by `_.template` to escape characters for inclusion in compiled string literals.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeStringChar(chr) {
+ return '\\' + stringEscapes[chr];
+ }
-/**
- * Derived from Identicon.js 2.3.1
- * http://github.com/stewartlord/identicon.js
- *
- * Copyright 2017, Stewart Lord and Dallinger Contributors
- * Released under the BSD license
- * http://www.opensource.org/licenses/bsd-license.php
- */
+ /**
+ * Gets the value at `key` of `object`.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */
+ function getValue(object, key) {
+ return object == null ? undefined : object[key];
+ }
+ /**
+ * Checks if `string` contains Unicode symbols.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {boolean} Returns `true` if a symbol is found, else `false`.
+ */
+ function hasUnicode(string) {
+ return reHasUnicode.test(string);
+ }
-var Identicon = function(hash, size, options){
- if (typeof(hash) !== 'string' || hash.length < 15) {
- throw 'A hash of at least 15 characters is required.';
- }
+ /**
+ * Checks if `string` contains a word composed of Unicode symbols.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {boolean} Returns `true` if a word is found, else `false`.
+ */
+ function hasUnicodeWord(string) {
+ return reHasUnicodeWord.test(string);
+ }
- this.defaults = {
- background: [240, 240, 240, 255],
- margin: 0.08,
- size: 64,
- saturation: 0.7,
- brightness: 0.5,
- format: 'pixels'
- };
+ /**
+ * Converts `iterator` to an array.
+ *
+ * @private
+ * @param {Object} iterator The iterator to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function iteratorToArray(iterator) {
+ var data,
+ result = [];
- this.options = typeof(options) === 'object' ? options : this.defaults;
+ while (!(data = iterator.next()).done) {
+ result.push(data.value);
+ }
+ return result;
+ }
- this.hash = hash
- this.background = [128, 128, 128];
- this.foreground = [255, 255, 255];
- this.size = size;
-};
+ /**
+ * Converts `map` to its key-value pairs.
+ *
+ * @private
+ * @param {Object} map The map to convert.
+ * @returns {Array} Returns the key-value pairs.
+ */
+ function mapToArray(map) {
+ var index = -1,
+ result = Array(map.size);
-Identicon.prototype = {
- background: null,
- foreground: null,
- hash: null,
- margin: null,
- size: null,
- format: null,
+ map.forEach(function(value, key) {
+ result[++index] = [key, value];
+ });
+ return result;
+ }
- image: function(){
- return new Pixels(this.size, this.foreground, this.background)
- },
+ /**
+ * Creates a unary function that invokes `func` with its argument transformed.
+ *
+ * @private
+ * @param {Function} func The function to wrap.
+ * @param {Function} transform The argument transform.
+ * @returns {Function} Returns the new function.
+ */
+ function overArg(func, transform) {
+ return function(arg) {
+ return func(transform(arg));
+ };
+ }
- render: function(){
- var image = this.image(),
- size = this.size,
- baseMargin = Math.floor(size * this.margin),
- cell = Math.floor((size - (baseMargin * 2)) / 5),
- margin = Math.floor((size - cell * 5) / 2),
- bg = this.background,
- fg = this.foreground;
+ /**
+ * Replaces all `placeholder` elements in `array` with an internal placeholder
+ * and returns an array of their indexes.
+ *
+ * @private
+ * @param {Array} array The array to modify.
+ * @param {*} placeholder The placeholder to replace.
+ * @returns {Array} Returns the new array of placeholder indexes.
+ */
+ function replaceHolders(array, placeholder) {
+ var index = -1,
+ length = array.length,
+ resIndex = 0,
+ result = [];
- // the first 15 characters of the hash control the pixels (even/odd)
- // they are drawn down the middle first, then mirrored outwards
- var i, color;
- for (i = 0; i < 15; i++) {
- color = parseInt(this.hash.charAt(i), 16) % 2 ? bg : fg;
- if (i < 5) {
- this.rectangle(2 * cell + margin, i * cell + margin, cell, cell, color, image);
- } else if (i < 10) {
- this.rectangle(1 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
- this.rectangle(3 * cell + margin, (i - 5) * cell + margin, cell, cell, color, image);
- } else if (i < 15) {
- this.rectangle(0 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
- this.rectangle(4 * cell + margin, (i - 10) * cell + margin, cell, cell, color, image);
- }
- }
+ while (++index < length) {
+ var value = array[index];
+ if (value === placeholder || value === PLACEHOLDER) {
+ array[index] = PLACEHOLDER;
+ result[resIndex++] = index;
+ }
+ }
+ return result;
+ }
- return image;
- },
+ /**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+ function setToArray(set) {
+ var index = -1,
+ result = Array(set.size);
- rectangle: function(x, y, w, h, color, image){
- var i, j;
- for (i = x; i < x + w; i++) {
- for (j = y; j < y + h; j++) {
- image.buffer[j][i] = color;
- }
- }
- }
+ set.forEach(function(value) {
+ result[++index] = value;
+ });
+ return result;
+ }
-};
+ /**
+ * Converts `set` to its value-value pairs.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the value-value pairs.
+ */
+ function setToPairs(set) {
+ var index = -1,
+ result = Array(set.size);
-var Pixels = function(size){
- this.buffer = [];
- for (i = 0; i < size; i++) {
- var row = []
- for (j = 0; j < size; j++) {
- row.push([0,0,120]);
- }
- this.buffer.push(row);
- }
-};
+ set.forEach(function(value) {
+ result[++index] = [value, value];
+ });
+ return result;
+ }
-Pixels.prototype = {
- pixels: null,
+ /**
+ * A specialized version of `_.indexOf` which performs strict equality
+ * comparisons of values, i.e. `===`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function strictIndexOf(array, value, fromIndex) {
+ var index = fromIndex - 1,
+ length = array.length;
-};
+ while (++index < length) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return -1;
+ }
+ /**
+ * A specialized version of `_.lastIndexOf` which performs strict equality
+ * comparisons of values, i.e. `===`.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+ function strictLastIndexOf(array, value, fromIndex) {
+ var index = fromIndex + 1;
+ while (index--) {
+ if (array[index] === value) {
+ return index;
+ }
+ }
+ return index;
+ }
-module.exports = Identicon;
+ /**
+ * Gets the number of symbols in `string`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @returns {number} Returns the string size.
+ */
+ function stringSize(string) {
+ return hasUnicode(string)
+ ? unicodeSize(string)
+ : asciiSize(string);
+ }
+ /**
+ * Converts `string` to an array.
+ *
+ * @private
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the converted array.
+ */
+ function stringToArray(string) {
+ return hasUnicode(string)
+ ? unicodeToArray(string)
+ : asciiToArray(string);
+ }
-/***/ }),
-/* 22 */
-/***/ (function(module, exports) {
-
-/**
-* A handy class to calculate color values.
-*
-* @version 1.0
-* @author Robert Eisele
-* @copyright Copyright (c) 2010, Robert Eisele
-* @link http://www.xarg.org/2010/03/generate-client-side-png-files-using-javascript/
-* @license http://www.opensource.org/licenses/bsd-license.php BSD License
-*
-*/
-
-(function() {
-
- // helper functions for that ctx
- function write(buffer, offs) {
- for (var i = 2; i < arguments.length; i++) {
- for (var j = 0; j < arguments[i].length; j++) {
- buffer[offs++] = arguments[i].charAt(j);
- }
- }
- }
-
- function byte2(w) {
- return String.fromCharCode((w >> 8) & 255, w & 255);
- }
-
- function byte4(w) {
- return String.fromCharCode((w >> 24) & 255, (w >> 16) & 255, (w >> 8) & 255, w & 255);
- }
-
- function byte2lsb(w) {
- return String.fromCharCode(w & 255, (w >> 8) & 255);
- }
-
- // modified from original source to support NPM
- var PNGlib = function(width,height,depth) {
-
- this.width = width;
- this.height = height;
- this.depth = depth;
-
- // pixel data and row filter identifier size
- this.pix_size = height * (width + 1);
-
- // deflate header, pix_size, block headers, adler32 checksum
- this.data_size = 2 + this.pix_size + 5 * Math.floor((0xfffe + this.pix_size) / 0xffff) + 4;
-
- // offsets and sizes of Png chunks
- this.ihdr_offs = 0; // IHDR offset and size
- this.ihdr_size = 4 + 4 + 13 + 4;
- this.plte_offs = this.ihdr_offs + this.ihdr_size; // PLTE offset and size
- this.plte_size = 4 + 4 + 3 * depth + 4;
- this.trns_offs = this.plte_offs + this.plte_size; // tRNS offset and size
- this.trns_size = 4 + 4 + depth + 4;
- this.idat_offs = this.trns_offs + this.trns_size; // IDAT offset and size
- this.idat_size = 4 + 4 + this.data_size + 4;
- this.iend_offs = this.idat_offs + this.idat_size; // IEND offset and size
- this.iend_size = 4 + 4 + 4;
- this.buffer_size = this.iend_offs + this.iend_size; // total PNG size
-
- this.buffer = new Array();
- this.palette = new Object();
- this.pindex = 0;
-
- var _crc32 = new Array();
-
- // initialize buffer with zero bytes
- for (var i = 0; i < this.buffer_size; i++) {
- this.buffer[i] = "\x00";
- }
-
- // initialize non-zero elements
- write(this.buffer, this.ihdr_offs, byte4(this.ihdr_size - 12), 'IHDR', byte4(width), byte4(height), "\x08\x03");
- write(this.buffer, this.plte_offs, byte4(this.plte_size - 12), 'PLTE');
- write(this.buffer, this.trns_offs, byte4(this.trns_size - 12), 'tRNS');
- write(this.buffer, this.idat_offs, byte4(this.idat_size - 12), 'IDAT');
- write(this.buffer, this.iend_offs, byte4(this.iend_size - 12), 'IEND');
-
- // initialize deflate header
- var header = ((8 + (7 << 4)) << 8) | (3 << 6);
- header+= 31 - (header % 31);
-
- write(this.buffer, this.idat_offs + 8, byte2(header));
-
- // initialize deflate block headers
- for (var i = 0; (i << 16) - 1 < this.pix_size; i++) {
- var size, bits;
- if (i + 0xffff < this.pix_size) {
- size = 0xffff;
- bits = "\x00";
- } else {
- size = this.pix_size - (i << 16) - i;
- bits = "\x01";
- }
- write(this.buffer, this.idat_offs + 8 + 2 + (i << 16) + (i << 2), bits, byte2lsb(size), byte2lsb(~size));
- }
-
- /* Create crc32 lookup table */
- for (var i = 0; i < 256; i++) {
- var c = i;
- for (var j = 0; j < 8; j++) {
- if (c & 1) {
- c = -306674912 ^ ((c >> 1) & 0x7fffffff);
- } else {
- c = (c >> 1) & 0x7fffffff;
- }
- }
- _crc32[i] = c;
- }
-
- // compute the index into a png for a given pixel
- this.index = function(x,y) {
- var i = y * (this.width + 1) + x + 1;
- var j = this.idat_offs + 8 + 2 + 5 * Math.floor((i / 0xffff) + 1) + i;
- return j;
- }
-
- // convert a color and build up the palette
- this.color = function(red, green, blue, alpha) {
-
- alpha = alpha >= 0 ? alpha : 255;
- var color = (((((alpha << 8) | red) << 8) | green) << 8) | blue;
-
- if (typeof this.palette[color] == "undefined") {
- if (this.pindex == this.depth) return "\x00";
-
- var ndx = this.plte_offs + 8 + 3 * this.pindex;
-
- this.buffer[ndx + 0] = String.fromCharCode(red);
- this.buffer[ndx + 1] = String.fromCharCode(green);
- this.buffer[ndx + 2] = String.fromCharCode(blue);
- this.buffer[this.trns_offs+8+this.pindex] = String.fromCharCode(alpha);
-
- this.palette[color] = String.fromCharCode(this.pindex++);
- }
- return this.palette[color];
- }
-
- // output a PNG string, Base64 encoded
- this.getBase64 = function() {
-
- var s = this.getDump();
-
- var ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
- var c1, c2, c3, e1, e2, e3, e4;
- var l = s.length;
- var i = 0;
- var r = "";
-
- do {
- c1 = s.charCodeAt(i);
- e1 = c1 >> 2;
- c2 = s.charCodeAt(i+1);
- e2 = ((c1 & 3) << 4) | (c2 >> 4);
- c3 = s.charCodeAt(i+2);
- if (l < i+2) { e3 = 64; } else { e3 = ((c2 & 0xf) << 2) | (c3 >> 6); }
- if (l < i+3) { e4 = 64; } else { e4 = c3 & 0x3f; }
- r+= ch.charAt(e1) + ch.charAt(e2) + ch.charAt(e3) + ch.charAt(e4);
- } while ((i+= 3) < l);
- return r;
- }
-
- // output a PNG string
- this.getDump = function() {
-
- // compute adler32 of output pixels + row filter bytes
- var BASE = 65521; /* largest prime smaller than 65536 */
- var NMAX = 5552; /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
- var s1 = 1;
- var s2 = 0;
- var n = NMAX;
-
- for (var y = 0; y < this.height; y++) {
- for (var x = -1; x < this.width; x++) {
- s1+= this.buffer[this.index(x, y)].charCodeAt(0);
- s2+= s1;
- if ((n-= 1) == 0) {
- s1%= BASE;
- s2%= BASE;
- n = NMAX;
- }
- }
- }
- s1%= BASE;
- s2%= BASE;
- write(this.buffer, this.idat_offs + this.idat_size - 8, byte4((s2 << 16) | s1));
-
- // compute crc32 of the PNG chunks
- function crc32(png, offs, size) {
- var crc = -1;
- for (var i = 4; i < size-4; i += 1) {
- crc = _crc32[(crc ^ png[offs+i].charCodeAt(0)) & 0xff] ^ ((crc >> 8) & 0x00ffffff);
- }
- write(png, offs+size-4, byte4(crc ^ -1));
- }
-
- crc32(this.buffer, this.ihdr_offs, this.ihdr_size);
- crc32(this.buffer, this.plte_offs, this.plte_size);
- crc32(this.buffer, this.trns_offs, this.trns_size);
- crc32(this.buffer, this.idat_offs, this.idat_size);
- crc32(this.buffer, this.iend_offs, this.iend_size);
-
- // convert PNG to string
- return "\x89PNG\r\n\x1a\n"+this.buffer.join('');
- }
- }
-
- // modified from original source to support NPM
- if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') {
- module.exports = PNGlib;
- } else {
- window.PNGlib = PNGlib;
- }
- })();
-
-/***/ }),
-/* 23 */
-/***/ (function(module, exports) {
-
-function range(j, k) {
- return Array
- .apply(null, Array((k - j) + 1))
- .map(function(discard, n){ return n + j; });
-}
-
-
-
-module.exports = range;
-
-
-/***/ }),
-/* 24 */
-/***/ (function(module, exports) {
-
-function texcoord(rows, columns, texture_indexes, num_textures) {
- var grid = [];
- var texture;
- var texture_start;
- var texture_next;
-
- for (var i = 0; i < rows; i++) {
- for (var j = 0; j < columns; j++) {
- texture = texture_indexes[i*rows + j];
- texture_start = (1/num_textures)*texture;
- texture_next = texture_start + (1/num_textures);
-
- grid.push([ 0, texture_start ]);
- grid.push([ 1, texture_start ]);
- grid.push([ 0, texture_next ]);
-
- grid.push([ 0, texture_next ]);
- grid.push([ 1, texture_start ]);
- grid.push([ 1, texture_next ]);
- }
- }
-
- return grid;
-}
-
-
-module.exports = texcoord;
-
-
-/***/ }),
-/* 25 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var conversions = __webpack_require__(2);
-var route = __webpack_require__(26);
-
-var convert = {};
-
-var models = Object.keys(conversions);
-
-function wrapRaw(fn) {
- var wrappedFn = function (args) {
- if (args === undefined || args === null) {
- return args;
- }
-
- if (arguments.length > 1) {
- args = Array.prototype.slice.call(arguments);
- }
-
- return fn(args);
- };
-
- // preserve .conversion property if there is one
- if ('conversion' in fn) {
- wrappedFn.conversion = fn.conversion;
- }
-
- return wrappedFn;
-}
-
-function wrapRounded(fn) {
- var wrappedFn = function (args) {
- if (args === undefined || args === null) {
- return args;
- }
-
- if (arguments.length > 1) {
- args = Array.prototype.slice.call(arguments);
- }
-
- var result = fn(args);
-
- // we're assuming the result is an array here.
- // see notice in conversions.js; don't use box types
- // in conversion functions.
- if (typeof result === 'object') {
- for (var len = result.length, i = 0; i < len; i++) {
- result[i] = Math.round(result[i]);
- }
- }
-
- return result;
- };
-
- // preserve .conversion property if there is one
- if ('conversion' in fn) {
- wrappedFn.conversion = fn.conversion;
- }
-
- return wrappedFn;
-}
-
-models.forEach(function (fromModel) {
- convert[fromModel] = {};
-
- Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
- Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
-
- var routes = route(fromModel);
- var routeModels = Object.keys(routes);
-
- routeModels.forEach(function (toModel) {
- var fn = routes[toModel];
-
- convert[fromModel][toModel] = wrapRounded(fn);
- convert[fromModel][toModel].raw = wrapRaw(fn);
- });
-});
-
-module.exports = convert;
-
-
-/***/ }),
-/* 26 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var conversions = __webpack_require__(2);
-
-/*
- this function routes a model to all other models.
-
- all functions that are routed have a property `.conversion` attached
- to the returned synthetic function. This property is an array
- of strings, each with the steps in between the 'from' and 'to'
- color models (inclusive).
-
- conversions that are not possible simply are not included.
-*/
-
-function buildGraph() {
- var graph = {};
- // https://jsperf.com/object-keys-vs-for-in-with-closure/3
- var models = Object.keys(conversions);
-
- for (var len = models.length, i = 0; i < len; i++) {
- graph[models[i]] = {
- // http://jsperf.com/1-vs-infinity
- // micro-opt, but this is simple.
- distance: -1,
- parent: null
- };
- }
-
- return graph;
-}
-
-// https://en.wikipedia.org/wiki/Breadth-first_search
-function deriveBFS(fromModel) {
- var graph = buildGraph();
- var queue = [fromModel]; // unshift -> queue -> pop
-
- graph[fromModel].distance = 0;
-
- while (queue.length) {
- var current = queue.pop();
- var adjacents = Object.keys(conversions[current]);
-
- for (var len = adjacents.length, i = 0; i < len; i++) {
- var adjacent = adjacents[i];
- var node = graph[adjacent];
-
- if (node.distance === -1) {
- node.distance = graph[current].distance + 1;
- node.parent = current;
- queue.unshift(adjacent);
- }
- }
- }
-
- return graph;
-}
-
-function link(from, to) {
- return function (args) {
- return to(from(args));
- };
-}
-
-function wrapConversion(toModel, graph) {
- var path = [graph[toModel].parent, toModel];
- var fn = conversions[graph[toModel].parent][toModel];
-
- var cur = graph[toModel].parent;
- while (graph[cur].parent) {
- path.unshift(graph[cur].parent);
- fn = link(conversions[graph[cur].parent][cur], fn);
- cur = graph[cur].parent;
- }
-
- fn.conversion = path;
- return fn;
-}
-
-module.exports = function (fromModel) {
- var graph = deriveBFS(fromModel);
- var conversion = {};
-
- var models = Object.keys(graph);
- for (var len = models.length, i = 0; i < len; i++) {
- var toModel = models[i];
- var node = graph[toModel];
-
- if (node.parent === null) {
- // no possible conversion, or this node is the source model.
- continue;
- }
-
- conversion[toModel] = wrapConversion(toModel, graph);
- }
-
- return conversion;
-};
-
-
-
-/***/ }),
-/* 27 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = {
- "aliceblue": [240, 248, 255],
- "antiquewhite": [250, 235, 215],
- "aqua": [0, 255, 255],
- "aquamarine": [127, 255, 212],
- "azure": [240, 255, 255],
- "beige": [245, 245, 220],
- "bisque": [255, 228, 196],
- "black": [0, 0, 0],
- "blanchedalmond": [255, 235, 205],
- "blue": [0, 0, 255],
- "blueviolet": [138, 43, 226],
- "brown": [165, 42, 42],
- "burlywood": [222, 184, 135],
- "cadetblue": [95, 158, 160],
- "chartreuse": [127, 255, 0],
- "chocolate": [210, 105, 30],
- "coral": [255, 127, 80],
- "cornflowerblue": [100, 149, 237],
- "cornsilk": [255, 248, 220],
- "crimson": [220, 20, 60],
- "cyan": [0, 255, 255],
- "darkblue": [0, 0, 139],
- "darkcyan": [0, 139, 139],
- "darkgoldenrod": [184, 134, 11],
- "darkgray": [169, 169, 169],
- "darkgreen": [0, 100, 0],
- "darkgrey": [169, 169, 169],
- "darkkhaki": [189, 183, 107],
- "darkmagenta": [139, 0, 139],
- "darkolivegreen": [85, 107, 47],
- "darkorange": [255, 140, 0],
- "darkorchid": [153, 50, 204],
- "darkred": [139, 0, 0],
- "darksalmon": [233, 150, 122],
- "darkseagreen": [143, 188, 143],
- "darkslateblue": [72, 61, 139],
- "darkslategray": [47, 79, 79],
- "darkslategrey": [47, 79, 79],
- "darkturquoise": [0, 206, 209],
- "darkviolet": [148, 0, 211],
- "deeppink": [255, 20, 147],
- "deepskyblue": [0, 191, 255],
- "dimgray": [105, 105, 105],
- "dimgrey": [105, 105, 105],
- "dodgerblue": [30, 144, 255],
- "firebrick": [178, 34, 34],
- "floralwhite": [255, 250, 240],
- "forestgreen": [34, 139, 34],
- "fuchsia": [255, 0, 255],
- "gainsboro": [220, 220, 220],
- "ghostwhite": [248, 248, 255],
- "gold": [255, 215, 0],
- "goldenrod": [218, 165, 32],
- "gray": [128, 128, 128],
- "green": [0, 128, 0],
- "greenyellow": [173, 255, 47],
- "grey": [128, 128, 128],
- "honeydew": [240, 255, 240],
- "hotpink": [255, 105, 180],
- "indianred": [205, 92, 92],
- "indigo": [75, 0, 130],
- "ivory": [255, 255, 240],
- "khaki": [240, 230, 140],
- "lavender": [230, 230, 250],
- "lavenderblush": [255, 240, 245],
- "lawngreen": [124, 252, 0],
- "lemonchiffon": [255, 250, 205],
- "lightblue": [173, 216, 230],
- "lightcoral": [240, 128, 128],
- "lightcyan": [224, 255, 255],
- "lightgoldenrodyellow": [250, 250, 210],
- "lightgray": [211, 211, 211],
- "lightgreen": [144, 238, 144],
- "lightgrey": [211, 211, 211],
- "lightpink": [255, 182, 193],
- "lightsalmon": [255, 160, 122],
- "lightseagreen": [32, 178, 170],
- "lightskyblue": [135, 206, 250],
- "lightslategray": [119, 136, 153],
- "lightslategrey": [119, 136, 153],
- "lightsteelblue": [176, 196, 222],
- "lightyellow": [255, 255, 224],
- "lime": [0, 255, 0],
- "limegreen": [50, 205, 50],
- "linen": [250, 240, 230],
- "magenta": [255, 0, 255],
- "maroon": [128, 0, 0],
- "mediumaquamarine": [102, 205, 170],
- "mediumblue": [0, 0, 205],
- "mediumorchid": [186, 85, 211],
- "mediumpurple": [147, 112, 219],
- "mediumseagreen": [60, 179, 113],
- "mediumslateblue": [123, 104, 238],
- "mediumspringgreen": [0, 250, 154],
- "mediumturquoise": [72, 209, 204],
- "mediumvioletred": [199, 21, 133],
- "midnightblue": [25, 25, 112],
- "mintcream": [245, 255, 250],
- "mistyrose": [255, 228, 225],
- "moccasin": [255, 228, 181],
- "navajowhite": [255, 222, 173],
- "navy": [0, 0, 128],
- "oldlace": [253, 245, 230],
- "olive": [128, 128, 0],
- "olivedrab": [107, 142, 35],
- "orange": [255, 165, 0],
- "orangered": [255, 69, 0],
- "orchid": [218, 112, 214],
- "palegoldenrod": [238, 232, 170],
- "palegreen": [152, 251, 152],
- "paleturquoise": [175, 238, 238],
- "palevioletred": [219, 112, 147],
- "papayawhip": [255, 239, 213],
- "peachpuff": [255, 218, 185],
- "peru": [205, 133, 63],
- "pink": [255, 192, 203],
- "plum": [221, 160, 221],
- "powderblue": [176, 224, 230],
- "purple": [128, 0, 128],
- "rebeccapurple": [102, 51, 153],
- "red": [255, 0, 0],
- "rosybrown": [188, 143, 143],
- "royalblue": [65, 105, 225],
- "saddlebrown": [139, 69, 19],
- "salmon": [250, 128, 114],
- "sandybrown": [244, 164, 96],
- "seagreen": [46, 139, 87],
- "seashell": [255, 245, 238],
- "sienna": [160, 82, 45],
- "silver": [192, 192, 192],
- "skyblue": [135, 206, 235],
- "slateblue": [106, 90, 205],
- "slategray": [112, 128, 144],
- "slategrey": [112, 128, 144],
- "snow": [255, 250, 250],
- "springgreen": [0, 255, 127],
- "steelblue": [70, 130, 180],
- "tan": [210, 180, 140],
- "teal": [0, 128, 128],
- "thistle": [216, 191, 216],
- "tomato": [255, 99, 71],
- "turquoise": [64, 224, 208],
- "violet": [238, 130, 238],
- "wheat": [245, 222, 179],
- "white": [255, 255, 255],
- "whitesmoke": [245, 245, 245],
- "yellow": [255, 255, 0],
- "yellowgreen": [154, 205, 50]
-};
-
-
-/***/ }),
-/* 28 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* MIT license */
-var colorNames = __webpack_require__(29);
-var swizzle = __webpack_require__(42);
-var hasOwnProperty = Object.hasOwnProperty;
-
-var reverseNames = Object.create(null);
-
-// create a list of reverse color names
-for (var name in colorNames) {
- if (hasOwnProperty.call(colorNames, name)) {
- reverseNames[colorNames[name]] = name;
- }
-}
-
-var cs = module.exports = {
- to: {},
- get: {}
-};
-
-cs.get = function (string) {
- var prefix = string.substring(0, 3).toLowerCase();
- var val;
- var model;
- switch (prefix) {
- case 'hsl':
- val = cs.get.hsl(string);
- model = 'hsl';
- break;
- case 'hwb':
- val = cs.get.hwb(string);
- model = 'hwb';
- break;
- default:
- val = cs.get.rgb(string);
- model = 'rgb';
- break;
- }
-
- if (!val) {
- return null;
- }
-
- return {model: model, value: val};
-};
-
-cs.get.rgb = function (string) {
- if (!string) {
- return null;
- }
-
- var abbr = /^#([a-f0-9]{3,4})$/i;
- var hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;
- var rgba = /^rgba?\(\s*([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)(?=[\s,])\s*(?:,\s*)?([+-]?\d+)\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/;
- var per = /^rgba?\(\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*,?\s*([+-]?[\d\.]+)\%\s*(?:[,|\/]\s*([+-]?[\d\.]+)(%?)\s*)?\)$/;
- var keyword = /^(\w+)$/;
-
- var rgb = [0, 0, 0, 1];
- var match;
- var i;
- var hexAlpha;
-
- if (match = string.match(hex)) {
- hexAlpha = match[2];
- match = match[1];
-
- for (i = 0; i < 3; i++) {
- // https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19
- var i2 = i * 2;
- rgb[i] = parseInt(match.slice(i2, i2 + 2), 16);
- }
-
- if (hexAlpha) {
- rgb[3] = parseInt(hexAlpha, 16) / 255;
- }
- } else if (match = string.match(abbr)) {
- match = match[1];
- hexAlpha = match[3];
-
- for (i = 0; i < 3; i++) {
- rgb[i] = parseInt(match[i] + match[i], 16);
- }
-
- if (hexAlpha) {
- rgb[3] = parseInt(hexAlpha + hexAlpha, 16) / 255;
- }
- } else if (match = string.match(rgba)) {
- for (i = 0; i < 3; i++) {
- rgb[i] = parseInt(match[i + 1], 0);
- }
-
- if (match[4]) {
- if (match[5]) {
- rgb[3] = parseFloat(match[4]) * 0.01;
- } else {
- rgb[3] = parseFloat(match[4]);
- }
- }
- } else if (match = string.match(per)) {
- for (i = 0; i < 3; i++) {
- rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
- }
-
- if (match[4]) {
- if (match[5]) {
- rgb[3] = parseFloat(match[4]) * 0.01;
- } else {
- rgb[3] = parseFloat(match[4]);
- }
- }
- } else if (match = string.match(keyword)) {
- if (match[1] === 'transparent') {
- return [0, 0, 0, 0];
- }
-
- if (!hasOwnProperty.call(colorNames, match[1])) {
- return null;
- }
-
- rgb = colorNames[match[1]];
- rgb[3] = 1;
-
- return rgb;
- } else {
- return null;
- }
-
- for (i = 0; i < 3; i++) {
- rgb[i] = clamp(rgb[i], 0, 255);
- }
- rgb[3] = clamp(rgb[3], 0, 1);
-
- return rgb;
-};
-
-cs.get.hsl = function (string) {
- if (!string) {
- return null;
- }
-
- var hsl = /^hsla?\(\s*([+-]?(?:\d{0,3}\.)?\d+)(?:deg)?\s*,?\s*([+-]?[\d\.]+)%\s*,?\s*([+-]?[\d\.]+)%\s*(?:[,|\/]\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
- var match = string.match(hsl);
-
- if (match) {
- var alpha = parseFloat(match[4]);
- var h = ((parseFloat(match[1]) % 360) + 360) % 360;
- var s = clamp(parseFloat(match[2]), 0, 100);
- var l = clamp(parseFloat(match[3]), 0, 100);
- var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
-
- return [h, s, l, a];
- }
-
- return null;
-};
-
-cs.get.hwb = function (string) {
- if (!string) {
- return null;
- }
-
- var hwb = /^hwb\(\s*([+-]?\d{0,3}(?:\.\d+)?)(?:deg)?\s*,\s*([+-]?[\d\.]+)%\s*,\s*([+-]?[\d\.]+)%\s*(?:,\s*([+-]?(?=\.\d|\d)(?:0|[1-9]\d*)?(?:\.\d*)?(?:[eE][+-]?\d+)?)\s*)?\)$/;
- var match = string.match(hwb);
-
- if (match) {
- var alpha = parseFloat(match[4]);
- var h = ((parseFloat(match[1]) % 360) + 360) % 360;
- var w = clamp(parseFloat(match[2]), 0, 100);
- var b = clamp(parseFloat(match[3]), 0, 100);
- var a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
- return [h, w, b, a];
- }
-
- return null;
-};
-
-cs.to.hex = function () {
- var rgba = swizzle(arguments);
-
- return (
- '#' +
- hexDouble(rgba[0]) +
- hexDouble(rgba[1]) +
- hexDouble(rgba[2]) +
- (rgba[3] < 1
- ? (hexDouble(Math.round(rgba[3] * 255)))
- : '')
- );
-};
-
-cs.to.rgb = function () {
- var rgba = swizzle(arguments);
-
- return rgba.length < 4 || rgba[3] === 1
- ? 'rgb(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ')'
- : 'rgba(' + Math.round(rgba[0]) + ', ' + Math.round(rgba[1]) + ', ' + Math.round(rgba[2]) + ', ' + rgba[3] + ')';
-};
-
-cs.to.rgb.percent = function () {
- var rgba = swizzle(arguments);
-
- var r = Math.round(rgba[0] / 255 * 100);
- var g = Math.round(rgba[1] / 255 * 100);
- var b = Math.round(rgba[2] / 255 * 100);
-
- return rgba.length < 4 || rgba[3] === 1
- ? 'rgb(' + r + '%, ' + g + '%, ' + b + '%)'
- : 'rgba(' + r + '%, ' + g + '%, ' + b + '%, ' + rgba[3] + ')';
-};
-
-cs.to.hsl = function () {
- var hsla = swizzle(arguments);
- return hsla.length < 4 || hsla[3] === 1
- ? 'hsl(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%)'
- : 'hsla(' + hsla[0] + ', ' + hsla[1] + '%, ' + hsla[2] + '%, ' + hsla[3] + ')';
-};
-
-// hwb is a bit different than rgb(a) & hsl(a) since there is no alpha specific syntax
-// (hwb have alpha optional & 1 is default value)
-cs.to.hwb = function () {
- var hwba = swizzle(arguments);
-
- var a = '';
- if (hwba.length >= 4 && hwba[3] !== 1) {
- a = ', ' + hwba[3];
- }
-
- return 'hwb(' + hwba[0] + ', ' + hwba[1] + '%, ' + hwba[2] + '%' + a + ')';
-};
-
-cs.to.keyword = function (rgb) {
- return reverseNames[rgb.slice(0, 3)];
-};
-
-// helpers
-function clamp(num, min, max) {
- return Math.min(Math.max(min, num), max);
-}
-
-function hexDouble(num) {
- var str = Math.round(num).toString(16).toUpperCase();
- return (str.length < 2) ? '0' + str : str;
-}
-
-
-/***/ }),
-/* 29 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-module.exports = {
- "aliceblue": [240, 248, 255],
- "antiquewhite": [250, 235, 215],
- "aqua": [0, 255, 255],
- "aquamarine": [127, 255, 212],
- "azure": [240, 255, 255],
- "beige": [245, 245, 220],
- "bisque": [255, 228, 196],
- "black": [0, 0, 0],
- "blanchedalmond": [255, 235, 205],
- "blue": [0, 0, 255],
- "blueviolet": [138, 43, 226],
- "brown": [165, 42, 42],
- "burlywood": [222, 184, 135],
- "cadetblue": [95, 158, 160],
- "chartreuse": [127, 255, 0],
- "chocolate": [210, 105, 30],
- "coral": [255, 127, 80],
- "cornflowerblue": [100, 149, 237],
- "cornsilk": [255, 248, 220],
- "crimson": [220, 20, 60],
- "cyan": [0, 255, 255],
- "darkblue": [0, 0, 139],
- "darkcyan": [0, 139, 139],
- "darkgoldenrod": [184, 134, 11],
- "darkgray": [169, 169, 169],
- "darkgreen": [0, 100, 0],
- "darkgrey": [169, 169, 169],
- "darkkhaki": [189, 183, 107],
- "darkmagenta": [139, 0, 139],
- "darkolivegreen": [85, 107, 47],
- "darkorange": [255, 140, 0],
- "darkorchid": [153, 50, 204],
- "darkred": [139, 0, 0],
- "darksalmon": [233, 150, 122],
- "darkseagreen": [143, 188, 143],
- "darkslateblue": [72, 61, 139],
- "darkslategray": [47, 79, 79],
- "darkslategrey": [47, 79, 79],
- "darkturquoise": [0, 206, 209],
- "darkviolet": [148, 0, 211],
- "deeppink": [255, 20, 147],
- "deepskyblue": [0, 191, 255],
- "dimgray": [105, 105, 105],
- "dimgrey": [105, 105, 105],
- "dodgerblue": [30, 144, 255],
- "firebrick": [178, 34, 34],
- "floralwhite": [255, 250, 240],
- "forestgreen": [34, 139, 34],
- "fuchsia": [255, 0, 255],
- "gainsboro": [220, 220, 220],
- "ghostwhite": [248, 248, 255],
- "gold": [255, 215, 0],
- "goldenrod": [218, 165, 32],
- "gray": [128, 128, 128],
- "green": [0, 128, 0],
- "greenyellow": [173, 255, 47],
- "grey": [128, 128, 128],
- "honeydew": [240, 255, 240],
- "hotpink": [255, 105, 180],
- "indianred": [205, 92, 92],
- "indigo": [75, 0, 130],
- "ivory": [255, 255, 240],
- "khaki": [240, 230, 140],
- "lavender": [230, 230, 250],
- "lavenderblush": [255, 240, 245],
- "lawngreen": [124, 252, 0],
- "lemonchiffon": [255, 250, 205],
- "lightblue": [173, 216, 230],
- "lightcoral": [240, 128, 128],
- "lightcyan": [224, 255, 255],
- "lightgoldenrodyellow": [250, 250, 210],
- "lightgray": [211, 211, 211],
- "lightgreen": [144, 238, 144],
- "lightgrey": [211, 211, 211],
- "lightpink": [255, 182, 193],
- "lightsalmon": [255, 160, 122],
- "lightseagreen": [32, 178, 170],
- "lightskyblue": [135, 206, 250],
- "lightslategray": [119, 136, 153],
- "lightslategrey": [119, 136, 153],
- "lightsteelblue": [176, 196, 222],
- "lightyellow": [255, 255, 224],
- "lime": [0, 255, 0],
- "limegreen": [50, 205, 50],
- "linen": [250, 240, 230],
- "magenta": [255, 0, 255],
- "maroon": [128, 0, 0],
- "mediumaquamarine": [102, 205, 170],
- "mediumblue": [0, 0, 205],
- "mediumorchid": [186, 85, 211],
- "mediumpurple": [147, 112, 219],
- "mediumseagreen": [60, 179, 113],
- "mediumslateblue": [123, 104, 238],
- "mediumspringgreen": [0, 250, 154],
- "mediumturquoise": [72, 209, 204],
- "mediumvioletred": [199, 21, 133],
- "midnightblue": [25, 25, 112],
- "mintcream": [245, 255, 250],
- "mistyrose": [255, 228, 225],
- "moccasin": [255, 228, 181],
- "navajowhite": [255, 222, 173],
- "navy": [0, 0, 128],
- "oldlace": [253, 245, 230],
- "olive": [128, 128, 0],
- "olivedrab": [107, 142, 35],
- "orange": [255, 165, 0],
- "orangered": [255, 69, 0],
- "orchid": [218, 112, 214],
- "palegoldenrod": [238, 232, 170],
- "palegreen": [152, 251, 152],
- "paleturquoise": [175, 238, 238],
- "palevioletred": [219, 112, 147],
- "papayawhip": [255, 239, 213],
- "peachpuff": [255, 218, 185],
- "peru": [205, 133, 63],
- "pink": [255, 192, 203],
- "plum": [221, 160, 221],
- "powderblue": [176, 224, 230],
- "purple": [128, 0, 128],
- "rebeccapurple": [102, 51, 153],
- "red": [255, 0, 0],
- "rosybrown": [188, 143, 143],
- "royalblue": [65, 105, 225],
- "saddlebrown": [139, 69, 19],
- "salmon": [250, 128, 114],
- "sandybrown": [244, 164, 96],
- "seagreen": [46, 139, 87],
- "seashell": [255, 245, 238],
- "sienna": [160, 82, 45],
- "silver": [192, 192, 192],
- "skyblue": [135, 206, 235],
- "slateblue": [106, 90, 205],
- "slategray": [112, 128, 144],
- "slategrey": [112, 128, 144],
- "snow": [255, 250, 250],
- "springgreen": [0, 255, 127],
- "steelblue": [70, 130, 180],
- "tan": [210, 180, 140],
- "teal": [0, 128, 128],
- "thistle": [216, 191, 216],
- "tomato": [255, 99, 71],
- "turquoise": [64, 224, 208],
- "violet": [238, 130, 238],
- "wheat": [245, 222, 179],
- "white": [255, 255, 255],
- "whitesmoke": [245, 245, 245],
- "yellow": [255, 255, 0],
- "yellowgreen": [154, 205, 50]
-};
-
-
-/***/ }),
-/* 30 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/**
- * Box-Muller implementation
- * https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
- */
-
-(function(exports){
- const PRECISION = 1e9;
- const _2PI = Math.PI * 2;
-
- /**
- *
- * @param {number} mean
- * @param {number} std
- * @param randFn - an optional function that returns a float between 0 (inclusive) and 1
- * (exclusive). Use this if you want to pass in a random number generator other than
- * Math.random().
- * @returns {number}
- */
- function generateGaussian(mean,std, randFn = null){
- var u1;
- var u2;
- if (randFn) {
- u1 = randFn();
- u2 = randFn();
- }
- else {
- u1 = Math.random();
- u2 = Math.random();
- }
-
- var z0 = Math.sqrt(-2.0 * Math.log(u1)) * Math.cos(_2PI * u2);
- var z1 = Math.sqrt(-2.0 * Math.log(u1)) * Math.sin(_2PI * u2);
-
- return z0 * std + mean;
- }
-
- exports(generateGaussian)
-})
-( true
- ? function(e) { module.exports = e; }
- // istanbul ignore next
- : function(e) { this["boxmuller"] = e; });
-
-
-
-/***/ }),
-/* 31 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-/* eslint complexity: [2, 18], max-statements: [2, 33] */
-module.exports = function hasSymbols() {
- if (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }
- if (typeof Symbol.iterator === 'symbol') { return true; }
-
- var obj = {};
- var sym = Symbol('test');
- var symObj = Object(sym);
- if (typeof sym === 'string') { return false; }
-
- if (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }
- if (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }
-
- // temp disabled per https://github.com/ljharb/object.assign/issues/17
- // if (sym instanceof Symbol) { return false; }
- // temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4
- // if (!(symObj instanceof Symbol)) { return false; }
-
- // if (typeof Symbol.prototype.toString !== 'function') { return false; }
- // if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }
-
- var symVal = 42;
- obj[sym] = symVal;
- for (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop
- if (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }
-
- if (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }
-
- var syms = Object.getOwnPropertySymbols(obj);
- if (syms.length !== 1 || syms[0] !== sym) { return false; }
-
- if (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }
-
- if (typeof Object.getOwnPropertyDescriptor === 'function') {
- var descriptor = Object.getOwnPropertyDescriptor(obj, sym);
- if (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }
- }
-
- return true;
-};
-
-
-/***/ }),
-/* 32 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
-var hasSymbols = __webpack_require__(31);
-
-module.exports = function hasToStringTagShams() {
- return hasSymbols() && !!Symbol.toStringTag;
-};
-
-
-/***/ }),
-/* 33 */
-/***/ (function(module, exports) {
-
-module.exports = function isArrayish(obj) {
- if (!obj || typeof obj === 'string') {
- return false;
- }
-
- return obj instanceof Array || Array.isArray(obj) ||
- (obj.length >= 0 && (obj.splice instanceof Function ||
- (Object.getOwnPropertyDescriptor(obj, (obj.length - 1)) && obj.constructor.name !== 'String')));
-};
-
-
-/***/ }),
-/* 34 */
-/***/ (function(module, exports) {
-
-/*!
- * Determine if an object is a Buffer
- *
- * @author Feross Aboukhadijeh
- * @license MIT
- */
-
-// The _isBuffer check is for Safari 5-7 support, because it's missing
-// Object.prototype.constructor. Remove this eventually
-module.exports = function (obj) {
- return obj != null && (isBuffer(obj) || isSlowBuffer(obj) || !!obj._isBuffer)
-}
-
-function isBuffer (obj) {
- return !!obj.constructor && typeof obj.constructor.isBuffer === 'function' && obj.constructor.isBuffer(obj)
-}
-
-// For Node v0.10 support. Remove this eventually.
-function isSlowBuffer (obj) {
- return typeof obj.readFloatLE === 'function' && typeof obj.slice === 'function' && isBuffer(obj.slice(0, 0))
-}
-
-
-/***/ }),
-/* 35 */
-/***/ (function(module, exports, __webpack_require__) {
-
-var isBuffer = __webpack_require__(34);
-var toString = Object.prototype.toString;
-
-/**
- * Get the native `typeof` a value.
- *
- * @param {*} `val`
- * @return {*} Native javascript type
- */
-
-module.exports = function kindOf(val) {
- // primitivies
- if (typeof val === 'undefined') {
- return 'undefined';
- }
- if (val === null) {
- return 'null';
- }
- if (val === true || val === false || val instanceof Boolean) {
- return 'boolean';
- }
- if (typeof val === 'string' || val instanceof String) {
- return 'string';
- }
- if (typeof val === 'number' || val instanceof Number) {
- return 'number';
- }
-
- // functions
- if (typeof val === 'function' || val instanceof Function) {
- return 'function';
- }
-
- // array
- if (typeof Array.isArray !== 'undefined' && Array.isArray(val)) {
- return 'array';
- }
-
- // check for instances of RegExp and Date before calling `toString`
- if (val instanceof RegExp) {
- return 'regexp';
- }
- if (val instanceof Date) {
- return 'date';
- }
-
- // other objects
- var type = toString.call(val);
-
- if (type === '[object RegExp]') {
- return 'regexp';
- }
- if (type === '[object Date]') {
- return 'date';
- }
- if (type === '[object Arguments]') {
- return 'arguments';
- }
- if (type === '[object Error]') {
- return 'error';
- }
-
- // buffer
- if (isBuffer(val)) {
- return 'buffer';
- }
-
- // es6: Map, WeakMap, Set, WeakSet
- if (type === '[object Set]') {
- return 'set';
- }
- if (type === '[object WeakSet]') {
- return 'weakset';
- }
- if (type === '[object Map]') {
- return 'map';
- }
- if (type === '[object WeakMap]') {
- return 'weakmap';
- }
- if (type === '[object Symbol]') {
- return 'symbol';
- }
-
- // typed arrays
- if (type === '[object Int8Array]') {
- return 'int8array';
- }
- if (type === '[object Uint8Array]') {
- return 'uint8array';
- }
- if (type === '[object Uint8ClampedArray]') {
- return 'uint8clampedarray';
- }
- if (type === '[object Int16Array]') {
- return 'int16array';
- }
- if (type === '[object Uint16Array]') {
- return 'uint16array';
- }
- if (type === '[object Int32Array]') {
- return 'int32array';
- }
- if (type === '[object Uint32Array]') {
- return 'uint32array';
- }
- if (type === '[object Float32Array]') {
- return 'float32array';
- }
- if (type === '[object Float64Array]') {
- return 'float64array';
- }
-
- // must be a plain object
- return 'object';
-};
-
-
-/***/ }),
-/* 36 */
-/***/ (function(module, exports, __webpack_require__) {
-
-/* WEBPACK VAR INJECTION */(function(global, module) {var __WEBPACK_AMD_DEFINE_RESULT__;/**
- * @license
- * Lodash
- * Copyright OpenJS Foundation and other contributors
- * Released under MIT license
- * Based on Underscore.js 1.8.3
- * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
- */
-;(function() {
-
- /** Used as a safe reference for `undefined` in pre-ES5 environments. */
- var undefined;
-
- /** Used as the semantic version number. */
- var VERSION = '4.17.21';
-
- /** Used as the size to enable large array optimizations. */
- var LARGE_ARRAY_SIZE = 200;
-
- /** Error message constants. */
- var CORE_ERROR_TEXT = 'Unsupported core-js use. Try https://npms.io/search?q=ponyfill.',
- FUNC_ERROR_TEXT = 'Expected a function',
- INVALID_TEMPL_VAR_ERROR_TEXT = 'Invalid `variable` option passed into `_.template`';
-
- /** Used to stand-in for `undefined` hash values. */
- var HASH_UNDEFINED = '__lodash_hash_undefined__';
-
- /** Used as the maximum memoize cache size. */
- var MAX_MEMOIZE_SIZE = 500;
-
- /** Used as the internal argument placeholder. */
- var PLACEHOLDER = '__lodash_placeholder__';
-
- /** Used to compose bitmasks for cloning. */
- var CLONE_DEEP_FLAG = 1,
- CLONE_FLAT_FLAG = 2,
- CLONE_SYMBOLS_FLAG = 4;
-
- /** Used to compose bitmasks for value comparisons. */
- var COMPARE_PARTIAL_FLAG = 1,
- COMPARE_UNORDERED_FLAG = 2;
-
- /** Used to compose bitmasks for function metadata. */
- var WRAP_BIND_FLAG = 1,
- WRAP_BIND_KEY_FLAG = 2,
- WRAP_CURRY_BOUND_FLAG = 4,
- WRAP_CURRY_FLAG = 8,
- WRAP_CURRY_RIGHT_FLAG = 16,
- WRAP_PARTIAL_FLAG = 32,
- WRAP_PARTIAL_RIGHT_FLAG = 64,
- WRAP_ARY_FLAG = 128,
- WRAP_REARG_FLAG = 256,
- WRAP_FLIP_FLAG = 512;
-
- /** Used as default options for `_.truncate`. */
- var DEFAULT_TRUNC_LENGTH = 30,
- DEFAULT_TRUNC_OMISSION = '...';
-
- /** Used to detect hot functions by number of calls within a span of milliseconds. */
- var HOT_COUNT = 800,
- HOT_SPAN = 16;
-
- /** Used to indicate the type of lazy iteratees. */
- var LAZY_FILTER_FLAG = 1,
- LAZY_MAP_FLAG = 2,
- LAZY_WHILE_FLAG = 3;
-
- /** Used as references for various `Number` constants. */
- var INFINITY = 1 / 0,
- MAX_SAFE_INTEGER = 9007199254740991,
- MAX_INTEGER = 1.7976931348623157e+308,
- NAN = 0 / 0;
-
- /** Used as references for the maximum length and index of an array. */
- var MAX_ARRAY_LENGTH = 4294967295,
- MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1,
- HALF_MAX_ARRAY_LENGTH = MAX_ARRAY_LENGTH >>> 1;
-
- /** Used to associate wrap methods with their bit flags. */
- var wrapFlags = [
- ['ary', WRAP_ARY_FLAG],
- ['bind', WRAP_BIND_FLAG],
- ['bindKey', WRAP_BIND_KEY_FLAG],
- ['curry', WRAP_CURRY_FLAG],
- ['curryRight', WRAP_CURRY_RIGHT_FLAG],
- ['flip', WRAP_FLIP_FLAG],
- ['partial', WRAP_PARTIAL_FLAG],
- ['partialRight', WRAP_PARTIAL_RIGHT_FLAG],
- ['rearg', WRAP_REARG_FLAG]
- ];
-
- /** `Object#toString` result references. */
- var argsTag = '[object Arguments]',
- arrayTag = '[object Array]',
- asyncTag = '[object AsyncFunction]',
- boolTag = '[object Boolean]',
- dateTag = '[object Date]',
- domExcTag = '[object DOMException]',
- errorTag = '[object Error]',
- funcTag = '[object Function]',
- genTag = '[object GeneratorFunction]',
- mapTag = '[object Map]',
- numberTag = '[object Number]',
- nullTag = '[object Null]',
- objectTag = '[object Object]',
- promiseTag = '[object Promise]',
- proxyTag = '[object Proxy]',
- regexpTag = '[object RegExp]',
- setTag = '[object Set]',
- stringTag = '[object String]',
- symbolTag = '[object Symbol]',
- undefinedTag = '[object Undefined]',
- weakMapTag = '[object WeakMap]',
- weakSetTag = '[object WeakSet]';
-
- var arrayBufferTag = '[object ArrayBuffer]',
- dataViewTag = '[object DataView]',
- float32Tag = '[object Float32Array]',
- float64Tag = '[object Float64Array]',
- int8Tag = '[object Int8Array]',
- int16Tag = '[object Int16Array]',
- int32Tag = '[object Int32Array]',
- uint8Tag = '[object Uint8Array]',
- uint8ClampedTag = '[object Uint8ClampedArray]',
- uint16Tag = '[object Uint16Array]',
- uint32Tag = '[object Uint32Array]';
-
- /** Used to match empty string literals in compiled template source. */
- var reEmptyStringLeading = /\b__p \+= '';/g,
- reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
- reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
-
- /** Used to match HTML entities and HTML characters. */
- var reEscapedHtml = /&(?:amp|lt|gt|quot|#39);/g,
- reUnescapedHtml = /[&<>"']/g,
- reHasEscapedHtml = RegExp(reEscapedHtml.source),
- reHasUnescapedHtml = RegExp(reUnescapedHtml.source);
-
- /** Used to match template delimiters. */
- var reEscape = /<%-([\s\S]+?)%>/g,
- reEvaluate = /<%([\s\S]+?)%>/g,
- reInterpolate = /<%=([\s\S]+?)%>/g;
-
- /** Used to match property names within property paths. */
- var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
- reIsPlainProp = /^\w*$/,
- rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;
-
- /**
- * Used to match `RegExp`
- * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
- */
- var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
- reHasRegExpChar = RegExp(reRegExpChar.source);
-
- /** Used to match leading whitespace. */
- var reTrimStart = /^\s+/;
-
- /** Used to match a single whitespace character. */
- var reWhitespace = /\s/;
-
- /** Used to match wrap detail comments. */
- var reWrapComment = /\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,
- reWrapDetails = /\{\n\/\* \[wrapped with (.+)\] \*/,
- reSplitDetails = /,? & /;
-
- /** Used to match words composed of alphanumeric characters. */
- var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
-
- /**
- * Used to validate the `validate` option in `_.template` variable.
- *
- * Forbids characters which could potentially change the meaning of the function argument definition:
- * - "()," (modification of function parameters)
- * - "=" (default value)
- * - "[]{}" (destructuring of function parameters)
- * - "/" (beginning of a comment)
- * - whitespace
- */
- var reForbiddenIdentifierChars = /[()=,{}\[\]\/\s]/;
-
- /** Used to match backslashes in property paths. */
- var reEscapeChar = /\\(\\)?/g;
-
- /**
- * Used to match
- * [ES template delimiters](http://ecma-international.org/ecma-262/7.0/#sec-template-literal-lexical-components).
- */
- var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
-
- /** Used to match `RegExp` flags from their coerced string values. */
- var reFlags = /\w*$/;
-
- /** Used to detect bad signed hexadecimal string values. */
- var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
-
- /** Used to detect binary string values. */
- var reIsBinary = /^0b[01]+$/i;
-
- /** Used to detect host constructors (Safari). */
- var reIsHostCtor = /^\[object .+?Constructor\]$/;
-
- /** Used to detect octal string values. */
- var reIsOctal = /^0o[0-7]+$/i;
-
- /** Used to detect unsigned integer values. */
- var reIsUint = /^(?:0|[1-9]\d*)$/;
-
- /** Used to match Latin Unicode letters (excluding mathematical operators). */
- var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
-
- /** Used to ensure capturing order of template delimiters. */
- var reNoMatch = /($^)/;
-
- /** Used to match unescaped characters in compiled string literals. */
- var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
-
- /** Used to compose unicode character classes. */
- var rsAstralRange = '\\ud800-\\udfff',
- rsComboMarksRange = '\\u0300-\\u036f',
- reComboHalfMarksRange = '\\ufe20-\\ufe2f',
- rsComboSymbolsRange = '\\u20d0-\\u20ff',
- rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
- rsDingbatRange = '\\u2700-\\u27bf',
- rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff',
- rsMathOpRange = '\\xac\\xb1\\xd7\\xf7',
- rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf',
- rsPunctuationRange = '\\u2000-\\u206f',
- rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000',
- rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde',
- rsVarRange = '\\ufe0e\\ufe0f',
- rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
-
- /** Used to compose unicode capture groups. */
- var rsApos = "['\u2019]",
- rsAstral = '[' + rsAstralRange + ']',
- rsBreak = '[' + rsBreakRange + ']',
- rsCombo = '[' + rsComboRange + ']',
- rsDigits = '\\d+',
- rsDingbat = '[' + rsDingbatRange + ']',
- rsLower = '[' + rsLowerRange + ']',
- rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']',
- rsFitz = '\\ud83c[\\udffb-\\udfff]',
- rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
- rsNonAstral = '[^' + rsAstralRange + ']',
- rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
- rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
- rsUpper = '[' + rsUpperRange + ']',
- rsZWJ = '\\u200d';
-
- /** Used to compose unicode regexes. */
- var rsMiscLower = '(?:' + rsLower + '|' + rsMisc + ')',
- rsMiscUpper = '(?:' + rsUpper + '|' + rsMisc + ')',
- rsOptContrLower = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?',
- rsOptContrUpper = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?',
- reOptMod = rsModifier + '?',
- rsOptVar = '[' + rsVarRange + ']?',
- rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
- rsOrdLower = '\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])',
- rsOrdUpper = '\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])',
- rsSeq = rsOptVar + reOptMod + rsOptJoin,
- rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq,
- rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
-
- /** Used to match apostrophes. */
- var reApos = RegExp(rsApos, 'g');
-
- /**
- * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
- * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
- */
- var reComboMark = RegExp(rsCombo, 'g');
-
- /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
- var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
-
- /** Used to match complex or compound words. */
- var reUnicodeWord = RegExp([
- rsUpper + '?' + rsLower + '+' + rsOptContrLower + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')',
- rsMiscUpper + '+' + rsOptContrUpper + '(?=' + [rsBreak, rsUpper + rsMiscLower, '$'].join('|') + ')',
- rsUpper + '?' + rsMiscLower + '+' + rsOptContrLower,
- rsUpper + '+' + rsOptContrUpper,
- rsOrdUpper,
- rsOrdLower,
- rsDigits,
- rsEmoji
- ].join('|'), 'g');
-
- /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
- var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboRange + rsVarRange + ']');
-
- /** Used to detect strings that need a more robust regexp to match words. */
- var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
-
- /** Used to assign default `context` object properties. */
- var contextProps = [
- 'Array', 'Buffer', 'DataView', 'Date', 'Error', 'Float32Array', 'Float64Array',
- 'Function', 'Int8Array', 'Int16Array', 'Int32Array', 'Map', 'Math', 'Object',
- 'Promise', 'RegExp', 'Set', 'String', 'Symbol', 'TypeError', 'Uint8Array',
- 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
- '_', 'clearTimeout', 'isFinite', 'parseInt', 'setTimeout'
- ];
-
- /** Used to make template sourceURLs easier to identify. */
- var templateCounter = -1;
-
- /** Used to identify `toStringTag` values of typed arrays. */
- var typedArrayTags = {};
- typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
- typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
- typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
- typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
- typedArrayTags[uint32Tag] = true;
- typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
- typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
- typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
- typedArrayTags[errorTag] = typedArrayTags[funcTag] =
- typedArrayTags[mapTag] = typedArrayTags[numberTag] =
- typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
- typedArrayTags[setTag] = typedArrayTags[stringTag] =
- typedArrayTags[weakMapTag] = false;
-
- /** Used to identify `toStringTag` values supported by `_.clone`. */
- var cloneableTags = {};
- cloneableTags[argsTag] = cloneableTags[arrayTag] =
- cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] =
- cloneableTags[boolTag] = cloneableTags[dateTag] =
- cloneableTags[float32Tag] = cloneableTags[float64Tag] =
- cloneableTags[int8Tag] = cloneableTags[int16Tag] =
- cloneableTags[int32Tag] = cloneableTags[mapTag] =
- cloneableTags[numberTag] = cloneableTags[objectTag] =
- cloneableTags[regexpTag] = cloneableTags[setTag] =
- cloneableTags[stringTag] = cloneableTags[symbolTag] =
- cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] =
- cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
- cloneableTags[errorTag] = cloneableTags[funcTag] =
- cloneableTags[weakMapTag] = false;
-
- /** Used to map Latin Unicode letters to basic Latin letters. */
- var deburredLetters = {
- // Latin-1 Supplement block.
- '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A',
- '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a',
- '\xc7': 'C', '\xe7': 'c',
- '\xd0': 'D', '\xf0': 'd',
- '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E',
- '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e',
- '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I',
- '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i',
- '\xd1': 'N', '\xf1': 'n',
- '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O',
- '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o',
- '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U',
- '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u',
- '\xdd': 'Y', '\xfd': 'y', '\xff': 'y',
- '\xc6': 'Ae', '\xe6': 'ae',
- '\xde': 'Th', '\xfe': 'th',
- '\xdf': 'ss',
- // Latin Extended-A block.
- '\u0100': 'A', '\u0102': 'A', '\u0104': 'A',
- '\u0101': 'a', '\u0103': 'a', '\u0105': 'a',
- '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C',
- '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c',
- '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd',
- '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E',
- '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e',
- '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G',
- '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g',
- '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h',
- '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I',
- '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i',
- '\u0134': 'J', '\u0135': 'j',
- '\u0136': 'K', '\u0137': 'k', '\u0138': 'k',
- '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L',
- '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l',
- '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N',
- '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n',
- '\u014c': 'O', '\u014e': 'O', '\u0150': 'O',
- '\u014d': 'o', '\u014f': 'o', '\u0151': 'o',
- '\u0154': 'R', '\u0156': 'R', '\u0158': 'R',
- '\u0155': 'r', '\u0157': 'r', '\u0159': 'r',
- '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S',
- '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's',
- '\u0162': 'T', '\u0164': 'T', '\u0166': 'T',
- '\u0163': 't', '\u0165': 't', '\u0167': 't',
- '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U',
- '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u',
- '\u0174': 'W', '\u0175': 'w',
- '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y',
- '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z',
- '\u017a': 'z', '\u017c': 'z', '\u017e': 'z',
- '\u0132': 'IJ', '\u0133': 'ij',
- '\u0152': 'Oe', '\u0153': 'oe',
- '\u0149': "'n", '\u017f': 's'
- };
-
- /** Used to map characters to HTML entities. */
- var htmlEscapes = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- "'": '''
- };
-
- /** Used to map HTML entities to characters. */
- var htmlUnescapes = {
- '&': '&',
- '<': '<',
- '>': '>',
- '"': '"',
- ''': "'"
- };
-
- /** Used to escape characters for inclusion in compiled string literals. */
- var stringEscapes = {
- '\\': '\\',
- "'": "'",
- '\n': 'n',
- '\r': 'r',
- '\u2028': 'u2028',
- '\u2029': 'u2029'
- };
-
- /** Built-in method references without a dependency on `root`. */
- var freeParseFloat = parseFloat,
- freeParseInt = parseInt;
-
- /** Detect free variable `global` from Node.js. */
- var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
-
- /** Detect free variable `self`. */
- var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
-
- /** Used as a reference to the global object. */
- var root = freeGlobal || freeSelf || Function('return this')();
-
- /** Detect free variable `exports`. */
- var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
-
- /** Detect free variable `module`. */
- var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
-
- /** Detect the popular CommonJS extension `module.exports`. */
- var moduleExports = freeModule && freeModule.exports === freeExports;
-
- /** Detect free variable `process` from Node.js. */
- var freeProcess = moduleExports && freeGlobal.process;
-
- /** Used to access faster Node.js helpers. */
- var nodeUtil = (function() {
- try {
- // Use `util.types` for Node.js 10+.
- var types = freeModule && freeModule.require && freeModule.require('util').types;
-
- if (types) {
- return types;
- }
-
- // Legacy `process.binding('util')` for Node.js < 10.
- return freeProcess && freeProcess.binding && freeProcess.binding('util');
- } catch (e) {}
- }());
-
- /* Node.js helper references. */
- var nodeIsArrayBuffer = nodeUtil && nodeUtil.isArrayBuffer,
- nodeIsDate = nodeUtil && nodeUtil.isDate,
- nodeIsMap = nodeUtil && nodeUtil.isMap,
- nodeIsRegExp = nodeUtil && nodeUtil.isRegExp,
- nodeIsSet = nodeUtil && nodeUtil.isSet,
- nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
-
- /*--------------------------------------------------------------------------*/
-
- /**
- * A faster alternative to `Function#apply`, this function invokes `func`
- * with the `this` binding of `thisArg` and the arguments of `args`.
- *
- * @private
- * @param {Function} func The function to invoke.
- * @param {*} thisArg The `this` binding of `func`.
- * @param {Array} args The arguments to invoke `func` with.
- * @returns {*} Returns the result of `func`.
- */
- function apply(func, thisArg, args) {
- switch (args.length) {
- case 0: return func.call(thisArg);
- case 1: return func.call(thisArg, args[0]);
- case 2: return func.call(thisArg, args[0], args[1]);
- case 3: return func.call(thisArg, args[0], args[1], args[2]);
- }
- return func.apply(thisArg, args);
- }
-
- /**
- * A specialized version of `baseAggregator` for arrays.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} setter The function to set `accumulator` values.
- * @param {Function} iteratee The iteratee to transform keys.
- * @param {Object} accumulator The initial aggregated object.
- * @returns {Function} Returns `accumulator`.
- */
- function arrayAggregator(array, setter, iteratee, accumulator) {
- var index = -1,
- length = array == null ? 0 : array.length;
-
- while (++index < length) {
- var value = array[index];
- setter(accumulator, value, iteratee(value), array);
- }
- return accumulator;
- }
-
- /**
- * A specialized version of `_.forEach` for arrays without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array} Returns `array`.
- */
- function arrayEach(array, iteratee) {
- var index = -1,
- length = array == null ? 0 : array.length;
-
- while (++index < length) {
- if (iteratee(array[index], index, array) === false) {
- break;
- }
- }
- return array;
- }
-
- /**
- * A specialized version of `_.forEachRight` for arrays without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array} Returns `array`.
- */
- function arrayEachRight(array, iteratee) {
- var length = array == null ? 0 : array.length;
-
- while (length--) {
- if (iteratee(array[length], length, array) === false) {
- break;
- }
- }
- return array;
- }
-
- /**
- * A specialized version of `_.every` for arrays without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} predicate The function invoked per iteration.
- * @returns {boolean} Returns `true` if all elements pass the predicate check,
- * else `false`.
- */
- function arrayEvery(array, predicate) {
- var index = -1,
- length = array == null ? 0 : array.length;
-
- while (++index < length) {
- if (!predicate(array[index], index, array)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * A specialized version of `_.filter` for arrays without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} predicate The function invoked per iteration.
- * @returns {Array} Returns the new filtered array.
- */
- function arrayFilter(array, predicate) {
- var index = -1,
- length = array == null ? 0 : array.length,
- resIndex = 0,
- result = [];
-
- while (++index < length) {
- var value = array[index];
- if (predicate(value, index, array)) {
- result[resIndex++] = value;
- }
- }
- return result;
- }
-
- /**
- * A specialized version of `_.includes` for arrays without support for
- * specifying an index to search from.
- *
- * @private
- * @param {Array} [array] The array to inspect.
- * @param {*} target The value to search for.
- * @returns {boolean} Returns `true` if `target` is found, else `false`.
- */
- function arrayIncludes(array, value) {
- var length = array == null ? 0 : array.length;
- return !!length && baseIndexOf(array, value, 0) > -1;
- }
-
- /**
- * This function is like `arrayIncludes` except that it accepts a comparator.
- *
- * @private
- * @param {Array} [array] The array to inspect.
- * @param {*} target The value to search for.
- * @param {Function} comparator The comparator invoked per element.
- * @returns {boolean} Returns `true` if `target` is found, else `false`.
- */
- function arrayIncludesWith(array, value, comparator) {
- var index = -1,
- length = array == null ? 0 : array.length;
-
- while (++index < length) {
- if (comparator(value, array[index])) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * A specialized version of `_.map` for arrays without support for iteratee
- * shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array} Returns the new mapped array.
- */
- function arrayMap(array, iteratee) {
- var index = -1,
- length = array == null ? 0 : array.length,
- result = Array(length);
-
- while (++index < length) {
- result[index] = iteratee(array[index], index, array);
- }
- return result;
- }
-
- /**
- * Appends the elements of `values` to `array`.
- *
- * @private
- * @param {Array} array The array to modify.
- * @param {Array} values The values to append.
- * @returns {Array} Returns `array`.
- */
- function arrayPush(array, values) {
- var index = -1,
- length = values.length,
- offset = array.length;
-
- while (++index < length) {
- array[offset + index] = values[index];
- }
- return array;
- }
-
- /**
- * A specialized version of `_.reduce` for arrays without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @param {*} [accumulator] The initial value.
- * @param {boolean} [initAccum] Specify using the first element of `array` as
- * the initial value.
- * @returns {*} Returns the accumulated value.
- */
- function arrayReduce(array, iteratee, accumulator, initAccum) {
- var index = -1,
- length = array == null ? 0 : array.length;
-
- if (initAccum && length) {
- accumulator = array[++index];
- }
- while (++index < length) {
- accumulator = iteratee(accumulator, array[index], index, array);
- }
- return accumulator;
- }
-
- /**
- * A specialized version of `_.reduceRight` for arrays without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @param {*} [accumulator] The initial value.
- * @param {boolean} [initAccum] Specify using the last element of `array` as
- * the initial value.
- * @returns {*} Returns the accumulated value.
- */
- function arrayReduceRight(array, iteratee, accumulator, initAccum) {
- var length = array == null ? 0 : array.length;
- if (initAccum && length) {
- accumulator = array[--length];
- }
- while (length--) {
- accumulator = iteratee(accumulator, array[length], length, array);
- }
- return accumulator;
- }
-
- /**
- * A specialized version of `_.some` for arrays without support for iteratee
- * shorthands.
- *
- * @private
- * @param {Array} [array] The array to iterate over.
- * @param {Function} predicate The function invoked per iteration.
- * @returns {boolean} Returns `true` if any element passes the predicate check,
- * else `false`.
- */
- function arraySome(array, predicate) {
- var index = -1,
- length = array == null ? 0 : array.length;
-
- while (++index < length) {
- if (predicate(array[index], index, array)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Gets the size of an ASCII `string`.
- *
- * @private
- * @param {string} string The string inspect.
- * @returns {number} Returns the string size.
- */
- var asciiSize = baseProperty('length');
-
- /**
- * Converts an ASCII `string` to an array.
- *
- * @private
- * @param {string} string The string to convert.
- * @returns {Array} Returns the converted array.
- */
- function asciiToArray(string) {
- return string.split('');
- }
-
- /**
- * Splits an ASCII `string` into an array of its words.
- *
- * @private
- * @param {string} The string to inspect.
- * @returns {Array} Returns the words of `string`.
- */
- function asciiWords(string) {
- return string.match(reAsciiWord) || [];
- }
-
- /**
- * The base implementation of methods like `_.findKey` and `_.findLastKey`,
- * without support for iteratee shorthands, which iterates over `collection`
- * using `eachFunc`.
- *
- * @private
- * @param {Array|Object} collection The collection to inspect.
- * @param {Function} predicate The function invoked per iteration.
- * @param {Function} eachFunc The function to iterate over `collection`.
- * @returns {*} Returns the found element or its key, else `undefined`.
- */
- function baseFindKey(collection, predicate, eachFunc) {
- var result;
- eachFunc(collection, function(value, key, collection) {
- if (predicate(value, key, collection)) {
- result = key;
- return false;
- }
- });
- return result;
- }
-
- /**
- * The base implementation of `_.findIndex` and `_.findLastIndex` without
- * support for iteratee shorthands.
- *
- * @private
- * @param {Array} array The array to inspect.
- * @param {Function} predicate The function invoked per iteration.
- * @param {number} fromIndex The index to search from.
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {number} Returns the index of the matched value, else `-1`.
- */
- function baseFindIndex(array, predicate, fromIndex, fromRight) {
- var length = array.length,
- index = fromIndex + (fromRight ? 1 : -1);
-
- while ((fromRight ? index-- : ++index < length)) {
- if (predicate(array[index], index, array)) {
- return index;
- }
- }
- return -1;
- }
-
- /**
- * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
- *
- * @private
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @param {number} fromIndex The index to search from.
- * @returns {number} Returns the index of the matched value, else `-1`.
- */
- function baseIndexOf(array, value, fromIndex) {
- return value === value
- ? strictIndexOf(array, value, fromIndex)
- : baseFindIndex(array, baseIsNaN, fromIndex);
- }
-
- /**
- * This function is like `baseIndexOf` except that it accepts a comparator.
- *
- * @private
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @param {number} fromIndex The index to search from.
- * @param {Function} comparator The comparator invoked per element.
- * @returns {number} Returns the index of the matched value, else `-1`.
- */
- function baseIndexOfWith(array, value, fromIndex, comparator) {
- var index = fromIndex - 1,
- length = array.length;
-
- while (++index < length) {
- if (comparator(array[index], value)) {
- return index;
- }
- }
- return -1;
- }
-
- /**
- * The base implementation of `_.isNaN` without support for number objects.
- *
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
- */
- function baseIsNaN(value) {
- return value !== value;
- }
-
- /**
- * The base implementation of `_.mean` and `_.meanBy` without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} array The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {number} Returns the mean.
- */
- function baseMean(array, iteratee) {
- var length = array == null ? 0 : array.length;
- return length ? (baseSum(array, iteratee) / length) : NAN;
- }
-
- /**
- * The base implementation of `_.property` without support for deep paths.
- *
- * @private
- * @param {string} key The key of the property to get.
- * @returns {Function} Returns the new accessor function.
- */
- function baseProperty(key) {
- return function(object) {
- return object == null ? undefined : object[key];
- };
- }
-
- /**
- * The base implementation of `_.propertyOf` without support for deep paths.
- *
- * @private
- * @param {Object} object The object to query.
- * @returns {Function} Returns the new accessor function.
- */
- function basePropertyOf(object) {
- return function(key) {
- return object == null ? undefined : object[key];
- };
- }
-
- /**
- * The base implementation of `_.reduce` and `_.reduceRight`, without support
- * for iteratee shorthands, which iterates over `collection` using `eachFunc`.
- *
- * @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @param {*} accumulator The initial value.
- * @param {boolean} initAccum Specify using the first or last element of
- * `collection` as the initial value.
- * @param {Function} eachFunc The function to iterate over `collection`.
- * @returns {*} Returns the accumulated value.
- */
- function baseReduce(collection, iteratee, accumulator, initAccum, eachFunc) {
- eachFunc(collection, function(value, index, collection) {
- accumulator = initAccum
- ? (initAccum = false, value)
- : iteratee(accumulator, value, index, collection);
- });
- return accumulator;
- }
-
- /**
- * The base implementation of `_.sortBy` which uses `comparer` to define the
- * sort order of `array` and replaces criteria objects with their corresponding
- * values.
- *
- * @private
- * @param {Array} array The array to sort.
- * @param {Function} comparer The function to define sort order.
- * @returns {Array} Returns `array`.
- */
- function baseSortBy(array, comparer) {
- var length = array.length;
-
- array.sort(comparer);
- while (length--) {
- array[length] = array[length].value;
- }
- return array;
- }
-
- /**
- * The base implementation of `_.sum` and `_.sumBy` without support for
- * iteratee shorthands.
- *
- * @private
- * @param {Array} array The array to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {number} Returns the sum.
- */
- function baseSum(array, iteratee) {
- var result,
- index = -1,
- length = array.length;
-
- while (++index < length) {
- var current = iteratee(array[index]);
- if (current !== undefined) {
- result = result === undefined ? current : (result + current);
- }
- }
- return result;
- }
-
- /**
- * The base implementation of `_.times` without support for iteratee shorthands
- * or max array length checks.
- *
- * @private
- * @param {number} n The number of times to invoke `iteratee`.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array} Returns the array of results.
- */
- function baseTimes(n, iteratee) {
- var index = -1,
- result = Array(n);
-
- while (++index < n) {
- result[index] = iteratee(index);
- }
- return result;
- }
-
- /**
- * The base implementation of `_.toPairs` and `_.toPairsIn` which creates an array
- * of key-value pairs for `object` corresponding to the property names of `props`.
- *
- * @private
- * @param {Object} object The object to query.
- * @param {Array} props The property names to get values for.
- * @returns {Object} Returns the key-value pairs.
- */
- function baseToPairs(object, props) {
- return arrayMap(props, function(key) {
- return [key, object[key]];
- });
- }
-
- /**
- * The base implementation of `_.trim`.
- *
- * @private
- * @param {string} string The string to trim.
- * @returns {string} Returns the trimmed string.
- */
- function baseTrim(string) {
- return string
- ? string.slice(0, trimmedEndIndex(string) + 1).replace(reTrimStart, '')
- : string;
- }
-
- /**
- * The base implementation of `_.unary` without support for storing metadata.
- *
- * @private
- * @param {Function} func The function to cap arguments for.
- * @returns {Function} Returns the new capped function.
- */
- function baseUnary(func) {
- return function(value) {
- return func(value);
- };
- }
-
- /**
- * The base implementation of `_.values` and `_.valuesIn` which creates an
- * array of `object` property values corresponding to the property names
- * of `props`.
- *
- * @private
- * @param {Object} object The object to query.
- * @param {Array} props The property names to get values for.
- * @returns {Object} Returns the array of property values.
- */
- function baseValues(object, props) {
- return arrayMap(props, function(key) {
- return object[key];
- });
- }
-
- /**
- * Checks if a `cache` value for `key` exists.
- *
- * @private
- * @param {Object} cache The cache to query.
- * @param {string} key The key of the entry to check.
- * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
- */
- function cacheHas(cache, key) {
- return cache.has(key);
- }
-
- /**
- * Used by `_.trim` and `_.trimStart` to get the index of the first string symbol
- * that is not found in the character symbols.
- *
- * @private
- * @param {Array} strSymbols The string symbols to inspect.
- * @param {Array} chrSymbols The character symbols to find.
- * @returns {number} Returns the index of the first unmatched string symbol.
- */
- function charsStartIndex(strSymbols, chrSymbols) {
- var index = -1,
- length = strSymbols.length;
-
- while (++index < length && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
- return index;
- }
-
- /**
- * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
- * that is not found in the character symbols.
- *
- * @private
- * @param {Array} strSymbols The string symbols to inspect.
- * @param {Array} chrSymbols The character symbols to find.
- * @returns {number} Returns the index of the last unmatched string symbol.
- */
- function charsEndIndex(strSymbols, chrSymbols) {
- var index = strSymbols.length;
-
- while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
- return index;
- }
-
- /**
- * Gets the number of `placeholder` occurrences in `array`.
- *
- * @private
- * @param {Array} array The array to inspect.
- * @param {*} placeholder The placeholder to search for.
- * @returns {number} Returns the placeholder count.
- */
- function countHolders(array, placeholder) {
- var length = array.length,
- result = 0;
-
- while (length--) {
- if (array[length] === placeholder) {
- ++result;
- }
- }
- return result;
- }
-
- /**
- * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
- * letters to basic Latin letters.
- *
- * @private
- * @param {string} letter The matched letter to deburr.
- * @returns {string} Returns the deburred letter.
- */
- var deburrLetter = basePropertyOf(deburredLetters);
-
- /**
- * Used by `_.escape` to convert characters to HTML entities.
- *
- * @private
- * @param {string} chr The matched character to escape.
- * @returns {string} Returns the escaped character.
- */
- var escapeHtmlChar = basePropertyOf(htmlEscapes);
-
- /**
- * Used by `_.template` to escape characters for inclusion in compiled string literals.
- *
- * @private
- * @param {string} chr The matched character to escape.
- * @returns {string} Returns the escaped character.
- */
- function escapeStringChar(chr) {
- return '\\' + stringEscapes[chr];
- }
-
- /**
- * Gets the value at `key` of `object`.
- *
- * @private
- * @param {Object} [object] The object to query.
- * @param {string} key The key of the property to get.
- * @returns {*} Returns the property value.
- */
- function getValue(object, key) {
- return object == null ? undefined : object[key];
- }
-
- /**
- * Checks if `string` contains Unicode symbols.
- *
- * @private
- * @param {string} string The string to inspect.
- * @returns {boolean} Returns `true` if a symbol is found, else `false`.
- */
- function hasUnicode(string) {
- return reHasUnicode.test(string);
- }
-
- /**
- * Checks if `string` contains a word composed of Unicode symbols.
- *
- * @private
- * @param {string} string The string to inspect.
- * @returns {boolean} Returns `true` if a word is found, else `false`.
- */
- function hasUnicodeWord(string) {
- return reHasUnicodeWord.test(string);
- }
-
- /**
- * Converts `iterator` to an array.
- *
- * @private
- * @param {Object} iterator The iterator to convert.
- * @returns {Array} Returns the converted array.
- */
- function iteratorToArray(iterator) {
- var data,
- result = [];
-
- while (!(data = iterator.next()).done) {
- result.push(data.value);
- }
- return result;
- }
-
- /**
- * Converts `map` to its key-value pairs.
- *
- * @private
- * @param {Object} map The map to convert.
- * @returns {Array} Returns the key-value pairs.
- */
- function mapToArray(map) {
- var index = -1,
- result = Array(map.size);
-
- map.forEach(function(value, key) {
- result[++index] = [key, value];
- });
- return result;
- }
-
- /**
- * Creates a unary function that invokes `func` with its argument transformed.
- *
- * @private
- * @param {Function} func The function to wrap.
- * @param {Function} transform The argument transform.
- * @returns {Function} Returns the new function.
- */
- function overArg(func, transform) {
- return function(arg) {
- return func(transform(arg));
- };
- }
-
- /**
- * Replaces all `placeholder` elements in `array` with an internal placeholder
- * and returns an array of their indexes.
- *
- * @private
- * @param {Array} array The array to modify.
- * @param {*} placeholder The placeholder to replace.
- * @returns {Array} Returns the new array of placeholder indexes.
- */
- function replaceHolders(array, placeholder) {
- var index = -1,
- length = array.length,
- resIndex = 0,
- result = [];
-
- while (++index < length) {
- var value = array[index];
- if (value === placeholder || value === PLACEHOLDER) {
- array[index] = PLACEHOLDER;
- result[resIndex++] = index;
- }
- }
- return result;
- }
-
- /**
- * Converts `set` to an array of its values.
- *
- * @private
- * @param {Object} set The set to convert.
- * @returns {Array} Returns the values.
- */
- function setToArray(set) {
- var index = -1,
- result = Array(set.size);
-
- set.forEach(function(value) {
- result[++index] = value;
- });
- return result;
- }
-
- /**
- * Converts `set` to its value-value pairs.
- *
- * @private
- * @param {Object} set The set to convert.
- * @returns {Array} Returns the value-value pairs.
- */
- function setToPairs(set) {
- var index = -1,
- result = Array(set.size);
-
- set.forEach(function(value) {
- result[++index] = [value, value];
- });
- return result;
- }
-
- /**
- * A specialized version of `_.indexOf` which performs strict equality
- * comparisons of values, i.e. `===`.
- *
- * @private
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @param {number} fromIndex The index to search from.
- * @returns {number} Returns the index of the matched value, else `-1`.
- */
- function strictIndexOf(array, value, fromIndex) {
- var index = fromIndex - 1,
- length = array.length;
-
- while (++index < length) {
- if (array[index] === value) {
- return index;
- }
- }
- return -1;
- }
-
- /**
- * A specialized version of `_.lastIndexOf` which performs strict equality
- * comparisons of values, i.e. `===`.
- *
- * @private
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @param {number} fromIndex The index to search from.
- * @returns {number} Returns the index of the matched value, else `-1`.
- */
- function strictLastIndexOf(array, value, fromIndex) {
- var index = fromIndex + 1;
- while (index--) {
- if (array[index] === value) {
- return index;
- }
- }
- return index;
- }
-
- /**
- * Gets the number of symbols in `string`.
- *
- * @private
- * @param {string} string The string to inspect.
- * @returns {number} Returns the string size.
- */
- function stringSize(string) {
- return hasUnicode(string)
- ? unicodeSize(string)
- : asciiSize(string);
- }
-
- /**
- * Converts `string` to an array.
- *
- * @private
- * @param {string} string The string to convert.
- * @returns {Array} Returns the converted array.
- */
- function stringToArray(string) {
- return hasUnicode(string)
- ? unicodeToArray(string)
- : asciiToArray(string);
- }
-
- /**
- * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
- * character of `string`.
- *
- * @private
- * @param {string} string The string to inspect.
- * @returns {number} Returns the index of the last non-whitespace character.
- */
- function trimmedEndIndex(string) {
- var index = string.length;
-
- while (index-- && reWhitespace.test(string.charAt(index))) {}
- return index;
- }
-
- /**
- * Used by `_.unescape` to convert HTML entities to characters.
- *
- * @private
- * @param {string} chr The matched character to unescape.
- * @returns {string} Returns the unescaped character.
- */
- var unescapeHtmlChar = basePropertyOf(htmlUnescapes);
+ /**
+ * Used by `_.unescape` to convert HTML entities to characters.
+ *
+ * @private
+ * @param {string} chr The matched character to unescape.
+ * @returns {string} Returns the unescaped character.
+ */
+ var unescapeHtmlChar = basePropertyOf(htmlUnescapes);
/**
* Gets the size of a Unicode `string`.
@@ -9207,13 +6859,234 @@ module.exports = function kindOf(val) {
/*------------------------------------------------------------------------*/
/**
- * Creates a hash object.
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function Hash(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ /**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+ function hashClear() {
+ this.__data__ = nativeCreate ? nativeCreate(null) : {};
+ this.size = 0;
+ }
+
+ /**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function hashDelete(key) {
+ var result = this.has(key) && delete this.__data__[key];
+ this.size -= result ? 1 : 0;
+ return result;
+ }
+
+ /**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function hashGet(key) {
+ var data = this.__data__;
+ if (nativeCreate) {
+ var result = data[key];
+ return result === HASH_UNDEFINED ? undefined : result;
+ }
+ return hasOwnProperty.call(data, key) ? data[key] : undefined;
+ }
+
+ /**
+ * Checks if a hash value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Hash
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function hashHas(key) {
+ var data = this.__data__;
+ return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);
+ }
+
+ /**
+ * Sets the hash `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Hash
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the hash instance.
+ */
+ function hashSet(key, value) {
+ var data = this.__data__;
+ this.size += this.has(key) ? 0 : 1;
+ data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
+ return this;
+ }
+
+ // Add methods to `Hash`.
+ Hash.prototype.clear = hashClear;
+ Hash.prototype['delete'] = hashDelete;
+ Hash.prototype.get = hashGet;
+ Hash.prototype.has = hashHas;
+ Hash.prototype.set = hashSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function ListCache(entries) {
+ var index = -1,
+ length = entries == null ? 0 : entries.length;
+
+ this.clear();
+ while (++index < length) {
+ var entry = entries[index];
+ this.set(entry[0], entry[1]);
+ }
+ }
+
+ /**
+ * Removes all key-value entries from the list cache.
+ *
+ * @private
+ * @name clear
+ * @memberOf ListCache
+ */
+ function listCacheClear() {
+ this.__data__ = [];
+ this.size = 0;
+ }
+
+ /**
+ * Removes `key` and its value from the list cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf ListCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+ function listCacheDelete(key) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ if (index < 0) {
+ return false;
+ }
+ var lastIndex = data.length - 1;
+ if (index == lastIndex) {
+ data.pop();
+ } else {
+ splice.call(data, index, 1);
+ }
+ --this.size;
+ return true;
+ }
+
+ /**
+ * Gets the list cache value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf ListCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+ function listCacheGet(key) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ return index < 0 ? undefined : data[index][1];
+ }
+
+ /**
+ * Checks if a list cache value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf ListCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function listCacheHas(key) {
+ return assocIndexOf(this.__data__, key) > -1;
+ }
+
+ /**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */
+ function listCacheSet(key, value) {
+ var data = this.__data__,
+ index = assocIndexOf(data, key);
+
+ if (index < 0) {
+ ++this.size;
+ data.push([key, value]);
+ } else {
+ data[index][1] = value;
+ }
+ return this;
+ }
+
+ // Add methods to `ListCache`.
+ ListCache.prototype.clear = listCacheClear;
+ ListCache.prototype['delete'] = listCacheDelete;
+ ListCache.prototype.get = listCacheGet;
+ ListCache.prototype.has = listCacheHas;
+ ListCache.prototype.set = listCacheSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a map cache object to store key-value pairs.
*
* @private
* @constructor
* @param {Array} [entries] The key-value pairs to cache.
*/
- function Hash(entries) {
+ function MapCache(entries) {
var index = -1,
length = entries == null ? 0 : entries.length;
@@ -9225,27114 +7098,27796 @@ module.exports = function kindOf(val) {
}
/**
- * Removes all key-value entries from the hash.
+ * Removes all key-value entries from the map.
*
* @private
* @name clear
- * @memberOf Hash
+ * @memberOf MapCache
*/
- function hashClear() {
- this.__data__ = nativeCreate ? nativeCreate(null) : {};
+ function mapCacheClear() {
this.size = 0;
+ this.__data__ = {
+ 'hash': new Hash,
+ 'map': new (Map || ListCache),
+ 'string': new Hash
+ };
}
/**
- * Removes `key` and its value from the hash.
+ * Removes `key` and its value from the map.
*
* @private
* @name delete
- * @memberOf Hash
- * @param {Object} hash The hash to modify.
+ * @memberOf MapCache
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
- function hashDelete(key) {
- var result = this.has(key) && delete this.__data__[key];
+ function mapCacheDelete(key) {
+ var result = getMapData(this, key)['delete'](key);
this.size -= result ? 1 : 0;
return result;
}
/**
- * Gets the hash value for `key`.
+ * Gets the map value for `key`.
*
* @private
* @name get
- * @memberOf Hash
+ * @memberOf MapCache
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
- function hashGet(key) {
- var data = this.__data__;
- if (nativeCreate) {
- var result = data[key];
- return result === HASH_UNDEFINED ? undefined : result;
- }
- return hasOwnProperty.call(data, key) ? data[key] : undefined;
+ function mapCacheGet(key) {
+ return getMapData(this, key).get(key);
}
/**
- * Checks if a hash value for `key` exists.
+ * Checks if a map value for `key` exists.
*
* @private
* @name has
- * @memberOf Hash
+ * @memberOf MapCache
* @param {string} key The key of the entry to check.
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
*/
- function hashHas(key) {
- var data = this.__data__;
- return nativeCreate ? (data[key] !== undefined) : hasOwnProperty.call(data, key);
+ function mapCacheHas(key) {
+ return getMapData(this, key).has(key);
}
/**
- * Sets the hash `key` to `value`.
+ * Sets the map `key` to `value`.
*
* @private
* @name set
- * @memberOf Hash
+ * @memberOf MapCache
* @param {string} key The key of the value to set.
* @param {*} value The value to set.
- * @returns {Object} Returns the hash instance.
+ * @returns {Object} Returns the map cache instance.
*/
- function hashSet(key, value) {
- var data = this.__data__;
- this.size += this.has(key) ? 0 : 1;
- data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
+ function mapCacheSet(key, value) {
+ var data = getMapData(this, key),
+ size = data.size;
+
+ data.set(key, value);
+ this.size += data.size == size ? 0 : 1;
return this;
}
- // Add methods to `Hash`.
- Hash.prototype.clear = hashClear;
- Hash.prototype['delete'] = hashDelete;
- Hash.prototype.get = hashGet;
- Hash.prototype.has = hashHas;
- Hash.prototype.set = hashSet;
+ // Add methods to `MapCache`.
+ MapCache.prototype.clear = mapCacheClear;
+ MapCache.prototype['delete'] = mapCacheDelete;
+ MapCache.prototype.get = mapCacheGet;
+ MapCache.prototype.has = mapCacheHas;
+ MapCache.prototype.set = mapCacheSet;
/*------------------------------------------------------------------------*/
/**
- * Creates an list cache object.
+ *
+ * Creates an array cache object to store unique values.
*
* @private
* @constructor
- * @param {Array} [entries] The key-value pairs to cache.
+ * @param {Array} [values] The values to cache.
*/
- function ListCache(entries) {
+ function SetCache(values) {
var index = -1,
- length = entries == null ? 0 : entries.length;
+ length = values == null ? 0 : values.length;
- this.clear();
+ this.__data__ = new MapCache;
while (++index < length) {
- var entry = entries[index];
- this.set(entry[0], entry[1]);
+ this.add(values[index]);
}
}
/**
- * Removes all key-value entries from the list cache.
+ * Adds `value` to the array cache.
+ *
+ * @private
+ * @name add
+ * @memberOf SetCache
+ * @alias push
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache instance.
+ */
+ function setCacheAdd(value) {
+ this.__data__.set(value, HASH_UNDEFINED);
+ return this;
+ }
+
+ /**
+ * Checks if `value` is in the array cache.
+ *
+ * @private
+ * @name has
+ * @memberOf SetCache
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
+ */
+ function setCacheHas(value) {
+ return this.__data__.has(value);
+ }
+
+ // Add methods to `SetCache`.
+ SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
+ SetCache.prototype.has = setCacheHas;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates a stack cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+ function Stack(entries) {
+ var data = this.__data__ = new ListCache(entries);
+ this.size = data.size;
+ }
+
+ /**
+ * Removes all key-value entries from the stack.
*
* @private
* @name clear
- * @memberOf ListCache
+ * @memberOf Stack
*/
- function listCacheClear() {
- this.__data__ = [];
+ function stackClear() {
+ this.__data__ = new ListCache;
this.size = 0;
}
/**
- * Removes `key` and its value from the list cache.
+ * Removes `key` and its value from the stack.
*
* @private
* @name delete
- * @memberOf ListCache
+ * @memberOf Stack
* @param {string} key The key of the value to remove.
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
*/
- function listCacheDelete(key) {
+ function stackDelete(key) {
var data = this.__data__,
- index = assocIndexOf(data, key);
+ result = data['delete'](key);
- if (index < 0) {
- return false;
- }
- var lastIndex = data.length - 1;
- if (index == lastIndex) {
- data.pop();
- } else {
- splice.call(data, index, 1);
- }
- --this.size;
- return true;
+ this.size = data.size;
+ return result;
}
/**
- * Gets the list cache value for `key`.
+ * Gets the stack value for `key`.
*
* @private
* @name get
- * @memberOf ListCache
+ * @memberOf Stack
* @param {string} key The key of the value to get.
* @returns {*} Returns the entry value.
*/
- function listCacheGet(key) {
- var data = this.__data__,
- index = assocIndexOf(data, key);
+ function stackGet(key) {
+ return this.__data__.get(key);
+ }
- return index < 0 ? undefined : data[index][1];
+ /**
+ * Checks if a stack value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Stack
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+ function stackHas(key) {
+ return this.__data__.has(key);
+ }
+
+ /**
+ * Sets the stack `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Stack
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the stack cache instance.
+ */
+ function stackSet(key, value) {
+ var data = this.__data__;
+ if (data instanceof ListCache) {
+ var pairs = data.__data__;
+ if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
+ pairs.push([key, value]);
+ this.size = ++data.size;
+ return this;
+ }
+ data = this.__data__ = new MapCache(pairs);
+ }
+ data.set(key, value);
+ this.size = data.size;
+ return this;
+ }
+
+ // Add methods to `Stack`.
+ Stack.prototype.clear = stackClear;
+ Stack.prototype['delete'] = stackDelete;
+ Stack.prototype.get = stackGet;
+ Stack.prototype.has = stackHas;
+ Stack.prototype.set = stackSet;
+
+ /*------------------------------------------------------------------------*/
+
+ /**
+ * Creates an array of the enumerable property names of the array-like `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @param {boolean} inherited Specify returning inherited property names.
+ * @returns {Array} Returns the array of property names.
+ */
+ function arrayLikeKeys(value, inherited) {
+ var isArr = isArray(value),
+ isArg = !isArr && isArguments(value),
+ isBuff = !isArr && !isArg && isBuffer(value),
+ isType = !isArr && !isArg && !isBuff && isTypedArray(value),
+ skipIndexes = isArr || isArg || isBuff || isType,
+ result = skipIndexes ? baseTimes(value.length, String) : [],
+ length = result.length;
+
+ for (var key in value) {
+ if ((inherited || hasOwnProperty.call(value, key)) &&
+ !(skipIndexes && (
+ // Safari 9 has enumerable `arguments.length` in strict mode.
+ key == 'length' ||
+ // Node.js 0.10 has enumerable non-index properties on buffers.
+ (isBuff && (key == 'offset' || key == 'parent')) ||
+ // PhantomJS 2 has enumerable non-index properties on typed arrays.
+ (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
+ // Skip index properties.
+ isIndex(key, length)
+ ))) {
+ result.push(key);
+ }
+ }
+ return result;
}
/**
- * Checks if a list cache value for `key` exists.
+ * A specialized version of `_.sample` for arrays.
*
* @private
- * @name has
- * @memberOf ListCache
- * @param {string} key The key of the entry to check.
- * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ * @param {Array} array The array to sample.
+ * @returns {*} Returns the random element.
*/
- function listCacheHas(key) {
- return assocIndexOf(this.__data__, key) > -1;
+ function arraySample(array) {
+ var length = array.length;
+ return length ? array[baseRandom(0, length - 1)] : undefined;
}
/**
- * Sets the list cache `key` to `value`.
+ * A specialized version of `_.sampleSize` for arrays.
*
* @private
- * @name set
- * @memberOf ListCache
- * @param {string} key The key of the value to set.
- * @param {*} value The value to set.
- * @returns {Object} Returns the list cache instance.
+ * @param {Array} array The array to sample.
+ * @param {number} n The number of elements to sample.
+ * @returns {Array} Returns the random elements.
*/
- function listCacheSet(key, value) {
- var data = this.__data__,
- index = assocIndexOf(data, key);
-
- if (index < 0) {
- ++this.size;
- data.push([key, value]);
- } else {
- data[index][1] = value;
- }
- return this;
+ function arraySampleSize(array, n) {
+ return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));
}
- // Add methods to `ListCache`.
- ListCache.prototype.clear = listCacheClear;
- ListCache.prototype['delete'] = listCacheDelete;
- ListCache.prototype.get = listCacheGet;
- ListCache.prototype.has = listCacheHas;
- ListCache.prototype.set = listCacheSet;
-
- /*------------------------------------------------------------------------*/
-
/**
- * Creates a map cache object to store key-value pairs.
+ * A specialized version of `_.shuffle` for arrays.
*
* @private
- * @constructor
- * @param {Array} [entries] The key-value pairs to cache.
+ * @param {Array} array The array to shuffle.
+ * @returns {Array} Returns the new shuffled array.
*/
- function MapCache(entries) {
- var index = -1,
- length = entries == null ? 0 : entries.length;
-
- this.clear();
- while (++index < length) {
- var entry = entries[index];
- this.set(entry[0], entry[1]);
- }
+ function arrayShuffle(array) {
+ return shuffleSelf(copyArray(array));
}
/**
- * Removes all key-value entries from the map.
+ * This function is like `assignValue` except that it doesn't assign
+ * `undefined` values.
*
* @private
- * @name clear
- * @memberOf MapCache
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
*/
- function mapCacheClear() {
- this.size = 0;
- this.__data__ = {
- 'hash': new Hash,
- 'map': new (Map || ListCache),
- 'string': new Hash
- };
+ function assignMergeValue(object, key, value) {
+ if ((value !== undefined && !eq(object[key], value)) ||
+ (value === undefined && !(key in object))) {
+ baseAssignValue(object, key, value);
+ }
}
/**
- * Removes `key` and its value from the map.
+ * Assigns `value` to `key` of `object` if the existing value is not equivalent
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
*
* @private
- * @name delete
- * @memberOf MapCache
- * @param {string} key The key of the value to remove.
- * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
*/
- function mapCacheDelete(key) {
- var result = getMapData(this, key)['delete'](key);
- this.size -= result ? 1 : 0;
- return result;
+ function assignValue(object, key, value) {
+ var objValue = object[key];
+ if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
+ (value === undefined && !(key in object))) {
+ baseAssignValue(object, key, value);
+ }
}
/**
- * Gets the map value for `key`.
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
*
* @private
- * @name get
- * @memberOf MapCache
- * @param {string} key The key of the value to get.
- * @returns {*} Returns the entry value.
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
*/
- function mapCacheGet(key) {
- return getMapData(this, key).get(key);
+ function assocIndexOf(array, key) {
+ var length = array.length;
+ while (length--) {
+ if (eq(array[length][0], key)) {
+ return length;
+ }
+ }
+ return -1;
}
/**
- * Checks if a map value for `key` exists.
+ * Aggregates elements of `collection` on `accumulator` with keys transformed
+ * by `iteratee` and values set by `setter`.
*
* @private
- * @name has
- * @memberOf MapCache
- * @param {string} key The key of the entry to check.
- * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform keys.
+ * @param {Object} accumulator The initial aggregated object.
+ * @returns {Function} Returns `accumulator`.
*/
- function mapCacheHas(key) {
- return getMapData(this, key).has(key);
+ function baseAggregator(collection, setter, iteratee, accumulator) {
+ baseEach(collection, function(value, key, collection) {
+ setter(accumulator, value, iteratee(value), collection);
+ });
+ return accumulator;
}
/**
- * Sets the map `key` to `value`.
+ * The base implementation of `_.assign` without support for multiple sources
+ * or `customizer` functions.
*
* @private
- * @name set
- * @memberOf MapCache
- * @param {string} key The key of the value to set.
- * @param {*} value The value to set.
- * @returns {Object} Returns the map cache instance.
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @returns {Object} Returns `object`.
*/
- function mapCacheSet(key, value) {
- var data = getMapData(this, key),
- size = data.size;
-
- data.set(key, value);
- this.size += data.size == size ? 0 : 1;
- return this;
+ function baseAssign(object, source) {
+ return object && copyObject(source, keys(source), object);
}
- // Add methods to `MapCache`.
- MapCache.prototype.clear = mapCacheClear;
- MapCache.prototype['delete'] = mapCacheDelete;
- MapCache.prototype.get = mapCacheGet;
- MapCache.prototype.has = mapCacheHas;
- MapCache.prototype.set = mapCacheSet;
-
- /*------------------------------------------------------------------------*/
-
/**
- *
- * Creates an array cache object to store unique values.
+ * The base implementation of `_.assignIn` without support for multiple sources
+ * or `customizer` functions.
*
* @private
- * @constructor
- * @param {Array} [values] The values to cache.
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @returns {Object} Returns `object`.
*/
- function SetCache(values) {
- var index = -1,
- length = values == null ? 0 : values.length;
-
- this.__data__ = new MapCache;
- while (++index < length) {
- this.add(values[index]);
- }
+ function baseAssignIn(object, source) {
+ return object && copyObject(source, keysIn(source), object);
}
/**
- * Adds `value` to the array cache.
+ * The base implementation of `assignValue` and `assignMergeValue` without
+ * value checks.
*
* @private
- * @name add
- * @memberOf SetCache
- * @alias push
- * @param {*} value The value to cache.
- * @returns {Object} Returns the cache instance.
+ * @param {Object} object The object to modify.
+ * @param {string} key The key of the property to assign.
+ * @param {*} value The value to assign.
*/
- function setCacheAdd(value) {
- this.__data__.set(value, HASH_UNDEFINED);
- return this;
+ function baseAssignValue(object, key, value) {
+ if (key == '__proto__' && defineProperty) {
+ defineProperty(object, key, {
+ 'configurable': true,
+ 'enumerable': true,
+ 'value': value,
+ 'writable': true
+ });
+ } else {
+ object[key] = value;
+ }
}
/**
- * Checks if `value` is in the array cache.
+ * The base implementation of `_.at` without support for individual paths.
*
* @private
- * @name has
- * @memberOf SetCache
- * @param {*} value The value to search for.
- * @returns {number} Returns `true` if `value` is found, else `false`.
+ * @param {Object} object The object to iterate over.
+ * @param {string[]} paths The property paths to pick.
+ * @returns {Array} Returns the picked elements.
*/
- function setCacheHas(value) {
- return this.__data__.has(value);
- }
-
- // Add methods to `SetCache`.
- SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
- SetCache.prototype.has = setCacheHas;
-
- /*------------------------------------------------------------------------*/
+ function baseAt(object, paths) {
+ var index = -1,
+ length = paths.length,
+ result = Array(length),
+ skip = object == null;
- /**
- * Creates a stack cache object to store key-value pairs.
- *
- * @private
- * @constructor
- * @param {Array} [entries] The key-value pairs to cache.
- */
- function Stack(entries) {
- var data = this.__data__ = new ListCache(entries);
- this.size = data.size;
+ while (++index < length) {
+ result[index] = skip ? undefined : get(object, paths[index]);
+ }
+ return result;
}
/**
- * Removes all key-value entries from the stack.
+ * The base implementation of `_.clamp` which doesn't coerce arguments.
*
- * @private
- * @name clear
- * @memberOf Stack
+ * @private
+ * @param {number} number The number to clamp.
+ * @param {number} [lower] The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the clamped number.
*/
- function stackClear() {
- this.__data__ = new ListCache;
- this.size = 0;
+ function baseClamp(number, lower, upper) {
+ if (number === number) {
+ if (upper !== undefined) {
+ number = number <= upper ? number : upper;
+ }
+ if (lower !== undefined) {
+ number = number >= lower ? number : lower;
+ }
+ }
+ return number;
}
/**
- * Removes `key` and its value from the stack.
+ * The base implementation of `_.clone` and `_.cloneDeep` which tracks
+ * traversed objects.
*
* @private
- * @name delete
- * @memberOf Stack
- * @param {string} key The key of the value to remove.
- * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ * @param {*} value The value to clone.
+ * @param {boolean} bitmask The bitmask flags.
+ * 1 - Deep clone
+ * 2 - Flatten inherited properties
+ * 4 - Clone symbols
+ * @param {Function} [customizer] The function to customize cloning.
+ * @param {string} [key] The key of `value`.
+ * @param {Object} [object] The parent object of `value`.
+ * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
+ * @returns {*} Returns the cloned value.
*/
- function stackDelete(key) {
- var data = this.__data__,
- result = data['delete'](key);
+ function baseClone(value, bitmask, customizer, key, object, stack) {
+ var result,
+ isDeep = bitmask & CLONE_DEEP_FLAG,
+ isFlat = bitmask & CLONE_FLAT_FLAG,
+ isFull = bitmask & CLONE_SYMBOLS_FLAG;
- this.size = data.size;
+ if (customizer) {
+ result = object ? customizer(value, key, object, stack) : customizer(value);
+ }
+ if (result !== undefined) {
+ return result;
+ }
+ if (!isObject(value)) {
+ return value;
+ }
+ var isArr = isArray(value);
+ if (isArr) {
+ result = initCloneArray(value);
+ if (!isDeep) {
+ return copyArray(value, result);
+ }
+ } else {
+ var tag = getTag(value),
+ isFunc = tag == funcTag || tag == genTag;
+
+ if (isBuffer(value)) {
+ return cloneBuffer(value, isDeep);
+ }
+ if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
+ result = (isFlat || isFunc) ? {} : initCloneObject(value);
+ if (!isDeep) {
+ return isFlat
+ ? copySymbolsIn(value, baseAssignIn(result, value))
+ : copySymbols(value, baseAssign(result, value));
+ }
+ } else {
+ if (!cloneableTags[tag]) {
+ return object ? value : {};
+ }
+ result = initCloneByTag(value, tag, baseClone, isDeep);
+ }
+ }
+ // Check for circular references and return its corresponding clone.
+ stack || (stack = new Stack);
+ var stacked = stack.get(value);
+ if (stacked) {
+ return stacked;
+ }
+ stack.set(value, result);
+
+ var keysFunc = isFull
+ ? (isFlat ? getAllKeysIn : getAllKeys)
+ : (isFlat ? keysIn : keys);
+
+ var props = isArr ? undefined : keysFunc(value);
+ arrayEach(props || value, function(subValue, key) {
+ if (props) {
+ key = subValue;
+ subValue = value[key];
+ }
+ // Recursively populate clone (susceptible to call stack limits).
+ assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
+ });
return result;
}
/**
- * Gets the stack value for `key`.
+ * The base implementation of `_.conforms` which doesn't clone `source`.
*
* @private
- * @name get
- * @memberOf Stack
- * @param {string} key The key of the value to get.
- * @returns {*} Returns the entry value.
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {Function} Returns the new spec function.
*/
- function stackGet(key) {
- return this.__data__.get(key);
+ function baseConforms(source) {
+ var props = keys(source);
+ return function(object) {
+ return baseConformsTo(object, source, props);
+ };
}
/**
- * Checks if a stack value for `key` exists.
+ * The base implementation of `_.conformsTo` which accepts `props` to check.
*
* @private
- * @name has
- * @memberOf Stack
- * @param {string} key The key of the entry to check.
- * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {boolean} Returns `true` if `object` conforms, else `false`.
*/
- function stackHas(key) {
- return this.__data__.has(key);
+ function baseConformsTo(object, source, props) {
+ var length = props.length;
+ if (object == null) {
+ return !length;
+ }
+ object = Object(object);
+ while (length--) {
+ var key = props[length],
+ predicate = source[key],
+ value = object[key];
+
+ if ((value === undefined && !(key in object)) || !predicate(value)) {
+ return false;
+ }
+ }
+ return true;
}
/**
- * Sets the stack `key` to `value`.
+ * The base implementation of `_.delay` and `_.defer` which accepts `args`
+ * to provide to `func`.
*
* @private
- * @name set
- * @memberOf Stack
- * @param {string} key The key of the value to set.
- * @param {*} value The value to set.
- * @returns {Object} Returns the stack cache instance.
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {Array} args The arguments to provide to `func`.
+ * @returns {number|Object} Returns the timer id or timeout object.
*/
- function stackSet(key, value) {
- var data = this.__data__;
- if (data instanceof ListCache) {
- var pairs = data.__data__;
- if (!Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
- pairs.push([key, value]);
- this.size = ++data.size;
- return this;
- }
- data = this.__data__ = new MapCache(pairs);
+ function baseDelay(func, wait, args) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
}
- data.set(key, value);
- this.size = data.size;
- return this;
+ return setTimeout(function() { func.apply(undefined, args); }, wait);
}
- // Add methods to `Stack`.
- Stack.prototype.clear = stackClear;
- Stack.prototype['delete'] = stackDelete;
- Stack.prototype.get = stackGet;
- Stack.prototype.has = stackHas;
- Stack.prototype.set = stackSet;
-
- /*------------------------------------------------------------------------*/
-
/**
- * Creates an array of the enumerable property names of the array-like `value`.
+ * The base implementation of methods like `_.difference` without support
+ * for excluding multiple arrays or iteratee shorthands.
*
* @private
- * @param {*} value The value to query.
- * @param {boolean} inherited Specify returning inherited property names.
- * @returns {Array} Returns the array of property names.
+ * @param {Array} array The array to inspect.
+ * @param {Array} values The values to exclude.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
*/
- function arrayLikeKeys(value, inherited) {
- var isArr = isArray(value),
- isArg = !isArr && isArguments(value),
- isBuff = !isArr && !isArg && isBuffer(value),
- isType = !isArr && !isArg && !isBuff && isTypedArray(value),
- skipIndexes = isArr || isArg || isBuff || isType,
- result = skipIndexes ? baseTimes(value.length, String) : [],
- length = result.length;
+ function baseDifference(array, values, iteratee, comparator) {
+ var index = -1,
+ includes = arrayIncludes,
+ isCommon = true,
+ length = array.length,
+ result = [],
+ valuesLength = values.length;
- for (var key in value) {
- if ((inherited || hasOwnProperty.call(value, key)) &&
- !(skipIndexes && (
- // Safari 9 has enumerable `arguments.length` in strict mode.
- key == 'length' ||
- // Node.js 0.10 has enumerable non-index properties on buffers.
- (isBuff && (key == 'offset' || key == 'parent')) ||
- // PhantomJS 2 has enumerable non-index properties on typed arrays.
- (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
- // Skip index properties.
- isIndex(key, length)
- ))) {
- result.push(key);
+ if (!length) {
+ return result;
+ }
+ if (iteratee) {
+ values = arrayMap(values, baseUnary(iteratee));
+ }
+ if (comparator) {
+ includes = arrayIncludesWith;
+ isCommon = false;
+ }
+ else if (values.length >= LARGE_ARRAY_SIZE) {
+ includes = cacheHas;
+ isCommon = false;
+ values = new SetCache(values);
+ }
+ outer:
+ while (++index < length) {
+ var value = array[index],
+ computed = iteratee == null ? value : iteratee(value);
+
+ value = (comparator || value !== 0) ? value : 0;
+ if (isCommon && computed === computed) {
+ var valuesIndex = valuesLength;
+ while (valuesIndex--) {
+ if (values[valuesIndex] === computed) {
+ continue outer;
+ }
+ }
+ result.push(value);
+ }
+ else if (!includes(values, computed, comparator)) {
+ result.push(value);
}
}
return result;
}
/**
- * A specialized version of `_.sample` for arrays.
+ * The base implementation of `_.forEach` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ */
+ var baseEach = createBaseEach(baseForOwn);
+
+ /**
+ * The base implementation of `_.forEachRight` without support for iteratee shorthands.
*
* @private
- * @param {Array} array The array to sample.
- * @returns {*} Returns the random element.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
*/
- function arraySample(array) {
- var length = array.length;
- return length ? array[baseRandom(0, length - 1)] : undefined;
- }
+ var baseEachRight = createBaseEach(baseForOwnRight, true);
/**
- * A specialized version of `_.sampleSize` for arrays.
+ * The base implementation of `_.every` without support for iteratee shorthands.
*
* @private
- * @param {Array} array The array to sample.
- * @param {number} n The number of elements to sample.
- * @returns {Array} Returns the random elements.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`
*/
- function arraySampleSize(array, n) {
- return shuffleSelf(copyArray(array), baseClamp(n, 0, array.length));
+ function baseEvery(collection, predicate) {
+ var result = true;
+ baseEach(collection, function(value, index, collection) {
+ result = !!predicate(value, index, collection);
+ return result;
+ });
+ return result;
}
/**
- * A specialized version of `_.shuffle` for arrays.
+ * The base implementation of methods like `_.max` and `_.min` which accepts a
+ * `comparator` to determine the extremum value.
*
* @private
- * @param {Array} array The array to shuffle.
- * @returns {Array} Returns the new shuffled array.
+ * @param {Array} array The array to iterate over.
+ * @param {Function} iteratee The iteratee invoked per iteration.
+ * @param {Function} comparator The comparator used to compare values.
+ * @returns {*} Returns the extremum value.
*/
- function arrayShuffle(array) {
- return shuffleSelf(copyArray(array));
+ function baseExtremum(array, iteratee, comparator) {
+ var index = -1,
+ length = array.length;
+
+ while (++index < length) {
+ var value = array[index],
+ current = iteratee(value);
+
+ if (current != null && (computed === undefined
+ ? (current === current && !isSymbol(current))
+ : comparator(current, computed)
+ )) {
+ var computed = current,
+ result = value;
+ }
+ }
+ return result;
}
/**
- * This function is like `assignValue` except that it doesn't assign
- * `undefined` values.
+ * The base implementation of `_.fill` without an iteratee call guard.
*
* @private
- * @param {Object} object The object to modify.
- * @param {string} key The key of the property to assign.
- * @param {*} value The value to assign.
+ * @param {Array} array The array to fill.
+ * @param {*} value The value to fill `array` with.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns `array`.
*/
- function assignMergeValue(object, key, value) {
- if ((value !== undefined && !eq(object[key], value)) ||
- (value === undefined && !(key in object))) {
- baseAssignValue(object, key, value);
+ function baseFill(array, value, start, end) {
+ var length = array.length;
+
+ start = toInteger(start);
+ if (start < 0) {
+ start = -start > length ? 0 : (length + start);
+ }
+ end = (end === undefined || end > length) ? length : toInteger(end);
+ if (end < 0) {
+ end += length;
}
+ end = start > end ? 0 : toLength(end);
+ while (start < end) {
+ array[start++] = value;
+ }
+ return array;
}
/**
- * Assigns `value` to `key` of `object` if the existing value is not equivalent
- * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons.
+ * The base implementation of `_.filter` without support for iteratee shorthands.
*
* @private
- * @param {Object} object The object to modify.
- * @param {string} key The key of the property to assign.
- * @param {*} value The value to assign.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
*/
- function assignValue(object, key, value) {
- var objValue = object[key];
- if (!(hasOwnProperty.call(object, key) && eq(objValue, value)) ||
- (value === undefined && !(key in object))) {
- baseAssignValue(object, key, value);
- }
+ function baseFilter(collection, predicate) {
+ var result = [];
+ baseEach(collection, function(value, index, collection) {
+ if (predicate(value, index, collection)) {
+ result.push(value);
+ }
+ });
+ return result;
}
/**
- * Gets the index at which the `key` is found in `array` of key-value pairs.
+ * The base implementation of `_.flatten` with support for restricting flattening.
*
* @private
- * @param {Array} array The array to inspect.
- * @param {*} key The key to search for.
- * @returns {number} Returns the index of the matched value, else `-1`.
+ * @param {Array} array The array to flatten.
+ * @param {number} depth The maximum recursion depth.
+ * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
+ * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
+ * @param {Array} [result=[]] The initial result value.
+ * @returns {Array} Returns the new flattened array.
*/
- function assocIndexOf(array, key) {
- var length = array.length;
- while (length--) {
- if (eq(array[length][0], key)) {
- return length;
+ function baseFlatten(array, depth, predicate, isStrict, result) {
+ var index = -1,
+ length = array.length;
+
+ predicate || (predicate = isFlattenable);
+ result || (result = []);
+
+ while (++index < length) {
+ var value = array[index];
+ if (depth > 0 && predicate(value)) {
+ if (depth > 1) {
+ // Recursively flatten arrays (susceptible to call stack limits).
+ baseFlatten(value, depth - 1, predicate, isStrict, result);
+ } else {
+ arrayPush(result, value);
+ }
+ } else if (!isStrict) {
+ result[result.length] = value;
}
}
- return -1;
+ return result;
}
/**
- * Aggregates elements of `collection` on `accumulator` with keys transformed
- * by `iteratee` and values set by `setter`.
+ * The base implementation of `baseForOwn` which iterates over `object`
+ * properties returned by `keysFunc` and invokes `iteratee` for each property.
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} setter The function to set `accumulator` values.
- * @param {Function} iteratee The iteratee to transform keys.
- * @param {Object} accumulator The initial aggregated object.
- * @returns {Function} Returns `accumulator`.
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns `object`.
*/
- function baseAggregator(collection, setter, iteratee, accumulator) {
- baseEach(collection, function(value, key, collection) {
- setter(accumulator, value, iteratee(value), collection);
- });
- return accumulator;
- }
+ var baseFor = createBaseFor();
/**
- * The base implementation of `_.assign` without support for multiple sources
- * or `customizer` functions.
+ * This function is like `baseFor` except that it iterates over properties
+ * in the opposite order.
*
* @private
- * @param {Object} object The destination object.
- * @param {Object} source The source object.
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
- function baseAssign(object, source) {
- return object && copyObject(source, keys(source), object);
+ var baseForRight = createBaseFor(true);
+
+ /**
+ * The base implementation of `_.forOwn` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwn(object, iteratee) {
+ return object && baseFor(object, iteratee, keys);
}
/**
- * The base implementation of `_.assignIn` without support for multiple sources
- * or `customizer` functions.
+ * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
*
* @private
- * @param {Object} object The destination object.
- * @param {Object} source The source object.
+ * @param {Object} object The object to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
* @returns {Object} Returns `object`.
*/
- function baseAssignIn(object, source) {
- return object && copyObject(source, keysIn(source), object);
+ function baseForOwnRight(object, iteratee) {
+ return object && baseForRight(object, iteratee, keys);
}
/**
- * The base implementation of `assignValue` and `assignMergeValue` without
- * value checks.
+ * The base implementation of `_.functions` which creates an array of
+ * `object` function property names filtered from `props`.
*
* @private
- * @param {Object} object The object to modify.
- * @param {string} key The key of the property to assign.
- * @param {*} value The value to assign.
+ * @param {Object} object The object to inspect.
+ * @param {Array} props The property names to filter.
+ * @returns {Array} Returns the function names.
*/
- function baseAssignValue(object, key, value) {
- if (key == '__proto__' && defineProperty) {
- defineProperty(object, key, {
- 'configurable': true,
- 'enumerable': true,
- 'value': value,
- 'writable': true
- });
- } else {
- object[key] = value;
- }
+ function baseFunctions(object, props) {
+ return arrayFilter(props, function(key) {
+ return isFunction(object[key]);
+ });
}
/**
- * The base implementation of `_.at` without support for individual paths.
+ * The base implementation of `_.get` without support for default values.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {string[]} paths The property paths to pick.
- * @returns {Array} Returns the picked elements.
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @returns {*} Returns the resolved value.
*/
- function baseAt(object, paths) {
- var index = -1,
- length = paths.length,
- result = Array(length),
- skip = object == null;
+ function baseGet(object, path) {
+ path = castPath(path, object);
- while (++index < length) {
- result[index] = skip ? undefined : get(object, paths[index]);
+ var index = 0,
+ length = path.length;
+
+ while (object != null && index < length) {
+ object = object[toKey(path[index++])];
}
- return result;
+ return (index && index == length) ? object : undefined;
}
/**
- * The base implementation of `_.clamp` which doesn't coerce arguments.
+ * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
+ * `keysFunc` and `symbolsFunc` to get the enumerable property names and
+ * symbols of `object`.
*
* @private
- * @param {number} number The number to clamp.
- * @param {number} [lower] The lower bound.
- * @param {number} upper The upper bound.
- * @returns {number} Returns the clamped number.
+ * @param {Object} object The object to query.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @param {Function} symbolsFunc The function to get the symbols of `object`.
+ * @returns {Array} Returns the array of property names and symbols.
*/
- function baseClamp(number, lower, upper) {
- if (number === number) {
- if (upper !== undefined) {
- number = number <= upper ? number : upper;
- }
- if (lower !== undefined) {
- number = number >= lower ? number : lower;
- }
- }
- return number;
+ function baseGetAllKeys(object, keysFunc, symbolsFunc) {
+ var result = keysFunc(object);
+ return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
}
/**
- * The base implementation of `_.clone` and `_.cloneDeep` which tracks
- * traversed objects.
+ * The base implementation of `getTag` without fallbacks for buggy environments.
*
* @private
- * @param {*} value The value to clone.
- * @param {boolean} bitmask The bitmask flags.
- * 1 - Deep clone
- * 2 - Flatten inherited properties
- * 4 - Clone symbols
- * @param {Function} [customizer] The function to customize cloning.
- * @param {string} [key] The key of `value`.
- * @param {Object} [object] The parent object of `value`.
- * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
- * @returns {*} Returns the cloned value.
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
*/
- function baseClone(value, bitmask, customizer, key, object, stack) {
- var result,
- isDeep = bitmask & CLONE_DEEP_FLAG,
- isFlat = bitmask & CLONE_FLAT_FLAG,
- isFull = bitmask & CLONE_SYMBOLS_FLAG;
-
- if (customizer) {
- result = object ? customizer(value, key, object, stack) : customizer(value);
- }
- if (result !== undefined) {
- return result;
- }
- if (!isObject(value)) {
- return value;
- }
- var isArr = isArray(value);
- if (isArr) {
- result = initCloneArray(value);
- if (!isDeep) {
- return copyArray(value, result);
- }
- } else {
- var tag = getTag(value),
- isFunc = tag == funcTag || tag == genTag;
-
- if (isBuffer(value)) {
- return cloneBuffer(value, isDeep);
- }
- if (tag == objectTag || tag == argsTag || (isFunc && !object)) {
- result = (isFlat || isFunc) ? {} : initCloneObject(value);
- if (!isDeep) {
- return isFlat
- ? copySymbolsIn(value, baseAssignIn(result, value))
- : copySymbols(value, baseAssign(result, value));
- }
- } else {
- if (!cloneableTags[tag]) {
- return object ? value : {};
- }
- result = initCloneByTag(value, tag, isDeep);
- }
- }
- // Check for circular references and return its corresponding clone.
- stack || (stack = new Stack);
- var stacked = stack.get(value);
- if (stacked) {
- return stacked;
- }
- stack.set(value, result);
-
- if (isSet(value)) {
- value.forEach(function(subValue) {
- result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
- });
- } else if (isMap(value)) {
- value.forEach(function(subValue, key) {
- result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
- });
+ function baseGetTag(value) {
+ if (value == null) {
+ return value === undefined ? undefinedTag : nullTag;
}
-
- var keysFunc = isFull
- ? (isFlat ? getAllKeysIn : getAllKeys)
- : (isFlat ? keysIn : keys);
-
- var props = isArr ? undefined : keysFunc(value);
- arrayEach(props || value, function(subValue, key) {
- if (props) {
- key = subValue;
- subValue = value[key];
- }
- // Recursively populate clone (susceptible to call stack limits).
- assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
- });
- return result;
+ return (symToStringTag && symToStringTag in Object(value))
+ ? getRawTag(value)
+ : objectToString(value);
}
/**
- * The base implementation of `_.conforms` which doesn't clone `source`.
+ * The base implementation of `_.gt` which doesn't coerce arguments.
*
* @private
- * @param {Object} source The object of property predicates to conform to.
- * @returns {Function} Returns the new spec function.
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is greater than `other`,
+ * else `false`.
*/
- function baseConforms(source) {
- var props = keys(source);
- return function(object) {
- return baseConformsTo(object, source, props);
- };
+ function baseGt(value, other) {
+ return value > other;
}
/**
- * The base implementation of `_.conformsTo` which accepts `props` to check.
+ * The base implementation of `_.has` without support for deep paths.
*
* @private
- * @param {Object} object The object to inspect.
- * @param {Object} source The object of property predicates to conform to.
- * @returns {boolean} Returns `true` if `object` conforms, else `false`.
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
*/
- function baseConformsTo(object, source, props) {
- var length = props.length;
- if (object == null) {
- return !length;
- }
- object = Object(object);
- while (length--) {
- var key = props[length],
- predicate = source[key],
- value = object[key];
+ function baseHas(object, key) {
+ return object != null && hasOwnProperty.call(object, key);
+ }
- if ((value === undefined && !(key in object)) || !predicate(value)) {
- return false;
- }
- }
- return true;
+ /**
+ * The base implementation of `_.hasIn` without support for deep paths.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {Array|string} key The key to check.
+ * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ */
+ function baseHasIn(object, key) {
+ return object != null && key in Object(object);
}
/**
- * The base implementation of `_.delay` and `_.defer` which accepts `args`
- * to provide to `func`.
+ * The base implementation of `_.inRange` which doesn't coerce arguments.
*
* @private
- * @param {Function} func The function to delay.
- * @param {number} wait The number of milliseconds to delay invocation.
- * @param {Array} args The arguments to provide to `func`.
- * @returns {number|Object} Returns the timer id or timeout object.
+ * @param {number} number The number to check.
+ * @param {number} start The start of the range.
+ * @param {number} end The end of the range.
+ * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
*/
- function baseDelay(func, wait, args) {
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- return setTimeout(function() { func.apply(undefined, args); }, wait);
+ function baseInRange(number, start, end) {
+ return number >= nativeMin(start, end) && number < nativeMax(start, end);
}
/**
- * The base implementation of methods like `_.difference` without support
- * for excluding multiple arrays or iteratee shorthands.
+ * The base implementation of methods like `_.intersection`, without support
+ * for iteratee shorthands, that accepts an array of arrays to inspect.
*
* @private
- * @param {Array} array The array to inspect.
- * @param {Array} values The values to exclude.
+ * @param {Array} arrays The arrays to inspect.
* @param {Function} [iteratee] The iteratee invoked per element.
* @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @returns {Array} Returns the new array of shared values.
*/
- function baseDifference(array, values, iteratee, comparator) {
- var index = -1,
- includes = arrayIncludes,
- isCommon = true,
- length = array.length,
- result = [],
- valuesLength = values.length;
+ function baseIntersection(arrays, iteratee, comparator) {
+ var includes = comparator ? arrayIncludesWith : arrayIncludes,
+ length = arrays[0].length,
+ othLength = arrays.length,
+ othIndex = othLength,
+ caches = Array(othLength),
+ maxLength = Infinity,
+ result = [];
- if (!length) {
- return result;
- }
- if (iteratee) {
- values = arrayMap(values, baseUnary(iteratee));
- }
- if (comparator) {
- includes = arrayIncludesWith;
- isCommon = false;
- }
- else if (values.length >= LARGE_ARRAY_SIZE) {
- includes = cacheHas;
- isCommon = false;
- values = new SetCache(values);
+ while (othIndex--) {
+ var array = arrays[othIndex];
+ if (othIndex && iteratee) {
+ array = arrayMap(array, baseUnary(iteratee));
+ }
+ maxLength = nativeMin(array.length, maxLength);
+ caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
+ ? new SetCache(othIndex && array)
+ : undefined;
}
+ array = arrays[0];
+
+ var index = -1,
+ seen = caches[0];
+
outer:
- while (++index < length) {
+ while (++index < length && result.length < maxLength) {
var value = array[index],
- computed = iteratee == null ? value : iteratee(value);
+ computed = iteratee ? iteratee(value) : value;
value = (comparator || value !== 0) ? value : 0;
- if (isCommon && computed === computed) {
- var valuesIndex = valuesLength;
- while (valuesIndex--) {
- if (values[valuesIndex] === computed) {
+ if (!(seen
+ ? cacheHas(seen, computed)
+ : includes(result, computed, comparator)
+ )) {
+ othIndex = othLength;
+ while (--othIndex) {
+ var cache = caches[othIndex];
+ if (!(cache
+ ? cacheHas(cache, computed)
+ : includes(arrays[othIndex], computed, comparator))
+ ) {
continue outer;
}
}
+ if (seen) {
+ seen.push(computed);
+ }
result.push(value);
}
- else if (!includes(values, computed, comparator)) {
- result.push(value);
- }
}
return result;
}
/**
- * The base implementation of `_.forEach` without support for iteratee shorthands.
+ * The base implementation of `_.invert` and `_.invertBy` which inverts
+ * `object` with values transformed by `iteratee` and set by `setter`.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array|Object} Returns `collection`.
+ * @param {Object} object The object to iterate over.
+ * @param {Function} setter The function to set `accumulator` values.
+ * @param {Function} iteratee The iteratee to transform values.
+ * @param {Object} accumulator The initial inverted object.
+ * @returns {Function} Returns `accumulator`.
*/
- var baseEach = createBaseEach(baseForOwn);
+ function baseInverter(object, setter, iteratee, accumulator) {
+ baseForOwn(object, function(value, key, object) {
+ setter(accumulator, iteratee(value), key, object);
+ });
+ return accumulator;
+ }
/**
- * The base implementation of `_.forEachRight` without support for iteratee shorthands.
+ * The base implementation of `_.invoke` without support for individual
+ * method arguments.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array|Object} Returns `collection`.
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the method to invoke.
+ * @param {Array} args The arguments to invoke the method with.
+ * @returns {*} Returns the result of the invoked method.
*/
- var baseEachRight = createBaseEach(baseForOwnRight, true);
+ function baseInvoke(object, path, args) {
+ path = castPath(path, object);
+ object = parent(object, path);
+ var func = object == null ? object : object[toKey(last(path))];
+ return func == null ? undefined : apply(func, object, args);
+ }
/**
- * The base implementation of `_.every` without support for iteratee shorthands.
+ * The base implementation of `_.isArguments`.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} predicate The function invoked per iteration.
- * @returns {boolean} Returns `true` if all elements pass the predicate check,
- * else `false`
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object,
*/
- function baseEvery(collection, predicate) {
- var result = true;
- baseEach(collection, function(value, index, collection) {
- result = !!predicate(value, index, collection);
- return result;
- });
- return result;
+ function baseIsArguments(value) {
+ return isObjectLike(value) && baseGetTag(value) == argsTag;
}
/**
- * The base implementation of methods like `_.max` and `_.min` which accepts a
- * `comparator` to determine the extremum value.
+ * The base implementation of `_.isArrayBuffer` without Node.js optimizations.
*
* @private
- * @param {Array} array The array to iterate over.
- * @param {Function} iteratee The iteratee invoked per iteration.
- * @param {Function} comparator The comparator used to compare values.
- * @returns {*} Returns the extremum value.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
*/
- function baseExtremum(array, iteratee, comparator) {
- var index = -1,
- length = array.length;
+ function baseIsArrayBuffer(value) {
+ return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;
+ }
- while (++index < length) {
- var value = array[index],
- current = iteratee(value);
+ /**
+ * The base implementation of `_.isDate` without Node.js optimizations.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+ */
+ function baseIsDate(value) {
+ return isObjectLike(value) && baseGetTag(value) == dateTag;
+ }
- if (current != null && (computed === undefined
- ? (current === current && !isSymbol(current))
- : comparator(current, computed)
- )) {
- var computed = current,
- result = value;
- }
+ /**
+ * The base implementation of `_.isEqual` which supports partial comparisons
+ * and tracks traversed objects.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {boolean} bitmask The bitmask flags.
+ * 1 - Unordered comparison
+ * 2 - Partial comparison
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @param {Object} [stack] Tracks traversed `value` and `other` objects.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ */
+ function baseIsEqual(value, other, bitmask, customizer, stack) {
+ if (value === other) {
+ return true;
}
- return result;
+ if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
+ return value !== value && other !== other;
+ }
+ return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
}
/**
- * The base implementation of `_.fill` without an iteratee call guard.
+ * A specialized version of `baseIsEqual` for arrays and objects which performs
+ * deep comparisons and tracks traversed objects enabling objects with circular
+ * references to be compared.
*
* @private
- * @param {Array} array The array to fill.
- * @param {*} value The value to fill `array` with.
- * @param {number} [start=0] The start position.
- * @param {number} [end=array.length] The end position.
- * @returns {Array} Returns `array`.
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} [stack] Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
- function baseFill(array, value, start, end) {
- var length = array.length;
+ function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
+ var objIsArr = isArray(object),
+ othIsArr = isArray(other),
+ objTag = objIsArr ? arrayTag : getTag(object),
+ othTag = othIsArr ? arrayTag : getTag(other);
- start = toInteger(start);
- if (start < 0) {
- start = -start > length ? 0 : (length + start);
+ objTag = objTag == argsTag ? objectTag : objTag;
+ othTag = othTag == argsTag ? objectTag : othTag;
+
+ var objIsObj = objTag == objectTag,
+ othIsObj = othTag == objectTag,
+ isSameTag = objTag == othTag;
+
+ if (isSameTag && isBuffer(object)) {
+ if (!isBuffer(other)) {
+ return false;
+ }
+ objIsArr = true;
+ objIsObj = false;
}
- end = (end === undefined || end > length) ? length : toInteger(end);
- if (end < 0) {
- end += length;
+ if (isSameTag && !objIsObj) {
+ stack || (stack = new Stack);
+ return (objIsArr || isTypedArray(object))
+ ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
+ : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
}
- end = start > end ? 0 : toLength(end);
- while (start < end) {
- array[start++] = value;
+ if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
+ var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
+ othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+
+ if (objIsWrapped || othIsWrapped) {
+ var objUnwrapped = objIsWrapped ? object.value() : object,
+ othUnwrapped = othIsWrapped ? other.value() : other;
+
+ stack || (stack = new Stack);
+ return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
+ }
}
- return array;
+ if (!isSameTag) {
+ return false;
+ }
+ stack || (stack = new Stack);
+ return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
}
/**
- * The base implementation of `_.filter` without support for iteratee shorthands.
+ * The base implementation of `_.isMap` without Node.js optimizations.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} predicate The function invoked per iteration.
- * @returns {Array} Returns the new filtered array.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a map, else `false`.
*/
- function baseFilter(collection, predicate) {
- var result = [];
- baseEach(collection, function(value, index, collection) {
- if (predicate(value, index, collection)) {
- result.push(value);
- }
- });
- return result;
+ function baseIsMap(value) {
+ return isObjectLike(value) && getTag(value) == mapTag;
}
/**
- * The base implementation of `_.flatten` with support for restricting flattening.
+ * The base implementation of `_.isMatch` without support for iteratee shorthands.
*
* @private
- * @param {Array} array The array to flatten.
- * @param {number} depth The maximum recursion depth.
- * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
- * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
- * @param {Array} [result=[]] The initial result value.
- * @returns {Array} Returns the new flattened array.
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @param {Array} matchData The property names, values, and compare flags to match.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
*/
- function baseFlatten(array, depth, predicate, isStrict, result) {
- var index = -1,
- length = array.length;
-
- predicate || (predicate = isFlattenable);
- result || (result = []);
+ function baseIsMatch(object, source, matchData, customizer) {
+ var index = matchData.length,
+ length = index,
+ noCustomizer = !customizer;
+ if (object == null) {
+ return !length;
+ }
+ object = Object(object);
+ while (index--) {
+ var data = matchData[index];
+ if ((noCustomizer && data[2])
+ ? data[1] !== object[data[0]]
+ : !(data[0] in object)
+ ) {
+ return false;
+ }
+ }
while (++index < length) {
- var value = array[index];
- if (depth > 0 && predicate(value)) {
- if (depth > 1) {
- // Recursively flatten arrays (susceptible to call stack limits).
- baseFlatten(value, depth - 1, predicate, isStrict, result);
- } else {
- arrayPush(result, value);
+ data = matchData[index];
+ var key = data[0],
+ objValue = object[key],
+ srcValue = data[1];
+
+ if (noCustomizer && data[2]) {
+ if (objValue === undefined && !(key in object)) {
+ return false;
+ }
+ } else {
+ var stack = new Stack;
+ if (customizer) {
+ var result = customizer(objValue, srcValue, key, object, source, stack);
+ }
+ if (!(result === undefined
+ ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
+ : result
+ )) {
+ return false;
}
- } else if (!isStrict) {
- result[result.length] = value;
}
}
- return result;
+ return true;
}
/**
- * The base implementation of `baseForOwn` which iterates over `object`
- * properties returned by `keysFunc` and invokes `iteratee` for each property.
- * Iteratee functions may exit iteration early by explicitly returning `false`.
+ * The base implementation of `_.isNative` without bad shim checks.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @param {Function} keysFunc The function to get the keys of `object`.
- * @returns {Object} Returns `object`.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function,
+ * else `false`.
*/
- var baseFor = createBaseFor();
+ function baseIsNative(value) {
+ if (!isObject(value) || isMasked(value)) {
+ return false;
+ }
+ var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
+ return pattern.test(toSource(value));
+ }
/**
- * This function is like `baseFor` except that it iterates over properties
- * in the opposite order.
+ * The base implementation of `_.isRegExp` without Node.js optimizations.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @param {Function} keysFunc The function to get the keys of `object`.
- * @returns {Object} Returns `object`.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
*/
- var baseForRight = createBaseFor(true);
+ function baseIsRegExp(value) {
+ return isObjectLike(value) && baseGetTag(value) == regexpTag;
+ }
/**
- * The base implementation of `_.forOwn` without support for iteratee shorthands.
+ * The base implementation of `_.isSet` without Node.js optimizations.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Object} Returns `object`.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a set, else `false`.
*/
- function baseForOwn(object, iteratee) {
- return object && baseFor(object, iteratee, keys);
+ function baseIsSet(value) {
+ return isObjectLike(value) && getTag(value) == setTag;
}
/**
- * The base implementation of `_.forOwnRight` without support for iteratee shorthands.
+ * The base implementation of `_.isTypedArray` without Node.js optimizations.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Object} Returns `object`.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
*/
- function baseForOwnRight(object, iteratee) {
- return object && baseForRight(object, iteratee, keys);
+ function baseIsTypedArray(value) {
+ return isObjectLike(value) &&
+ isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
}
/**
- * The base implementation of `_.functions` which creates an array of
- * `object` function property names filtered from `props`.
+ * The base implementation of `_.iteratee`.
*
* @private
- * @param {Object} object The object to inspect.
- * @param {Array} props The property names to filter.
- * @returns {Array} Returns the function names.
+ * @param {*} [value=_.identity] The value to convert to an iteratee.
+ * @returns {Function} Returns the iteratee.
*/
- function baseFunctions(object, props) {
- return arrayFilter(props, function(key) {
- return isFunction(object[key]);
- });
+ function baseIteratee(value) {
+ // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
+ // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
+ if (typeof value == 'function') {
+ return value;
+ }
+ if (value == null) {
+ return identity;
+ }
+ if (typeof value == 'object') {
+ return isArray(value)
+ ? baseMatchesProperty(value[0], value[1])
+ : baseMatches(value);
+ }
+ return property(value);
}
/**
- * The base implementation of `_.get` without support for default values.
+ * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
*
* @private
* @param {Object} object The object to query.
- * @param {Array|string} path The path of the property to get.
- * @returns {*} Returns the resolved value.
+ * @returns {Array} Returns the array of property names.
*/
- function baseGet(object, path) {
- path = castPath(path, object);
-
- var index = 0,
- length = path.length;
-
- while (object != null && index < length) {
- object = object[toKey(path[index++])];
+ function baseKeys(object) {
+ if (!isPrototype(object)) {
+ return nativeKeys(object);
}
- return (index && index == length) ? object : undefined;
+ var result = [];
+ for (var key in Object(object)) {
+ if (hasOwnProperty.call(object, key) && key != 'constructor') {
+ result.push(key);
+ }
+ }
+ return result;
}
/**
- * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
- * `keysFunc` and `symbolsFunc` to get the enumerable property names and
- * symbols of `object`.
+ * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
*
* @private
* @param {Object} object The object to query.
- * @param {Function} keysFunc The function to get the keys of `object`.
- * @param {Function} symbolsFunc The function to get the symbols of `object`.
- * @returns {Array} Returns the array of property names and symbols.
+ * @returns {Array} Returns the array of property names.
*/
- function baseGetAllKeys(object, keysFunc, symbolsFunc) {
- var result = keysFunc(object);
- return isArray(object) ? result : arrayPush(result, symbolsFunc(object));
- }
+ function baseKeysIn(object) {
+ if (!isObject(object)) {
+ return nativeKeysIn(object);
+ }
+ var isProto = isPrototype(object),
+ result = [];
- /**
- * The base implementation of `getTag` without fallbacks for buggy environments.
- *
- * @private
- * @param {*} value The value to query.
- * @returns {string} Returns the `toStringTag`.
- */
- function baseGetTag(value) {
- if (value == null) {
- return value === undefined ? undefinedTag : nullTag;
+ for (var key in object) {
+ if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
+ result.push(key);
+ }
}
- return (symToStringTag && symToStringTag in Object(value))
- ? getRawTag(value)
- : objectToString(value);
+ return result;
}
/**
- * The base implementation of `_.gt` which doesn't coerce arguments.
+ * The base implementation of `_.lt` which doesn't coerce arguments.
*
* @private
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if `value` is greater than `other`,
+ * @returns {boolean} Returns `true` if `value` is less than `other`,
* else `false`.
*/
- function baseGt(value, other) {
- return value > other;
- }
-
- /**
- * The base implementation of `_.has` without support for deep paths.
- *
- * @private
- * @param {Object} [object] The object to query.
- * @param {Array|string} key The key to check.
- * @returns {boolean} Returns `true` if `key` exists, else `false`.
- */
- function baseHas(object, key) {
- return object != null && hasOwnProperty.call(object, key);
+ function baseLt(value, other) {
+ return value < other;
}
/**
- * The base implementation of `_.hasIn` without support for deep paths.
+ * The base implementation of `_.map` without support for iteratee shorthands.
*
* @private
- * @param {Object} [object] The object to query.
- * @param {Array|string} key The key to check.
- * @returns {boolean} Returns `true` if `key` exists, else `false`.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} iteratee The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
*/
- function baseHasIn(object, key) {
- return object != null && key in Object(object);
- }
+ function baseMap(collection, iteratee) {
+ var index = -1,
+ result = isArrayLike(collection) ? Array(collection.length) : [];
- /**
- * The base implementation of `_.inRange` which doesn't coerce arguments.
- *
- * @private
- * @param {number} number The number to check.
- * @param {number} start The start of the range.
- * @param {number} end The end of the range.
- * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
- */
- function baseInRange(number, start, end) {
- return number >= nativeMin(start, end) && number < nativeMax(start, end);
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = iteratee(value, key, collection);
+ });
+ return result;
}
/**
- * The base implementation of methods like `_.intersection`, without support
- * for iteratee shorthands, that accepts an array of arrays to inspect.
+ * The base implementation of `_.matches` which doesn't clone `source`.
*
* @private
- * @param {Array} arrays The arrays to inspect.
- * @param {Function} [iteratee] The iteratee invoked per element.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of shared values.
+ * @param {Object} source The object of property values to match.
+ * @returns {Function} Returns the new spec function.
*/
- function baseIntersection(arrays, iteratee, comparator) {
- var includes = comparator ? arrayIncludesWith : arrayIncludes,
- length = arrays[0].length,
- othLength = arrays.length,
- othIndex = othLength,
- caches = Array(othLength),
- maxLength = Infinity,
- result = [];
-
- while (othIndex--) {
- var array = arrays[othIndex];
- if (othIndex && iteratee) {
- array = arrayMap(array, baseUnary(iteratee));
- }
- maxLength = nativeMin(array.length, maxLength);
- caches[othIndex] = !comparator && (iteratee || (length >= 120 && array.length >= 120))
- ? new SetCache(othIndex && array)
- : undefined;
- }
- array = arrays[0];
-
- var index = -1,
- seen = caches[0];
-
- outer:
- while (++index < length && result.length < maxLength) {
- var value = array[index],
- computed = iteratee ? iteratee(value) : value;
-
- value = (comparator || value !== 0) ? value : 0;
- if (!(seen
- ? cacheHas(seen, computed)
- : includes(result, computed, comparator)
- )) {
- othIndex = othLength;
- while (--othIndex) {
- var cache = caches[othIndex];
- if (!(cache
- ? cacheHas(cache, computed)
- : includes(arrays[othIndex], computed, comparator))
- ) {
- continue outer;
- }
- }
- if (seen) {
- seen.push(computed);
- }
- result.push(value);
- }
+ function baseMatches(source) {
+ var matchData = getMatchData(source);
+ if (matchData.length == 1 && matchData[0][2]) {
+ return matchesStrictComparable(matchData[0][0], matchData[0][1]);
}
- return result;
+ return function(object) {
+ return object === source || baseIsMatch(object, source, matchData);
+ };
}
/**
- * The base implementation of `_.invert` and `_.invertBy` which inverts
- * `object` with values transformed by `iteratee` and set by `setter`.
+ * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {Function} setter The function to set `accumulator` values.
- * @param {Function} iteratee The iteratee to transform values.
- * @param {Object} accumulator The initial inverted object.
- * @returns {Function} Returns `accumulator`.
+ * @param {string} path The path of the property to get.
+ * @param {*} srcValue The value to match.
+ * @returns {Function} Returns the new spec function.
*/
- function baseInverter(object, setter, iteratee, accumulator) {
- baseForOwn(object, function(value, key, object) {
- setter(accumulator, iteratee(value), key, object);
- });
- return accumulator;
+ function baseMatchesProperty(path, srcValue) {
+ if (isKey(path) && isStrictComparable(srcValue)) {
+ return matchesStrictComparable(toKey(path), srcValue);
+ }
+ return function(object) {
+ var objValue = get(object, path);
+ return (objValue === undefined && objValue === srcValue)
+ ? hasIn(object, path)
+ : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
+ };
}
/**
- * The base implementation of `_.invoke` without support for individual
- * method arguments.
+ * The base implementation of `_.merge` without support for multiple sources.
*
* @private
- * @param {Object} object The object to query.
- * @param {Array|string} path The path of the method to invoke.
- * @param {Array} args The arguments to invoke the method with.
- * @returns {*} Returns the result of the invoked method.
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {number} srcIndex The index of `source`.
+ * @param {Function} [customizer] The function to customize merged values.
+ * @param {Object} [stack] Tracks traversed source values and their merged
+ * counterparts.
*/
- function baseInvoke(object, path, args) {
- path = castPath(path, object);
- object = parent(object, path);
- var func = object == null ? object : object[toKey(last(path))];
- return func == null ? undefined : apply(func, object, args);
+ function baseMerge(object, source, srcIndex, customizer, stack) {
+ if (object === source) {
+ return;
+ }
+ baseFor(source, function(srcValue, key) {
+ if (isObject(srcValue)) {
+ stack || (stack = new Stack);
+ baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
+ }
+ else {
+ var newValue = customizer
+ ? customizer(object[key], srcValue, (key + ''), object, source, stack)
+ : undefined;
+
+ if (newValue === undefined) {
+ newValue = srcValue;
+ }
+ assignMergeValue(object, key, newValue);
+ }
+ }, keysIn);
}
/**
- * The base implementation of `_.isArguments`.
+ * A specialized version of `baseMerge` for arrays and objects which performs
+ * deep merges and tracks traversed objects enabling objects with circular
+ * references to be merged.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+ * @param {Object} object The destination object.
+ * @param {Object} source The source object.
+ * @param {string} key The key of the value to merge.
+ * @param {number} srcIndex The index of `source`.
+ * @param {Function} mergeFunc The function to merge values.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @param {Object} [stack] Tracks traversed source values and their merged
+ * counterparts.
*/
- function baseIsArguments(value) {
- return isObjectLike(value) && baseGetTag(value) == argsTag;
+ function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
+ var objValue = object[key],
+ srcValue = source[key],
+ stacked = stack.get(srcValue);
+
+ if (stacked) {
+ assignMergeValue(object, key, stacked);
+ return;
+ }
+ var newValue = customizer
+ ? customizer(objValue, srcValue, (key + ''), object, source, stack)
+ : undefined;
+
+ var isCommon = newValue === undefined;
+
+ if (isCommon) {
+ var isArr = isArray(srcValue),
+ isBuff = !isArr && isBuffer(srcValue),
+ isTyped = !isArr && !isBuff && isTypedArray(srcValue);
+
+ newValue = srcValue;
+ if (isArr || isBuff || isTyped) {
+ if (isArray(objValue)) {
+ newValue = objValue;
+ }
+ else if (isArrayLikeObject(objValue)) {
+ newValue = copyArray(objValue);
+ }
+ else if (isBuff) {
+ isCommon = false;
+ newValue = cloneBuffer(srcValue, true);
+ }
+ else if (isTyped) {
+ isCommon = false;
+ newValue = cloneTypedArray(srcValue, true);
+ }
+ else {
+ newValue = [];
+ }
+ }
+ else if (isPlainObject(srcValue) || isArguments(srcValue)) {
+ newValue = objValue;
+ if (isArguments(objValue)) {
+ newValue = toPlainObject(objValue);
+ }
+ else if (!isObject(objValue) || (srcIndex && isFunction(objValue))) {
+ newValue = initCloneObject(srcValue);
+ }
+ }
+ else {
+ isCommon = false;
+ }
+ }
+ if (isCommon) {
+ // Recursively merge objects and arrays (susceptible to call stack limits).
+ stack.set(srcValue, newValue);
+ mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
+ stack['delete'](srcValue);
+ }
+ assignMergeValue(object, key, newValue);
}
/**
- * The base implementation of `_.isArrayBuffer` without Node.js optimizations.
+ * The base implementation of `_.nth` which doesn't coerce arguments.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
+ * @param {Array} array The array to query.
+ * @param {number} n The index of the element to return.
+ * @returns {*} Returns the nth element of `array`.
*/
- function baseIsArrayBuffer(value) {
- return isObjectLike(value) && baseGetTag(value) == arrayBufferTag;
+ function baseNth(array, n) {
+ var length = array.length;
+ if (!length) {
+ return;
+ }
+ n += n < 0 ? length : 0;
+ return isIndex(n, length) ? array[n] : undefined;
}
/**
- * The base implementation of `_.isDate` without Node.js optimizations.
+ * The base implementation of `_.orderBy` without param guards.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
+ * @param {string[]} orders The sort orders of `iteratees`.
+ * @returns {Array} Returns the new sorted array.
*/
- function baseIsDate(value) {
- return isObjectLike(value) && baseGetTag(value) == dateTag;
+ function baseOrderBy(collection, iteratees, orders) {
+ var index = -1;
+ iteratees = arrayMap(iteratees.length ? iteratees : [identity], baseUnary(getIteratee()));
+
+ var result = baseMap(collection, function(value, key, collection) {
+ var criteria = arrayMap(iteratees, function(iteratee) {
+ return iteratee(value);
+ });
+ return { 'criteria': criteria, 'index': ++index, 'value': value };
+ });
+
+ return baseSortBy(result, function(object, other) {
+ return compareMultiple(object, other, orders);
+ });
}
/**
- * The base implementation of `_.isEqual` which supports partial comparisons
- * and tracks traversed objects.
+ * The base implementation of `_.pick` without support for individual
+ * property identifiers.
*
* @private
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @param {boolean} bitmask The bitmask flags.
- * 1 - Unordered comparison
- * 2 - Partial comparison
- * @param {Function} [customizer] The function to customize comparisons.
- * @param {Object} [stack] Tracks traversed `value` and `other` objects.
- * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @param {Object} object The source object.
+ * @param {string[]} paths The property paths to pick.
+ * @returns {Object} Returns the new object.
*/
- function baseIsEqual(value, other, bitmask, customizer, stack) {
- if (value === other) {
- return true;
- }
- if (value == null || other == null || (!isObjectLike(value) && !isObjectLike(other))) {
- return value !== value && other !== other;
- }
- return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
+ function basePick(object, paths) {
+ return basePickBy(object, paths, function(value, path) {
+ return hasIn(object, path);
+ });
}
/**
- * A specialized version of `baseIsEqual` for arrays and objects which performs
- * deep comparisons and tracks traversed objects enabling objects with circular
- * references to be compared.
+ * The base implementation of `_.pickBy` without support for iteratee shorthands.
*
* @private
- * @param {Object} object The object to compare.
- * @param {Object} other The other object to compare.
- * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
- * @param {Function} customizer The function to customize comparisons.
- * @param {Function} equalFunc The function to determine equivalents of values.
- * @param {Object} [stack] Tracks traversed `object` and `other` objects.
- * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ * @param {Object} object The source object.
+ * @param {string[]} paths The property paths to pick.
+ * @param {Function} predicate The function invoked per property.
+ * @returns {Object} Returns the new object.
*/
- function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
- var objIsArr = isArray(object),
- othIsArr = isArray(other),
- objTag = objIsArr ? arrayTag : getTag(object),
- othTag = othIsArr ? arrayTag : getTag(other);
-
- objTag = objTag == argsTag ? objectTag : objTag;
- othTag = othTag == argsTag ? objectTag : othTag;
-
- var objIsObj = objTag == objectTag,
- othIsObj = othTag == objectTag,
- isSameTag = objTag == othTag;
-
- if (isSameTag && isBuffer(object)) {
- if (!isBuffer(other)) {
- return false;
- }
- objIsArr = true;
- objIsObj = false;
- }
- if (isSameTag && !objIsObj) {
- stack || (stack = new Stack);
- return (objIsArr || isTypedArray(object))
- ? equalArrays(object, other, bitmask, customizer, equalFunc, stack)
- : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
- }
- if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
- var objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
- othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
+ function basePickBy(object, paths, predicate) {
+ var index = -1,
+ length = paths.length,
+ result = {};
- if (objIsWrapped || othIsWrapped) {
- var objUnwrapped = objIsWrapped ? object.value() : object,
- othUnwrapped = othIsWrapped ? other.value() : other;
+ while (++index < length) {
+ var path = paths[index],
+ value = baseGet(object, path);
- stack || (stack = new Stack);
- return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
+ if (predicate(value, path)) {
+ baseSet(result, castPath(path, object), value);
}
}
- if (!isSameTag) {
- return false;
- }
- stack || (stack = new Stack);
- return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
+ return result;
}
/**
- * The base implementation of `_.isMap` without Node.js optimizations.
+ * A specialized version of `baseProperty` which supports deep paths.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+ * @param {Array|string} path The path of the property to get.
+ * @returns {Function} Returns the new accessor function.
*/
- function baseIsMap(value) {
- return isObjectLike(value) && getTag(value) == mapTag;
+ function basePropertyDeep(path) {
+ return function(object) {
+ return baseGet(object, path);
+ };
}
/**
- * The base implementation of `_.isMatch` without support for iteratee shorthands.
+ * The base implementation of `_.pullAllBy` without support for iteratee
+ * shorthands.
*
* @private
- * @param {Object} object The object to inspect.
- * @param {Object} source The object of property values to match.
- * @param {Array} matchData The property names, values, and compare flags to match.
- * @param {Function} [customizer] The function to customize comparisons.
- * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns `array`.
*/
- function baseIsMatch(object, source, matchData, customizer) {
- var index = matchData.length,
- length = index,
- noCustomizer = !customizer;
+ function basePullAll(array, values, iteratee, comparator) {
+ var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
+ index = -1,
+ length = values.length,
+ seen = array;
- if (object == null) {
- return !length;
+ if (array === values) {
+ values = copyArray(values);
}
- object = Object(object);
- while (index--) {
- var data = matchData[index];
- if ((noCustomizer && data[2])
- ? data[1] !== object[data[0]]
- : !(data[0] in object)
- ) {
- return false;
- }
+ if (iteratee) {
+ seen = arrayMap(array, baseUnary(iteratee));
}
while (++index < length) {
- data = matchData[index];
- var key = data[0],
- objValue = object[key],
- srcValue = data[1];
+ var fromIndex = 0,
+ value = values[index],
+ computed = iteratee ? iteratee(value) : value;
- if (noCustomizer && data[2]) {
- if (objValue === undefined && !(key in object)) {
- return false;
- }
- } else {
- var stack = new Stack;
- if (customizer) {
- var result = customizer(objValue, srcValue, key, object, source, stack);
- }
- if (!(result === undefined
- ? baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG, customizer, stack)
- : result
- )) {
- return false;
+ while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
+ if (seen !== array) {
+ splice.call(seen, fromIndex, 1);
}
+ splice.call(array, fromIndex, 1);
}
}
- return true;
+ return array;
}
/**
- * The base implementation of `_.isNative` without bad shim checks.
+ * The base implementation of `_.pullAt` without support for individual
+ * indexes or capturing the removed elements.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a native function,
- * else `false`.
+ * @param {Array} array The array to modify.
+ * @param {number[]} indexes The indexes of elements to remove.
+ * @returns {Array} Returns `array`.
*/
- function baseIsNative(value) {
- if (!isObject(value) || isMasked(value)) {
- return false;
+ function basePullAt(array, indexes) {
+ var length = array ? indexes.length : 0,
+ lastIndex = length - 1;
+
+ while (length--) {
+ var index = indexes[length];
+ if (length == lastIndex || index !== previous) {
+ var previous = index;
+ if (isIndex(index)) {
+ splice.call(array, index, 1);
+ } else {
+ baseUnset(array, index);
+ }
+ }
}
- var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
- return pattern.test(toSource(value));
+ return array;
}
/**
- * The base implementation of `_.isRegExp` without Node.js optimizations.
+ * The base implementation of `_.random` without support for returning
+ * floating-point numbers.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+ * @param {number} lower The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the random number.
*/
- function baseIsRegExp(value) {
- return isObjectLike(value) && baseGetTag(value) == regexpTag;
+ function baseRandom(lower, upper) {
+ return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
}
/**
- * The base implementation of `_.isSet` without Node.js optimizations.
+ * The base implementation of `_.range` and `_.rangeRight` which doesn't
+ * coerce arguments.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a set, else `false`.
+ * @param {number} start The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} step The value to increment or decrement by.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Array} Returns the range of numbers.
*/
- function baseIsSet(value) {
- return isObjectLike(value) && getTag(value) == setTag;
+ function baseRange(start, end, step, fromRight) {
+ var index = -1,
+ length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
+ result = Array(length);
+
+ while (length--) {
+ result[fromRight ? length : ++index] = start;
+ start += step;
+ }
+ return result;
}
/**
- * The base implementation of `_.isTypedArray` without Node.js optimizations.
+ * The base implementation of `_.repeat` which doesn't coerce arguments.
*
* @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ * @param {string} string The string to repeat.
+ * @param {number} n The number of times to repeat the string.
+ * @returns {string} Returns the repeated string.
*/
- function baseIsTypedArray(value) {
- return isObjectLike(value) &&
- isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
+ function baseRepeat(string, n) {
+ var result = '';
+ if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
+ return result;
+ }
+ // Leverage the exponentiation by squaring algorithm for a faster repeat.
+ // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+ do {
+ if (n % 2) {
+ result += string;
+ }
+ n = nativeFloor(n / 2);
+ if (n) {
+ string += string;
+ }
+ } while (n);
+
+ return result;
}
/**
- * The base implementation of `_.iteratee`.
+ * The base implementation of `_.rest` which doesn't validate or coerce arguments.
*
* @private
- * @param {*} [value=_.identity] The value to convert to an iteratee.
- * @returns {Function} Returns the iteratee.
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
*/
- function baseIteratee(value) {
- // Don't store the `typeof` result in a variable to avoid a JIT bug in Safari 9.
- // See https://bugs.webkit.org/show_bug.cgi?id=156034 for more details.
- if (typeof value == 'function') {
- return value;
- }
- if (value == null) {
- return identity;
- }
- if (typeof value == 'object') {
- return isArray(value)
- ? baseMatchesProperty(value[0], value[1])
- : baseMatches(value);
- }
- return property(value);
+ function baseRest(func, start) {
+ return setToString(overRest(func, start, identity), func + '');
}
/**
- * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
+ * The base implementation of `_.sample`.
*
* @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names.
+ * @param {Array|Object} collection The collection to sample.
+ * @returns {*} Returns the random element.
*/
- function baseKeys(object) {
- if (!isPrototype(object)) {
- return nativeKeys(object);
- }
- var result = [];
- for (var key in Object(object)) {
- if (hasOwnProperty.call(object, key) && key != 'constructor') {
- result.push(key);
- }
- }
- return result;
+ function baseSample(collection) {
+ return arraySample(values(collection));
}
/**
- * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
+ * The base implementation of `_.sampleSize` without param guards.
*
* @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names.
+ * @param {Array|Object} collection The collection to sample.
+ * @param {number} n The number of elements to sample.
+ * @returns {Array} Returns the random elements.
*/
- function baseKeysIn(object) {
+ function baseSampleSize(collection, n) {
+ var array = values(collection);
+ return shuffleSelf(array, baseClamp(n, 0, array.length));
+ }
+
+ /**
+ * The base implementation of `_.set`.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {*} value The value to set.
+ * @param {Function} [customizer] The function to customize path creation.
+ * @returns {Object} Returns `object`.
+ */
+ function baseSet(object, path, value, customizer) {
if (!isObject(object)) {
- return nativeKeysIn(object);
- }
- var isProto = isPrototype(object),
- result = [];
+ return object;
+ }
+ path = castPath(path, object);
- for (var key in object) {
- if (!(key == 'constructor' && (isProto || !hasOwnProperty.call(object, key)))) {
- result.push(key);
+ var index = -1,
+ length = path.length,
+ lastIndex = length - 1,
+ nested = object;
+
+ while (nested != null && ++index < length) {
+ var key = toKey(path[index]),
+ newValue = value;
+
+ if (index != lastIndex) {
+ var objValue = nested[key];
+ newValue = customizer ? customizer(objValue, key, nested) : undefined;
+ if (newValue === undefined) {
+ newValue = isObject(objValue)
+ ? objValue
+ : (isIndex(path[index + 1]) ? [] : {});
+ }
}
+ assignValue(nested, key, newValue);
+ nested = nested[key];
}
- return result;
+ return object;
}
/**
- * The base implementation of `_.lt` which doesn't coerce arguments.
+ * The base implementation of `setData` without support for hot loop shorting.
*
* @private
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if `value` is less than `other`,
- * else `false`.
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
*/
- function baseLt(value, other) {
- return value < other;
- }
+ var baseSetData = !metaMap ? identity : function(func, data) {
+ metaMap.set(func, data);
+ return func;
+ };
/**
- * The base implementation of `_.map` without support for iteratee shorthands.
+ * The base implementation of `setToString` without support for hot loop shorting.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} iteratee The function invoked per iteration.
- * @returns {Array} Returns the new mapped array.
+ * @param {Function} func The function to modify.
+ * @param {Function} string The `toString` result.
+ * @returns {Function} Returns `func`.
*/
- function baseMap(collection, iteratee) {
- var index = -1,
- result = isArrayLike(collection) ? Array(collection.length) : [];
-
- baseEach(collection, function(value, key, collection) {
- result[++index] = iteratee(value, key, collection);
+ var baseSetToString = !defineProperty ? identity : function(func, string) {
+ return defineProperty(func, 'toString', {
+ 'configurable': true,
+ 'enumerable': false,
+ 'value': constant(string),
+ 'writable': true
});
- return result;
- }
+ };
/**
- * The base implementation of `_.matches` which doesn't clone `source`.
+ * The base implementation of `_.shuffle`.
*
* @private
- * @param {Object} source The object of property values to match.
- * @returns {Function} Returns the new spec function.
+ * @param {Array|Object} collection The collection to shuffle.
+ * @returns {Array} Returns the new shuffled array.
*/
- function baseMatches(source) {
- var matchData = getMatchData(source);
- if (matchData.length == 1 && matchData[0][2]) {
- return matchesStrictComparable(matchData[0][0], matchData[0][1]);
- }
- return function(object) {
- return object === source || baseIsMatch(object, source, matchData);
- };
+ function baseShuffle(collection) {
+ return shuffleSelf(values(collection));
}
/**
- * The base implementation of `_.matchesProperty` which doesn't clone `srcValue`.
+ * The base implementation of `_.slice` without an iteratee call guard.
*
* @private
- * @param {string} path The path of the property to get.
- * @param {*} srcValue The value to match.
- * @returns {Function} Returns the new spec function.
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
*/
- function baseMatchesProperty(path, srcValue) {
- if (isKey(path) && isStrictComparable(srcValue)) {
- return matchesStrictComparable(toKey(path), srcValue);
+ function baseSlice(array, start, end) {
+ var index = -1,
+ length = array.length;
+
+ if (start < 0) {
+ start = -start > length ? 0 : (length + start);
}
- return function(object) {
- var objValue = get(object, path);
- return (objValue === undefined && objValue === srcValue)
- ? hasIn(object, path)
- : baseIsEqual(srcValue, objValue, COMPARE_PARTIAL_FLAG | COMPARE_UNORDERED_FLAG);
- };
+ end = end > length ? length : end;
+ if (end < 0) {
+ end += length;
+ }
+ length = start > end ? 0 : ((end - start) >>> 0);
+ start >>>= 0;
+
+ var result = Array(length);
+ while (++index < length) {
+ result[index] = array[index + start];
+ }
+ return result;
}
/**
- * The base implementation of `_.merge` without support for multiple sources.
+ * The base implementation of `_.some` without support for iteratee shorthands.
*
* @private
- * @param {Object} object The destination object.
- * @param {Object} source The source object.
- * @param {number} srcIndex The index of `source`.
- * @param {Function} [customizer] The function to customize merged values.
- * @param {Object} [stack] Tracks traversed source values and their merged
- * counterparts.
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} predicate The function invoked per iteration.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
*/
- function baseMerge(object, source, srcIndex, customizer, stack) {
- if (object === source) {
- return;
- }
- baseFor(source, function(srcValue, key) {
- stack || (stack = new Stack);
- if (isObject(srcValue)) {
- baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
- }
- else {
- var newValue = customizer
- ? customizer(safeGet(object, key), srcValue, (key + ''), object, source, stack)
- : undefined;
+ function baseSome(collection, predicate) {
+ var result;
- if (newValue === undefined) {
- newValue = srcValue;
- }
- assignMergeValue(object, key, newValue);
- }
- }, keysIn);
+ baseEach(collection, function(value, index, collection) {
+ result = predicate(value, index, collection);
+ return !result;
+ });
+ return !!result;
}
/**
- * A specialized version of `baseMerge` for arrays and objects which performs
- * deep merges and tracks traversed objects enabling objects with circular
- * references to be merged.
+ * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which
+ * performs a binary search of `array` to determine the index at which `value`
+ * should be inserted into `array` in order to maintain its sort order.
*
* @private
- * @param {Object} object The destination object.
- * @param {Object} source The source object.
- * @param {string} key The key of the value to merge.
- * @param {number} srcIndex The index of `source`.
- * @param {Function} mergeFunc The function to merge values.
- * @param {Function} [customizer] The function to customize assigned values.
- * @param {Object} [stack] Tracks traversed source values and their merged
- * counterparts.
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {boolean} [retHighest] Specify returning the highest qualified index.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
*/
- function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
- var objValue = safeGet(object, key),
- srcValue = safeGet(source, key),
- stacked = stack.get(srcValue);
-
- if (stacked) {
- assignMergeValue(object, key, stacked);
- return;
- }
- var newValue = customizer
- ? customizer(objValue, srcValue, (key + ''), object, source, stack)
- : undefined;
-
- var isCommon = newValue === undefined;
+ function baseSortedIndex(array, value, retHighest) {
+ var low = 0,
+ high = array == null ? low : array.length;
- if (isCommon) {
- var isArr = isArray(srcValue),
- isBuff = !isArr && isBuffer(srcValue),
- isTyped = !isArr && !isBuff && isTypedArray(srcValue);
+ if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
+ while (low < high) {
+ var mid = (low + high) >>> 1,
+ computed = array[mid];
- newValue = srcValue;
- if (isArr || isBuff || isTyped) {
- if (isArray(objValue)) {
- newValue = objValue;
- }
- else if (isArrayLikeObject(objValue)) {
- newValue = copyArray(objValue);
- }
- else if (isBuff) {
- isCommon = false;
- newValue = cloneBuffer(srcValue, true);
- }
- else if (isTyped) {
- isCommon = false;
- newValue = cloneTypedArray(srcValue, true);
- }
- else {
- newValue = [];
- }
- }
- else if (isPlainObject(srcValue) || isArguments(srcValue)) {
- newValue = objValue;
- if (isArguments(objValue)) {
- newValue = toPlainObject(objValue);
- }
- else if (!isObject(objValue) || isFunction(objValue)) {
- newValue = initCloneObject(srcValue);
+ if (computed !== null && !isSymbol(computed) &&
+ (retHighest ? (computed <= value) : (computed < value))) {
+ low = mid + 1;
+ } else {
+ high = mid;
}
}
- else {
- isCommon = false;
- }
- }
- if (isCommon) {
- // Recursively merge objects and arrays (susceptible to call stack limits).
- stack.set(srcValue, newValue);
- mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
- stack['delete'](srcValue);
- }
- assignMergeValue(object, key, newValue);
- }
-
- /**
- * The base implementation of `_.nth` which doesn't coerce arguments.
- *
- * @private
- * @param {Array} array The array to query.
- * @param {number} n The index of the element to return.
- * @returns {*} Returns the nth element of `array`.
- */
- function baseNth(array, n) {
- var length = array.length;
- if (!length) {
- return;
+ return high;
}
- n += n < 0 ? length : 0;
- return isIndex(n, length) ? array[n] : undefined;
+ return baseSortedIndexBy(array, value, identity, retHighest);
}
/**
- * The base implementation of `_.orderBy` without param guards.
+ * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`
+ * which invokes `iteratee` for `value` and each element of `array` to compute
+ * their sort ranking. The iteratee is invoked with one argument; (value).
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function[]|Object[]|string[]} iteratees The iteratees to sort by.
- * @param {string[]} orders The sort orders of `iteratees`.
- * @returns {Array} Returns the new sorted array.
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} iteratee The iteratee invoked per element.
+ * @param {boolean} [retHighest] Specify returning the highest qualified index.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
*/
- function baseOrderBy(collection, iteratees, orders) {
- if (iteratees.length) {
- iteratees = arrayMap(iteratees, function(iteratee) {
- if (isArray(iteratee)) {
- return function(value) {
- return baseGet(value, iteratee.length === 1 ? iteratee[0] : iteratee);
- }
- }
- return iteratee;
- });
- } else {
- iteratees = [identity];
- }
-
- var index = -1;
- iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
+ function baseSortedIndexBy(array, value, iteratee, retHighest) {
+ value = iteratee(value);
- var result = baseMap(collection, function(value, key, collection) {
- var criteria = arrayMap(iteratees, function(iteratee) {
- return iteratee(value);
- });
- return { 'criteria': criteria, 'index': ++index, 'value': value };
- });
+ var low = 0,
+ high = array == null ? 0 : array.length,
+ valIsNaN = value !== value,
+ valIsNull = value === null,
+ valIsSymbol = isSymbol(value),
+ valIsUndefined = value === undefined;
- return baseSortBy(result, function(object, other) {
- return compareMultiple(object, other, orders);
- });
- }
+ while (low < high) {
+ var mid = nativeFloor((low + high) / 2),
+ computed = iteratee(array[mid]),
+ othIsDefined = computed !== undefined,
+ othIsNull = computed === null,
+ othIsReflexive = computed === computed,
+ othIsSymbol = isSymbol(computed);
- /**
- * The base implementation of `_.pick` without support for individual
- * property identifiers.
- *
- * @private
- * @param {Object} object The source object.
- * @param {string[]} paths The property paths to pick.
- * @returns {Object} Returns the new object.
- */
- function basePick(object, paths) {
- return basePickBy(object, paths, function(value, path) {
- return hasIn(object, path);
- });
+ if (valIsNaN) {
+ var setLow = retHighest || othIsReflexive;
+ } else if (valIsUndefined) {
+ setLow = othIsReflexive && (retHighest || othIsDefined);
+ } else if (valIsNull) {
+ setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
+ } else if (valIsSymbol) {
+ setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
+ } else if (othIsNull || othIsSymbol) {
+ setLow = false;
+ } else {
+ setLow = retHighest ? (computed <= value) : (computed < value);
+ }
+ if (setLow) {
+ low = mid + 1;
+ } else {
+ high = mid;
+ }
+ }
+ return nativeMin(high, MAX_ARRAY_INDEX);
}
/**
- * The base implementation of `_.pickBy` without support for iteratee shorthands.
+ * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without
+ * support for iteratee shorthands.
*
* @private
- * @param {Object} object The source object.
- * @param {string[]} paths The property paths to pick.
- * @param {Function} predicate The function invoked per property.
- * @returns {Object} Returns the new object.
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
*/
- function basePickBy(object, paths, predicate) {
+ function baseSortedUniq(array, iteratee) {
var index = -1,
- length = paths.length,
- result = {};
+ length = array.length,
+ resIndex = 0,
+ result = [];
while (++index < length) {
- var path = paths[index],
- value = baseGet(object, path);
+ var value = array[index],
+ computed = iteratee ? iteratee(value) : value;
- if (predicate(value, path)) {
- baseSet(result, castPath(path, object), value);
+ if (!index || !eq(computed, seen)) {
+ var seen = computed;
+ result[resIndex++] = value === 0 ? 0 : value;
}
}
return result;
}
/**
- * A specialized version of `baseProperty` which supports deep paths.
+ * The base implementation of `_.toNumber` which doesn't ensure correct
+ * conversions of binary, hexadecimal, or octal string values.
*
* @private
- * @param {Array|string} path The path of the property to get.
- * @returns {Function} Returns the new accessor function.
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
*/
- function basePropertyDeep(path) {
- return function(object) {
- return baseGet(object, path);
- };
+ function baseToNumber(value) {
+ if (typeof value == 'number') {
+ return value;
+ }
+ if (isSymbol(value)) {
+ return NAN;
+ }
+ return +value;
}
/**
- * The base implementation of `_.pullAllBy` without support for iteratee
- * shorthands.
+ * The base implementation of `_.toString` which doesn't convert nullish
+ * values to empty strings.
*
* @private
- * @param {Array} array The array to modify.
- * @param {Array} values The values to remove.
+ * @param {*} value The value to process.
+ * @returns {string} Returns the string.
+ */
+ function baseToString(value) {
+ // Exit early for strings to avoid a performance hit in some environments.
+ if (typeof value == 'string') {
+ return value;
+ }
+ if (isArray(value)) {
+ // Recursively convert values (susceptible to call stack limits).
+ return arrayMap(value, baseToString) + '';
+ }
+ if (isSymbol(value)) {
+ return symbolToString ? symbolToString.call(value) : '';
+ }
+ var result = (value + '');
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+ }
+
+ /**
+ * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
* @param {Function} [iteratee] The iteratee invoked per element.
* @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns `array`.
+ * @returns {Array} Returns the new duplicate free array.
*/
- function basePullAll(array, values, iteratee, comparator) {
- var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
- index = -1,
- length = values.length,
- seen = array;
+ function baseUniq(array, iteratee, comparator) {
+ var index = -1,
+ includes = arrayIncludes,
+ length = array.length,
+ isCommon = true,
+ result = [],
+ seen = result;
- if (array === values) {
- values = copyArray(values);
+ if (comparator) {
+ isCommon = false;
+ includes = arrayIncludesWith;
}
- if (iteratee) {
- seen = arrayMap(array, baseUnary(iteratee));
+ else if (length >= LARGE_ARRAY_SIZE) {
+ var set = iteratee ? null : createSet(array);
+ if (set) {
+ return setToArray(set);
+ }
+ isCommon = false;
+ includes = cacheHas;
+ seen = new SetCache;
+ }
+ else {
+ seen = iteratee ? [] : result;
}
+ outer:
while (++index < length) {
- var fromIndex = 0,
- value = values[index],
+ var value = array[index],
computed = iteratee ? iteratee(value) : value;
- while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
- if (seen !== array) {
- splice.call(seen, fromIndex, 1);
+ value = (comparator || value !== 0) ? value : 0;
+ if (isCommon && computed === computed) {
+ var seenIndex = seen.length;
+ while (seenIndex--) {
+ if (seen[seenIndex] === computed) {
+ continue outer;
+ }
}
- splice.call(array, fromIndex, 1);
+ if (iteratee) {
+ seen.push(computed);
+ }
+ result.push(value);
+ }
+ else if (!includes(seen, computed, comparator)) {
+ if (seen !== result) {
+ seen.push(computed);
+ }
+ result.push(value);
}
}
- return array;
+ return result;
}
/**
- * The base implementation of `_.pullAt` without support for individual
- * indexes or capturing the removed elements.
+ * The base implementation of `_.unset`.
*
* @private
- * @param {Array} array The array to modify.
- * @param {number[]} indexes The indexes of elements to remove.
- * @returns {Array} Returns `array`.
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The property path to unset.
+ * @returns {boolean} Returns `true` if the property is deleted, else `false`.
*/
- function basePullAt(array, indexes) {
- var length = array ? indexes.length : 0,
- lastIndex = length - 1;
+ function baseUnset(object, path) {
+ path = castPath(path, object);
+ object = parent(object, path);
+ return object == null || delete object[toKey(last(path))];
+ }
- while (length--) {
- var index = indexes[length];
- if (length == lastIndex || index !== previous) {
- var previous = index;
- if (isIndex(index)) {
- splice.call(array, index, 1);
- } else {
- baseUnset(array, index);
- }
- }
- }
- return array;
+ /**
+ * The base implementation of `_.update`.
+ *
+ * @private
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to update.
+ * @param {Function} updater The function to produce the updated value.
+ * @param {Function} [customizer] The function to customize path creation.
+ * @returns {Object} Returns `object`.
+ */
+ function baseUpdate(object, path, updater, customizer) {
+ return baseSet(object, path, updater(baseGet(object, path)), customizer);
}
/**
- * The base implementation of `_.random` without support for returning
- * floating-point numbers.
+ * The base implementation of methods like `_.dropWhile` and `_.takeWhile`
+ * without support for iteratee shorthands.
*
* @private
- * @param {number} lower The lower bound.
- * @param {number} upper The upper bound.
- * @returns {number} Returns the random number.
+ * @param {Array} array The array to query.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Array} Returns the slice of `array`.
*/
- function baseRandom(lower, upper) {
- return lower + nativeFloor(nativeRandom() * (upper - lower + 1));
+ function baseWhile(array, predicate, isDrop, fromRight) {
+ var length = array.length,
+ index = fromRight ? length : -1;
+
+ while ((fromRight ? index-- : ++index < length) &&
+ predicate(array[index], index, array)) {}
+
+ return isDrop
+ ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
+ : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
}
/**
- * The base implementation of `_.range` and `_.rangeRight` which doesn't
- * coerce arguments.
+ * The base implementation of `wrapperValue` which returns the result of
+ * performing a sequence of actions on the unwrapped `value`, where each
+ * successive action is supplied the return value of the previous.
*
* @private
- * @param {number} start The start of the range.
- * @param {number} end The end of the range.
- * @param {number} step The value to increment or decrement by.
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Array} Returns the range of numbers.
+ * @param {*} value The unwrapped value.
+ * @param {Array} actions Actions to perform to resolve the unwrapped value.
+ * @returns {*} Returns the resolved value.
*/
- function baseRange(start, end, step, fromRight) {
- var index = -1,
- length = nativeMax(nativeCeil((end - start) / (step || 1)), 0),
- result = Array(length);
-
- while (length--) {
- result[fromRight ? length : ++index] = start;
- start += step;
+ function baseWrapperValue(value, actions) {
+ var result = value;
+ if (result instanceof LazyWrapper) {
+ result = result.value();
}
- return result;
+ return arrayReduce(actions, function(result, action) {
+ return action.func.apply(action.thisArg, arrayPush([result], action.args));
+ }, result);
}
/**
- * The base implementation of `_.repeat` which doesn't coerce arguments.
+ * The base implementation of methods like `_.xor`, without support for
+ * iteratee shorthands, that accepts an array of arrays to inspect.
*
* @private
- * @param {string} string The string to repeat.
- * @param {number} n The number of times to repeat the string.
- * @returns {string} Returns the repeated string.
+ * @param {Array} arrays The arrays to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of values.
*/
- function baseRepeat(string, n) {
- var result = '';
- if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
- return result;
+ function baseXor(arrays, iteratee, comparator) {
+ var length = arrays.length;
+ if (length < 2) {
+ return length ? baseUniq(arrays[0]) : [];
}
- // Leverage the exponentiation by squaring algorithm for a faster repeat.
- // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
- do {
- if (n % 2) {
- result += string;
- }
- n = nativeFloor(n / 2);
- if (n) {
- string += string;
- }
- } while (n);
+ var index = -1,
+ result = Array(length);
- return result;
+ while (++index < length) {
+ var array = arrays[index],
+ othIndex = -1;
+
+ while (++othIndex < length) {
+ if (othIndex != index) {
+ result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);
+ }
+ }
+ }
+ return baseUniq(baseFlatten(result, 1), iteratee, comparator);
}
/**
- * The base implementation of `_.rest` which doesn't validate or coerce arguments.
+ * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
*
* @private
- * @param {Function} func The function to apply a rest parameter to.
- * @param {number} [start=func.length-1] The start position of the rest parameter.
- * @returns {Function} Returns the new function.
+ * @param {Array} props The property identifiers.
+ * @param {Array} values The property values.
+ * @param {Function} assignFunc The function to assign values.
+ * @returns {Object} Returns the new object.
*/
- function baseRest(func, start) {
- return setToString(overRest(func, start, identity), func + '');
+ function baseZipObject(props, values, assignFunc) {
+ var index = -1,
+ length = props.length,
+ valsLength = values.length,
+ result = {};
+
+ while (++index < length) {
+ var value = index < valsLength ? values[index] : undefined;
+ assignFunc(result, props[index], value);
+ }
+ return result;
}
/**
- * The base implementation of `_.sample`.
+ * Casts `value` to an empty array if it's not an array like object.
*
* @private
- * @param {Array|Object} collection The collection to sample.
- * @returns {*} Returns the random element.
+ * @param {*} value The value to inspect.
+ * @returns {Array|Object} Returns the cast array-like object.
*/
- function baseSample(collection) {
- return arraySample(values(collection));
+ function castArrayLikeObject(value) {
+ return isArrayLikeObject(value) ? value : [];
}
/**
- * The base implementation of `_.sampleSize` without param guards.
+ * Casts `value` to `identity` if it's not a function.
*
* @private
- * @param {Array|Object} collection The collection to sample.
- * @param {number} n The number of elements to sample.
- * @returns {Array} Returns the random elements.
+ * @param {*} value The value to inspect.
+ * @returns {Function} Returns cast function.
*/
- function baseSampleSize(collection, n) {
- var array = values(collection);
- return shuffleSelf(array, baseClamp(n, 0, array.length));
+ function castFunction(value) {
+ return typeof value == 'function' ? value : identity;
}
/**
- * The base implementation of `_.set`.
+ * Casts `value` to a path array if it's not one.
*
* @private
- * @param {Object} object The object to modify.
- * @param {Array|string} path The path of the property to set.
- * @param {*} value The value to set.
- * @param {Function} [customizer] The function to customize path creation.
- * @returns {Object} Returns `object`.
+ * @param {*} value The value to inspect.
+ * @param {Object} [object] The object to query keys on.
+ * @returns {Array} Returns the cast property path array.
*/
- function baseSet(object, path, value, customizer) {
- if (!isObject(object)) {
- return object;
- }
- path = castPath(path, object);
-
- var index = -1,
- length = path.length,
- lastIndex = length - 1,
- nested = object;
-
- while (nested != null && ++index < length) {
- var key = toKey(path[index]),
- newValue = value;
-
- if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
- return object;
- }
-
- if (index != lastIndex) {
- var objValue = nested[key];
- newValue = customizer ? customizer(objValue, key, nested) : undefined;
- if (newValue === undefined) {
- newValue = isObject(objValue)
- ? objValue
- : (isIndex(path[index + 1]) ? [] : {});
- }
- }
- assignValue(nested, key, newValue);
- nested = nested[key];
+ function castPath(value, object) {
+ if (isArray(value)) {
+ return value;
}
- return object;
+ return isKey(value, object) ? [value] : stringToPath(toString(value));
}
/**
- * The base implementation of `setData` without support for hot loop shorting.
+ * A `baseRest` alias which can be replaced with `identity` by module
+ * replacement plugins.
*
* @private
- * @param {Function} func The function to associate metadata with.
- * @param {*} data The metadata.
- * @returns {Function} Returns `func`.
+ * @type {Function}
+ * @param {Function} func The function to apply a rest parameter to.
+ * @returns {Function} Returns the new function.
*/
- var baseSetData = !metaMap ? identity : function(func, data) {
- metaMap.set(func, data);
- return func;
- };
+ var castRest = baseRest;
/**
- * The base implementation of `setToString` without support for hot loop shorting.
+ * Casts `array` to a slice if it's needed.
*
* @private
- * @param {Function} func The function to modify.
- * @param {Function} string The `toString` result.
- * @returns {Function} Returns `func`.
+ * @param {Array} array The array to inspect.
+ * @param {number} start The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the cast slice.
*/
- var baseSetToString = !defineProperty ? identity : function(func, string) {
- return defineProperty(func, 'toString', {
- 'configurable': true,
- 'enumerable': false,
- 'value': constant(string),
- 'writable': true
- });
- };
+ function castSlice(array, start, end) {
+ var length = array.length;
+ end = end === undefined ? length : end;
+ return (!start && end >= length) ? array : baseSlice(array, start, end);
+ }
/**
- * The base implementation of `_.shuffle`.
+ * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).
*
* @private
- * @param {Array|Object} collection The collection to shuffle.
- * @returns {Array} Returns the new shuffled array.
+ * @param {number|Object} id The timer id or timeout object of the timer to clear.
*/
- function baseShuffle(collection) {
- return shuffleSelf(values(collection));
- }
+ var clearTimeout = ctxClearTimeout || function(id) {
+ return root.clearTimeout(id);
+ };
/**
- * The base implementation of `_.slice` without an iteratee call guard.
+ * Creates a clone of `buffer`.
*
* @private
- * @param {Array} array The array to slice.
- * @param {number} [start=0] The start position.
- * @param {number} [end=array.length] The end position.
- * @returns {Array} Returns the slice of `array`.
+ * @param {Buffer} buffer The buffer to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Buffer} Returns the cloned buffer.
*/
- function baseSlice(array, start, end) {
- var index = -1,
- length = array.length;
-
- if (start < 0) {
- start = -start > length ? 0 : (length + start);
- }
- end = end > length ? length : end;
- if (end < 0) {
- end += length;
- }
- length = start > end ? 0 : ((end - start) >>> 0);
- start >>>= 0;
-
- var result = Array(length);
- while (++index < length) {
- result[index] = array[index + start];
+ function cloneBuffer(buffer, isDeep) {
+ if (isDeep) {
+ return buffer.slice();
}
+ var length = buffer.length,
+ result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
+
+ buffer.copy(result);
return result;
}
/**
- * The base implementation of `_.some` without support for iteratee shorthands.
+ * Creates a clone of `arrayBuffer`.
*
* @private
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} predicate The function invoked per iteration.
- * @returns {boolean} Returns `true` if any element passes the predicate check,
- * else `false`.
+ * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
+ * @returns {ArrayBuffer} Returns the cloned array buffer.
*/
- function baseSome(collection, predicate) {
- var result;
-
- baseEach(collection, function(value, index, collection) {
- result = predicate(value, index, collection);
- return !result;
- });
- return !!result;
+ function cloneArrayBuffer(arrayBuffer) {
+ var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
+ new Uint8Array(result).set(new Uint8Array(arrayBuffer));
+ return result;
}
/**
- * The base implementation of `_.sortedIndex` and `_.sortedLastIndex` which
- * performs a binary search of `array` to determine the index at which `value`
- * should be inserted into `array` in order to maintain its sort order.
+ * Creates a clone of `dataView`.
*
* @private
- * @param {Array} array The sorted array to inspect.
- * @param {*} value The value to evaluate.
- * @param {boolean} [retHighest] Specify returning the highest qualified index.
- * @returns {number} Returns the index at which `value` should be inserted
- * into `array`.
+ * @param {Object} dataView The data view to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned data view.
*/
- function baseSortedIndex(array, value, retHighest) {
- var low = 0,
- high = array == null ? low : array.length;
-
- if (typeof value == 'number' && value === value && high <= HALF_MAX_ARRAY_LENGTH) {
- while (low < high) {
- var mid = (low + high) >>> 1,
- computed = array[mid];
-
- if (computed !== null && !isSymbol(computed) &&
- (retHighest ? (computed <= value) : (computed < value))) {
- low = mid + 1;
- } else {
- high = mid;
- }
- }
- return high;
- }
- return baseSortedIndexBy(array, value, identity, retHighest);
+ function cloneDataView(dataView, isDeep) {
+ var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
+ return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
}
/**
- * The base implementation of `_.sortedIndexBy` and `_.sortedLastIndexBy`
- * which invokes `iteratee` for `value` and each element of `array` to compute
- * their sort ranking. The iteratee is invoked with one argument; (value).
+ * Creates a clone of `map`.
*
* @private
- * @param {Array} array The sorted array to inspect.
- * @param {*} value The value to evaluate.
- * @param {Function} iteratee The iteratee invoked per element.
- * @param {boolean} [retHighest] Specify returning the highest qualified index.
- * @returns {number} Returns the index at which `value` should be inserted
- * into `array`.
+ * @param {Object} map The map to clone.
+ * @param {Function} cloneFunc The function to clone values.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned map.
*/
- function baseSortedIndexBy(array, value, iteratee, retHighest) {
- var low = 0,
- high = array == null ? 0 : array.length;
- if (high === 0) {
- return 0;
- }
-
- value = iteratee(value);
- var valIsNaN = value !== value,
- valIsNull = value === null,
- valIsSymbol = isSymbol(value),
- valIsUndefined = value === undefined;
-
- while (low < high) {
- var mid = nativeFloor((low + high) / 2),
- computed = iteratee(array[mid]),
- othIsDefined = computed !== undefined,
- othIsNull = computed === null,
- othIsReflexive = computed === computed,
- othIsSymbol = isSymbol(computed);
-
- if (valIsNaN) {
- var setLow = retHighest || othIsReflexive;
- } else if (valIsUndefined) {
- setLow = othIsReflexive && (retHighest || othIsDefined);
- } else if (valIsNull) {
- setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull);
- } else if (valIsSymbol) {
- setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol);
- } else if (othIsNull || othIsSymbol) {
- setLow = false;
- } else {
- setLow = retHighest ? (computed <= value) : (computed < value);
- }
- if (setLow) {
- low = mid + 1;
- } else {
- high = mid;
- }
- }
- return nativeMin(high, MAX_ARRAY_INDEX);
+ function cloneMap(map, isDeep, cloneFunc) {
+ var array = isDeep ? cloneFunc(mapToArray(map), CLONE_DEEP_FLAG) : mapToArray(map);
+ return arrayReduce(array, addMapEntry, new map.constructor);
}
/**
- * The base implementation of `_.sortedUniq` and `_.sortedUniqBy` without
- * support for iteratee shorthands.
+ * Creates a clone of `regexp`.
*
* @private
- * @param {Array} array The array to inspect.
- * @param {Function} [iteratee] The iteratee invoked per element.
- * @returns {Array} Returns the new duplicate free array.
+ * @param {Object} regexp The regexp to clone.
+ * @returns {Object} Returns the cloned regexp.
*/
- function baseSortedUniq(array, iteratee) {
- var index = -1,
- length = array.length,
- resIndex = 0,
- result = [];
-
- while (++index < length) {
- var value = array[index],
- computed = iteratee ? iteratee(value) : value;
-
- if (!index || !eq(computed, seen)) {
- var seen = computed;
- result[resIndex++] = value === 0 ? 0 : value;
- }
- }
+ function cloneRegExp(regexp) {
+ var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
+ result.lastIndex = regexp.lastIndex;
return result;
}
/**
- * The base implementation of `_.toNumber` which doesn't ensure correct
- * conversions of binary, hexadecimal, or octal string values.
+ * Creates a clone of `set`.
*
* @private
- * @param {*} value The value to process.
- * @returns {number} Returns the number.
+ * @param {Object} set The set to clone.
+ * @param {Function} cloneFunc The function to clone values.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned set.
*/
- function baseToNumber(value) {
- if (typeof value == 'number') {
- return value;
- }
- if (isSymbol(value)) {
- return NAN;
- }
- return +value;
+ function cloneSet(set, isDeep, cloneFunc) {
+ var array = isDeep ? cloneFunc(setToArray(set), CLONE_DEEP_FLAG) : setToArray(set);
+ return arrayReduce(array, addSetEntry, new set.constructor);
}
/**
- * The base implementation of `_.toString` which doesn't convert nullish
- * values to empty strings.
+ * Creates a clone of the `symbol` object.
*
* @private
- * @param {*} value The value to process.
- * @returns {string} Returns the string.
+ * @param {Object} symbol The symbol object to clone.
+ * @returns {Object} Returns the cloned symbol object.
*/
- function baseToString(value) {
- // Exit early for strings to avoid a performance hit in some environments.
- if (typeof value == 'string') {
- return value;
- }
- if (isArray(value)) {
- // Recursively convert values (susceptible to call stack limits).
- return arrayMap(value, baseToString) + '';
- }
- if (isSymbol(value)) {
- return symbolToString ? symbolToString.call(value) : '';
- }
- var result = (value + '');
- return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+ function cloneSymbol(symbol) {
+ return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
}
/**
- * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+ * Creates a clone of `typedArray`.
*
* @private
- * @param {Array} array The array to inspect.
- * @param {Function} [iteratee] The iteratee invoked per element.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new duplicate free array.
+ * @param {Object} typedArray The typed array to clone.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the cloned typed array.
*/
- function baseUniq(array, iteratee, comparator) {
- var index = -1,
- includes = arrayIncludes,
- length = array.length,
- isCommon = true,
- result = [],
- seen = result;
+ function cloneTypedArray(typedArray, isDeep) {
+ var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
+ return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
+ }
- if (comparator) {
- isCommon = false;
- includes = arrayIncludesWith;
- }
- else if (length >= LARGE_ARRAY_SIZE) {
- var set = iteratee ? null : createSet(array);
- if (set) {
- return setToArray(set);
- }
- isCommon = false;
- includes = cacheHas;
- seen = new SetCache;
- }
- else {
- seen = iteratee ? [] : result;
- }
- outer:
- while (++index < length) {
- var value = array[index],
- computed = iteratee ? iteratee(value) : value;
+ /**
+ * Compares values to sort them in ascending order.
+ *
+ * @private
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {number} Returns the sort order indicator for `value`.
+ */
+ function compareAscending(value, other) {
+ if (value !== other) {
+ var valIsDefined = value !== undefined,
+ valIsNull = value === null,
+ valIsReflexive = value === value,
+ valIsSymbol = isSymbol(value);
- value = (comparator || value !== 0) ? value : 0;
- if (isCommon && computed === computed) {
- var seenIndex = seen.length;
- while (seenIndex--) {
- if (seen[seenIndex] === computed) {
- continue outer;
- }
- }
- if (iteratee) {
- seen.push(computed);
- }
- result.push(value);
+ var othIsDefined = other !== undefined,
+ othIsNull = other === null,
+ othIsReflexive = other === other,
+ othIsSymbol = isSymbol(other);
+
+ if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
+ (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
+ (valIsNull && othIsDefined && othIsReflexive) ||
+ (!valIsDefined && othIsReflexive) ||
+ !valIsReflexive) {
+ return 1;
}
- else if (!includes(seen, computed, comparator)) {
- if (seen !== result) {
- seen.push(computed);
- }
- result.push(value);
+ if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
+ (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
+ (othIsNull && valIsDefined && valIsReflexive) ||
+ (!othIsDefined && valIsReflexive) ||
+ !othIsReflexive) {
+ return -1;
}
}
- return result;
+ return 0;
}
/**
- * The base implementation of `_.unset`.
+ * Used by `_.orderBy` to compare multiple properties of a value to another
+ * and stable sort them.
*
- * @private
- * @param {Object} object The object to modify.
- * @param {Array|string} path The property path to unset.
- * @returns {boolean} Returns `true` if the property is deleted, else `false`.
- */
- function baseUnset(object, path) {
- path = castPath(path, object);
- object = parent(object, path);
- return object == null || delete object[toKey(last(path))];
- }
-
- /**
- * The base implementation of `_.update`.
+ * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
+ * specify an order of "desc" for descending or "asc" for ascending sort order
+ * of corresponding values.
*
* @private
- * @param {Object} object The object to modify.
- * @param {Array|string} path The path of the property to update.
- * @param {Function} updater The function to produce the updated value.
- * @param {Function} [customizer] The function to customize path creation.
- * @returns {Object} Returns `object`.
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {boolean[]|string[]} orders The order to sort by for each property.
+ * @returns {number} Returns the sort order indicator for `object`.
*/
- function baseUpdate(object, path, updater, customizer) {
- return baseSet(object, path, updater(baseGet(object, path)), customizer);
+ function compareMultiple(object, other, orders) {
+ var index = -1,
+ objCriteria = object.criteria,
+ othCriteria = other.criteria,
+ length = objCriteria.length,
+ ordersLength = orders.length;
+
+ while (++index < length) {
+ var result = compareAscending(objCriteria[index], othCriteria[index]);
+ if (result) {
+ if (index >= ordersLength) {
+ return result;
+ }
+ var order = orders[index];
+ return result * (order == 'desc' ? -1 : 1);
+ }
+ }
+ // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+ // that causes it, under certain circumstances, to provide the same value for
+ // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
+ // for more details.
+ //
+ // This also ensures a stable sort in V8 and other engines.
+ // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
+ return object.index - other.index;
}
/**
- * The base implementation of methods like `_.dropWhile` and `_.takeWhile`
- * without support for iteratee shorthands.
+ * Creates an array that is the composition of partially applied arguments,
+ * placeholders, and provided arguments into a single array of arguments.
*
* @private
- * @param {Array} array The array to query.
- * @param {Function} predicate The function invoked per iteration.
- * @param {boolean} [isDrop] Specify dropping elements instead of taking them.
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Array} Returns the slice of `array`.
+ * @param {Array} args The provided arguments.
+ * @param {Array} partials The arguments to prepend to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @params {boolean} [isCurried] Specify composing for a curried function.
+ * @returns {Array} Returns the new array of composed arguments.
*/
- function baseWhile(array, predicate, isDrop, fromRight) {
- var length = array.length,
- index = fromRight ? length : -1;
-
- while ((fromRight ? index-- : ++index < length) &&
- predicate(array[index], index, array)) {}
+ function composeArgs(args, partials, holders, isCurried) {
+ var argsIndex = -1,
+ argsLength = args.length,
+ holdersLength = holders.length,
+ leftIndex = -1,
+ leftLength = partials.length,
+ rangeLength = nativeMax(argsLength - holdersLength, 0),
+ result = Array(leftLength + rangeLength),
+ isUncurried = !isCurried;
- return isDrop
- ? baseSlice(array, (fromRight ? 0 : index), (fromRight ? index + 1 : length))
- : baseSlice(array, (fromRight ? index + 1 : 0), (fromRight ? length : index));
+ while (++leftIndex < leftLength) {
+ result[leftIndex] = partials[leftIndex];
+ }
+ while (++argsIndex < holdersLength) {
+ if (isUncurried || argsIndex < argsLength) {
+ result[holders[argsIndex]] = args[argsIndex];
+ }
+ }
+ while (rangeLength--) {
+ result[leftIndex++] = args[argsIndex++];
+ }
+ return result;
}
/**
- * The base implementation of `wrapperValue` which returns the result of
- * performing a sequence of actions on the unwrapped `value`, where each
- * successive action is supplied the return value of the previous.
+ * This function is like `composeArgs` except that the arguments composition
+ * is tailored for `_.partialRight`.
*
* @private
- * @param {*} value The unwrapped value.
- * @param {Array} actions Actions to perform to resolve the unwrapped value.
- * @returns {*} Returns the resolved value.
+ * @param {Array} args The provided arguments.
+ * @param {Array} partials The arguments to append to those provided.
+ * @param {Array} holders The `partials` placeholder indexes.
+ * @params {boolean} [isCurried] Specify composing for a curried function.
+ * @returns {Array} Returns the new array of composed arguments.
*/
- function baseWrapperValue(value, actions) {
- var result = value;
- if (result instanceof LazyWrapper) {
- result = result.value();
+ function composeArgsRight(args, partials, holders, isCurried) {
+ var argsIndex = -1,
+ argsLength = args.length,
+ holdersIndex = -1,
+ holdersLength = holders.length,
+ rightIndex = -1,
+ rightLength = partials.length,
+ rangeLength = nativeMax(argsLength - holdersLength, 0),
+ result = Array(rangeLength + rightLength),
+ isUncurried = !isCurried;
+
+ while (++argsIndex < rangeLength) {
+ result[argsIndex] = args[argsIndex];
}
- return arrayReduce(actions, function(result, action) {
- return action.func.apply(action.thisArg, arrayPush([result], action.args));
- }, result);
+ var offset = argsIndex;
+ while (++rightIndex < rightLength) {
+ result[offset + rightIndex] = partials[rightIndex];
+ }
+ while (++holdersIndex < holdersLength) {
+ if (isUncurried || argsIndex < argsLength) {
+ result[offset + holders[holdersIndex]] = args[argsIndex++];
+ }
+ }
+ return result;
}
/**
- * The base implementation of methods like `_.xor`, without support for
- * iteratee shorthands, that accepts an array of arrays to inspect.
+ * Copies the values of `source` to `array`.
*
* @private
- * @param {Array} arrays The arrays to inspect.
- * @param {Function} [iteratee] The iteratee invoked per element.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of values.
+ * @param {Array} source The array to copy values from.
+ * @param {Array} [array=[]] The array to copy values to.
+ * @returns {Array} Returns `array`.
*/
- function baseXor(arrays, iteratee, comparator) {
- var length = arrays.length;
- if (length < 2) {
- return length ? baseUniq(arrays[0]) : [];
- }
+ function copyArray(source, array) {
var index = -1,
- result = Array(length);
+ length = source.length;
+ array || (array = Array(length));
while (++index < length) {
- var array = arrays[index],
- othIndex = -1;
-
- while (++othIndex < length) {
- if (othIndex != index) {
- result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator);
- }
- }
+ array[index] = source[index];
}
- return baseUniq(baseFlatten(result, 1), iteratee, comparator);
+ return array;
}
/**
- * This base implementation of `_.zipObject` which assigns values using `assignFunc`.
+ * Copies properties of `source` to `object`.
*
* @private
- * @param {Array} props The property identifiers.
- * @param {Array} values The property values.
- * @param {Function} assignFunc The function to assign values.
- * @returns {Object} Returns the new object.
+ * @param {Object} source The object to copy properties from.
+ * @param {Array} props The property identifiers to copy.
+ * @param {Object} [object={}] The object to copy properties to.
+ * @param {Function} [customizer] The function to customize copied values.
+ * @returns {Object} Returns `object`.
*/
- function baseZipObject(props, values, assignFunc) {
+ function copyObject(source, props, object, customizer) {
+ var isNew = !object;
+ object || (object = {});
+
var index = -1,
- length = props.length,
- valsLength = values.length,
- result = {};
+ length = props.length;
while (++index < length) {
- var value = index < valsLength ? values[index] : undefined;
- assignFunc(result, props[index], value);
- }
- return result;
- }
+ var key = props[index];
- /**
- * Casts `value` to an empty array if it's not an array like object.
- *
- * @private
- * @param {*} value The value to inspect.
- * @returns {Array|Object} Returns the cast array-like object.
- */
- function castArrayLikeObject(value) {
- return isArrayLikeObject(value) ? value : [];
- }
+ var newValue = customizer
+ ? customizer(object[key], source[key], key, object, source)
+ : undefined;
- /**
- * Casts `value` to `identity` if it's not a function.
- *
- * @private
- * @param {*} value The value to inspect.
- * @returns {Function} Returns cast function.
- */
- function castFunction(value) {
- return typeof value == 'function' ? value : identity;
+ if (newValue === undefined) {
+ newValue = source[key];
+ }
+ if (isNew) {
+ baseAssignValue(object, key, newValue);
+ } else {
+ assignValue(object, key, newValue);
+ }
+ }
+ return object;
}
/**
- * Casts `value` to a path array if it's not one.
+ * Copies own symbols of `source` to `object`.
*
* @private
- * @param {*} value The value to inspect.
- * @param {Object} [object] The object to query keys on.
- * @returns {Array} Returns the cast property path array.
+ * @param {Object} source The object to copy symbols from.
+ * @param {Object} [object={}] The object to copy symbols to.
+ * @returns {Object} Returns `object`.
*/
- function castPath(value, object) {
- if (isArray(value)) {
- return value;
- }
- return isKey(value, object) ? [value] : stringToPath(toString(value));
+ function copySymbols(source, object) {
+ return copyObject(source, getSymbols(source), object);
}
/**
- * A `baseRest` alias which can be replaced with `identity` by module
- * replacement plugins.
+ * Copies own and inherited symbols of `source` to `object`.
*
* @private
- * @type {Function}
- * @param {Function} func The function to apply a rest parameter to.
- * @returns {Function} Returns the new function.
+ * @param {Object} source The object to copy symbols from.
+ * @param {Object} [object={}] The object to copy symbols to.
+ * @returns {Object} Returns `object`.
*/
- var castRest = baseRest;
+ function copySymbolsIn(source, object) {
+ return copyObject(source, getSymbolsIn(source), object);
+ }
/**
- * Casts `array` to a slice if it's needed.
+ * Creates a function like `_.groupBy`.
*
* @private
- * @param {Array} array The array to inspect.
- * @param {number} start The start position.
- * @param {number} [end=array.length] The end position.
- * @returns {Array} Returns the cast slice.
+ * @param {Function} setter The function to set accumulator values.
+ * @param {Function} [initializer] The accumulator object initializer.
+ * @returns {Function} Returns the new aggregator function.
*/
- function castSlice(array, start, end) {
- var length = array.length;
- end = end === undefined ? length : end;
- return (!start && end >= length) ? array : baseSlice(array, start, end);
+ function createAggregator(setter, initializer) {
+ return function(collection, iteratee) {
+ var func = isArray(collection) ? arrayAggregator : baseAggregator,
+ accumulator = initializer ? initializer() : {};
+
+ return func(collection, setter, getIteratee(iteratee, 2), accumulator);
+ };
}
/**
- * A simple wrapper around the global [`clearTimeout`](https://mdn.io/clearTimeout).
+ * Creates a function like `_.assign`.
*
* @private
- * @param {number|Object} id The timer id or timeout object of the timer to clear.
+ * @param {Function} assigner The function to assign values.
+ * @returns {Function} Returns the new assigner function.
*/
- var clearTimeout = ctxClearTimeout || function(id) {
- return root.clearTimeout(id);
- };
+ function createAssigner(assigner) {
+ return baseRest(function(object, sources) {
+ var index = -1,
+ length = sources.length,
+ customizer = length > 1 ? sources[length - 1] : undefined,
+ guard = length > 2 ? sources[2] : undefined;
+
+ customizer = (assigner.length > 3 && typeof customizer == 'function')
+ ? (length--, customizer)
+ : undefined;
+
+ if (guard && isIterateeCall(sources[0], sources[1], guard)) {
+ customizer = length < 3 ? undefined : customizer;
+ length = 1;
+ }
+ object = Object(object);
+ while (++index < length) {
+ var source = sources[index];
+ if (source) {
+ assigner(object, source, index, customizer);
+ }
+ }
+ return object;
+ });
+ }
/**
- * Creates a clone of `buffer`.
+ * Creates a `baseEach` or `baseEachRight` function.
*
* @private
- * @param {Buffer} buffer The buffer to clone.
- * @param {boolean} [isDeep] Specify a deep clone.
- * @returns {Buffer} Returns the cloned buffer.
+ * @param {Function} eachFunc The function to iterate over a collection.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
*/
- function cloneBuffer(buffer, isDeep) {
- if (isDeep) {
- return buffer.slice();
- }
- var length = buffer.length,
- result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
+ function createBaseEach(eachFunc, fromRight) {
+ return function(collection, iteratee) {
+ if (collection == null) {
+ return collection;
+ }
+ if (!isArrayLike(collection)) {
+ return eachFunc(collection, iteratee);
+ }
+ var length = collection.length,
+ index = fromRight ? length : -1,
+ iterable = Object(collection);
- buffer.copy(result);
- return result;
+ while ((fromRight ? index-- : ++index < length)) {
+ if (iteratee(iterable[index], index, iterable) === false) {
+ break;
+ }
+ }
+ return collection;
+ };
}
/**
- * Creates a clone of `arrayBuffer`.
+ * Creates a base function for methods like `_.forIn` and `_.forOwn`.
*
* @private
- * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
- * @returns {ArrayBuffer} Returns the cloned array buffer.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new base function.
*/
- function cloneArrayBuffer(arrayBuffer) {
- var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
- new Uint8Array(result).set(new Uint8Array(arrayBuffer));
- return result;
+ function createBaseFor(fromRight) {
+ return function(object, iteratee, keysFunc) {
+ var index = -1,
+ iterable = Object(object),
+ props = keysFunc(object),
+ length = props.length;
+
+ while (length--) {
+ var key = props[fromRight ? length : ++index];
+ if (iteratee(iterable[key], key, iterable) === false) {
+ break;
+ }
+ }
+ return object;
+ };
}
/**
- * Creates a clone of `dataView`.
+ * Creates a function that wraps `func` to invoke it with the optional `this`
+ * binding of `thisArg`.
*
* @private
- * @param {Object} dataView The data view to clone.
- * @param {boolean} [isDeep] Specify a deep clone.
- * @returns {Object} Returns the cloned data view.
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @returns {Function} Returns the new wrapped function.
*/
- function cloneDataView(dataView, isDeep) {
- var buffer = isDeep ? cloneArrayBuffer(dataView.buffer) : dataView.buffer;
- return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
+ function createBind(func, bitmask, thisArg) {
+ var isBind = bitmask & WRAP_BIND_FLAG,
+ Ctor = createCtor(func);
+
+ function wrapper() {
+ var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+ return fn.apply(isBind ? thisArg : this, arguments);
+ }
+ return wrapper;
}
/**
- * Creates a clone of `regexp`.
+ * Creates a function like `_.lowerFirst`.
*
* @private
- * @param {Object} regexp The regexp to clone.
- * @returns {Object} Returns the cloned regexp.
+ * @param {string} methodName The name of the `String` case method to use.
+ * @returns {Function} Returns the new case function.
*/
- function cloneRegExp(regexp) {
- var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
- result.lastIndex = regexp.lastIndex;
- return result;
+ function createCaseFirst(methodName) {
+ return function(string) {
+ string = toString(string);
+
+ var strSymbols = hasUnicode(string)
+ ? stringToArray(string)
+ : undefined;
+
+ var chr = strSymbols
+ ? strSymbols[0]
+ : string.charAt(0);
+
+ var trailing = strSymbols
+ ? castSlice(strSymbols, 1).join('')
+ : string.slice(1);
+
+ return chr[methodName]() + trailing;
+ };
}
/**
- * Creates a clone of the `symbol` object.
+ * Creates a function like `_.camelCase`.
*
* @private
- * @param {Object} symbol The symbol object to clone.
- * @returns {Object} Returns the cloned symbol object.
+ * @param {Function} callback The function to combine each word.
+ * @returns {Function} Returns the new compounder function.
*/
- function cloneSymbol(symbol) {
- return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
+ function createCompounder(callback) {
+ return function(string) {
+ return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
+ };
}
/**
- * Creates a clone of `typedArray`.
+ * Creates a function that produces an instance of `Ctor` regardless of
+ * whether it was invoked as part of a `new` expression or by `call` or `apply`.
*
* @private
- * @param {Object} typedArray The typed array to clone.
- * @param {boolean} [isDeep] Specify a deep clone.
- * @returns {Object} Returns the cloned typed array.
+ * @param {Function} Ctor The constructor to wrap.
+ * @returns {Function} Returns the new wrapped function.
*/
- function cloneTypedArray(typedArray, isDeep) {
- var buffer = isDeep ? cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
- return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
+ function createCtor(Ctor) {
+ return function() {
+ // Use a `switch` statement to work with class constructors. See
+ // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
+ // for more details.
+ var args = arguments;
+ switch (args.length) {
+ case 0: return new Ctor;
+ case 1: return new Ctor(args[0]);
+ case 2: return new Ctor(args[0], args[1]);
+ case 3: return new Ctor(args[0], args[1], args[2]);
+ case 4: return new Ctor(args[0], args[1], args[2], args[3]);
+ case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
+ case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
+ case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ }
+ var thisBinding = baseCreate(Ctor.prototype),
+ result = Ctor.apply(thisBinding, args);
+
+ // Mimic the constructor's `return` behavior.
+ // See https://es5.github.io/#x13.2.2 for more details.
+ return isObject(result) ? result : thisBinding;
+ };
}
/**
- * Compares values to sort them in ascending order.
+ * Creates a function that wraps `func` to enable currying.
*
* @private
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {number} Returns the sort order indicator for `value`.
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {number} arity The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
*/
- function compareAscending(value, other) {
- if (value !== other) {
- var valIsDefined = value !== undefined,
- valIsNull = value === null,
- valIsReflexive = value === value,
- valIsSymbol = isSymbol(value);
+ function createCurry(func, bitmask, arity) {
+ var Ctor = createCtor(func);
- var othIsDefined = other !== undefined,
- othIsNull = other === null,
- othIsReflexive = other === other,
- othIsSymbol = isSymbol(other);
+ function wrapper() {
+ var length = arguments.length,
+ args = Array(length),
+ index = length,
+ placeholder = getHolder(wrapper);
- if ((!othIsNull && !othIsSymbol && !valIsSymbol && value > other) ||
- (valIsSymbol && othIsDefined && othIsReflexive && !othIsNull && !othIsSymbol) ||
- (valIsNull && othIsDefined && othIsReflexive) ||
- (!valIsDefined && othIsReflexive) ||
- !valIsReflexive) {
- return 1;
+ while (index--) {
+ args[index] = arguments[index];
}
- if ((!valIsNull && !valIsSymbol && !othIsSymbol && value < other) ||
- (othIsSymbol && valIsDefined && valIsReflexive && !valIsNull && !valIsSymbol) ||
- (othIsNull && valIsDefined && valIsReflexive) ||
- (!othIsDefined && valIsReflexive) ||
- !othIsReflexive) {
- return -1;
+ var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
+ ? []
+ : replaceHolders(args, placeholder);
+
+ length -= holders.length;
+ if (length < arity) {
+ return createRecurry(
+ func, bitmask, createHybrid, wrapper.placeholder, undefined,
+ args, holders, undefined, undefined, arity - length);
}
+ var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+ return apply(fn, this, args);
}
- return 0;
+ return wrapper;
}
/**
- * Used by `_.orderBy` to compare multiple properties of a value to another
- * and stable sort them.
+ * Creates a `_.find` or `_.findLast` function.
*
- * If `orders` is unspecified, all values are sorted in ascending order. Otherwise,
- * specify an order of "desc" for descending or "asc" for ascending sort order
- * of corresponding values.
+ * @private
+ * @param {Function} findIndexFunc The function to find the collection index.
+ * @returns {Function} Returns the new find function.
+ */
+ function createFind(findIndexFunc) {
+ return function(collection, predicate, fromIndex) {
+ var iterable = Object(collection);
+ if (!isArrayLike(collection)) {
+ var iteratee = getIteratee(predicate, 3);
+ collection = keys(collection);
+ predicate = function(key) { return iteratee(iterable[key], key, iterable); };
+ }
+ var index = findIndexFunc(collection, predicate, fromIndex);
+ return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
+ };
+ }
+
+ /**
+ * Creates a `_.flow` or `_.flowRight` function.
*
* @private
- * @param {Object} object The object to compare.
- * @param {Object} other The other object to compare.
- * @param {boolean[]|string[]} orders The order to sort by for each property.
- * @returns {number} Returns the sort order indicator for `object`.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new flow function.
*/
- function compareMultiple(object, other, orders) {
- var index = -1,
- objCriteria = object.criteria,
- othCriteria = other.criteria,
- length = objCriteria.length,
- ordersLength = orders.length;
+ function createFlow(fromRight) {
+ return flatRest(function(funcs) {
+ var length = funcs.length,
+ index = length,
+ prereq = LodashWrapper.prototype.thru;
- while (++index < length) {
- var result = compareAscending(objCriteria[index], othCriteria[index]);
- if (result) {
- if (index >= ordersLength) {
- return result;
+ if (fromRight) {
+ funcs.reverse();
+ }
+ while (index--) {
+ var func = funcs[index];
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ if (prereq && !wrapper && getFuncName(func) == 'wrapper') {
+ var wrapper = new LodashWrapper([], true);
}
- var order = orders[index];
- return result * (order == 'desc' ? -1 : 1);
}
- }
- // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
- // that causes it, under certain circumstances, to provide the same value for
- // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
- // for more details.
- //
- // This also ensures a stable sort in V8 and other engines.
- // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details.
- return object.index - other.index;
+ index = wrapper ? index : length;
+ while (++index < length) {
+ func = funcs[index];
+
+ var funcName = getFuncName(func),
+ data = funcName == 'wrapper' ? getData(func) : undefined;
+
+ if (data && isLaziable(data[0]) &&
+ data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) &&
+ !data[4].length && data[9] == 1
+ ) {
+ wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
+ } else {
+ wrapper = (func.length == 1 && isLaziable(func))
+ ? wrapper[funcName]()
+ : wrapper.thru(func);
+ }
+ }
+ return function() {
+ var args = arguments,
+ value = args[0];
+
+ if (wrapper && args.length == 1 && isArray(value)) {
+ return wrapper.plant(value).value();
+ }
+ var index = 0,
+ result = length ? funcs[index].apply(this, args) : value;
+
+ while (++index < length) {
+ result = funcs[index].call(this, result);
+ }
+ return result;
+ };
+ });
}
/**
- * Creates an array that is the composition of partially applied arguments,
- * placeholders, and provided arguments into a single array of arguments.
+ * Creates a function that wraps `func` to invoke it with optional `this`
+ * binding of `thisArg`, partial application, and currying.
*
* @private
- * @param {Array} args The provided arguments.
- * @param {Array} partials The arguments to prepend to those provided.
- * @param {Array} holders The `partials` placeholder indexes.
- * @params {boolean} [isCurried] Specify composing for a curried function.
- * @returns {Array} Returns the new array of composed arguments.
+ * @param {Function|string} func The function or method name to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to prepend to those provided to
+ * the new function.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [partialsRight] The arguments to append to those provided
+ * to the new function.
+ * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
*/
- function composeArgs(args, partials, holders, isCurried) {
- var argsIndex = -1,
- argsLength = args.length,
- holdersLength = holders.length,
- leftIndex = -1,
- leftLength = partials.length,
- rangeLength = nativeMax(argsLength - holdersLength, 0),
- result = Array(leftLength + rangeLength),
- isUncurried = !isCurried;
+ function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
+ var isAry = bitmask & WRAP_ARY_FLAG,
+ isBind = bitmask & WRAP_BIND_FLAG,
+ isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
+ isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
+ isFlip = bitmask & WRAP_FLIP_FLAG,
+ Ctor = isBindKey ? undefined : createCtor(func);
- while (++leftIndex < leftLength) {
- result[leftIndex] = partials[leftIndex];
- }
- while (++argsIndex < holdersLength) {
- if (isUncurried || argsIndex < argsLength) {
- result[holders[argsIndex]] = args[argsIndex];
+ function wrapper() {
+ var length = arguments.length,
+ args = Array(length),
+ index = length;
+
+ while (index--) {
+ args[index] = arguments[index];
}
+ if (isCurried) {
+ var placeholder = getHolder(wrapper),
+ holdersCount = countHolders(args, placeholder);
+ }
+ if (partials) {
+ args = composeArgs(args, partials, holders, isCurried);
+ }
+ if (partialsRight) {
+ args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
+ }
+ length -= holdersCount;
+ if (isCurried && length < arity) {
+ var newHolders = replaceHolders(args, placeholder);
+ return createRecurry(
+ func, bitmask, createHybrid, wrapper.placeholder, thisArg,
+ args, newHolders, argPos, ary, arity - length
+ );
+ }
+ var thisBinding = isBind ? thisArg : this,
+ fn = isBindKey ? thisBinding[func] : func;
+
+ length = args.length;
+ if (argPos) {
+ args = reorder(args, argPos);
+ } else if (isFlip && length > 1) {
+ args.reverse();
+ }
+ if (isAry && ary < length) {
+ args.length = ary;
+ }
+ if (this && this !== root && this instanceof wrapper) {
+ fn = Ctor || createCtor(fn);
+ }
+ return fn.apply(thisBinding, args);
}
- while (rangeLength--) {
- result[leftIndex++] = args[argsIndex++];
- }
- return result;
+ return wrapper;
}
/**
- * This function is like `composeArgs` except that the arguments composition
- * is tailored for `_.partialRight`.
+ * Creates a function like `_.invertBy`.
*
* @private
- * @param {Array} args The provided arguments.
- * @param {Array} partials The arguments to append to those provided.
- * @param {Array} holders The `partials` placeholder indexes.
- * @params {boolean} [isCurried] Specify composing for a curried function.
- * @returns {Array} Returns the new array of composed arguments.
+ * @param {Function} setter The function to set accumulator values.
+ * @param {Function} toIteratee The function to resolve iteratees.
+ * @returns {Function} Returns the new inverter function.
*/
- function composeArgsRight(args, partials, holders, isCurried) {
- var argsIndex = -1,
- argsLength = args.length,
- holdersIndex = -1,
- holdersLength = holders.length,
- rightIndex = -1,
- rightLength = partials.length,
- rangeLength = nativeMax(argsLength - holdersLength, 0),
- result = Array(rangeLength + rightLength),
- isUncurried = !isCurried;
+ function createInverter(setter, toIteratee) {
+ return function(object, iteratee) {
+ return baseInverter(object, setter, toIteratee(iteratee), {});
+ };
+ }
- while (++argsIndex < rangeLength) {
- result[argsIndex] = args[argsIndex];
- }
- var offset = argsIndex;
- while (++rightIndex < rightLength) {
- result[offset + rightIndex] = partials[rightIndex];
- }
- while (++holdersIndex < holdersLength) {
- if (isUncurried || argsIndex < argsLength) {
- result[offset + holders[holdersIndex]] = args[argsIndex++];
+ /**
+ * Creates a function that performs a mathematical operation on two values.
+ *
+ * @private
+ * @param {Function} operator The function to perform the operation.
+ * @param {number} [defaultValue] The value used for `undefined` arguments.
+ * @returns {Function} Returns the new mathematical operation function.
+ */
+ function createMathOperation(operator, defaultValue) {
+ return function(value, other) {
+ var result;
+ if (value === undefined && other === undefined) {
+ return defaultValue;
+ }
+ if (value !== undefined) {
+ result = value;
+ }
+ if (other !== undefined) {
+ if (result === undefined) {
+ return other;
+ }
+ if (typeof value == 'string' || typeof other == 'string') {
+ value = baseToString(value);
+ other = baseToString(other);
+ } else {
+ value = baseToNumber(value);
+ other = baseToNumber(other);
+ }
+ result = operator(value, other);
}
- }
- return result;
+ return result;
+ };
}
/**
- * Copies the values of `source` to `array`.
+ * Creates a function like `_.over`.
*
* @private
- * @param {Array} source The array to copy values from.
- * @param {Array} [array=[]] The array to copy values to.
- * @returns {Array} Returns `array`.
+ * @param {Function} arrayFunc The function to iterate over iteratees.
+ * @returns {Function} Returns the new over function.
*/
- function copyArray(source, array) {
- var index = -1,
- length = source.length;
-
- array || (array = Array(length));
- while (++index < length) {
- array[index] = source[index];
- }
- return array;
+ function createOver(arrayFunc) {
+ return flatRest(function(iteratees) {
+ iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
+ return baseRest(function(args) {
+ var thisArg = this;
+ return arrayFunc(iteratees, function(iteratee) {
+ return apply(iteratee, thisArg, args);
+ });
+ });
+ });
}
/**
- * Copies properties of `source` to `object`.
+ * Creates the padding for `string` based on `length`. The `chars` string
+ * is truncated if the number of characters exceeds `length`.
*
* @private
- * @param {Object} source The object to copy properties from.
- * @param {Array} props The property identifiers to copy.
- * @param {Object} [object={}] The object to copy properties to.
- * @param {Function} [customizer] The function to customize copied values.
- * @returns {Object} Returns `object`.
+ * @param {number} length The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padding for `string`.
*/
- function copyObject(source, props, object, customizer) {
- var isNew = !object;
- object || (object = {});
-
- var index = -1,
- length = props.length;
-
- while (++index < length) {
- var key = props[index];
-
- var newValue = customizer
- ? customizer(object[key], source[key], key, object, source)
- : undefined;
+ function createPadding(length, chars) {
+ chars = chars === undefined ? ' ' : baseToString(chars);
- if (newValue === undefined) {
- newValue = source[key];
- }
- if (isNew) {
- baseAssignValue(object, key, newValue);
- } else {
- assignValue(object, key, newValue);
- }
+ var charsLength = chars.length;
+ if (charsLength < 2) {
+ return charsLength ? baseRepeat(chars, length) : chars;
}
- return object;
+ var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));
+ return hasUnicode(chars)
+ ? castSlice(stringToArray(result), 0, length).join('')
+ : result.slice(0, length);
}
/**
- * Copies own symbols of `source` to `object`.
+ * Creates a function that wraps `func` to invoke it with the `this` binding
+ * of `thisArg` and `partials` prepended to the arguments it receives.
*
* @private
- * @param {Object} source The object to copy symbols from.
- * @param {Object} [object={}] The object to copy symbols to.
- * @returns {Object} Returns `object`.
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {Array} partials The arguments to prepend to those provided to
+ * the new function.
+ * @returns {Function} Returns the new wrapped function.
*/
- function copySymbols(source, object) {
- return copyObject(source, getSymbols(source), object);
+ function createPartial(func, bitmask, thisArg, partials) {
+ var isBind = bitmask & WRAP_BIND_FLAG,
+ Ctor = createCtor(func);
+
+ function wrapper() {
+ var argsIndex = -1,
+ argsLength = arguments.length,
+ leftIndex = -1,
+ leftLength = partials.length,
+ args = Array(leftLength + argsLength),
+ fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+
+ while (++leftIndex < leftLength) {
+ args[leftIndex] = partials[leftIndex];
+ }
+ while (argsLength--) {
+ args[leftIndex++] = arguments[++argsIndex];
+ }
+ return apply(fn, isBind ? thisArg : this, args);
+ }
+ return wrapper;
}
/**
- * Copies own and inherited symbols of `source` to `object`.
+ * Creates a `_.range` or `_.rangeRight` function.
*
* @private
- * @param {Object} source The object to copy symbols from.
- * @param {Object} [object={}] The object to copy symbols to.
- * @returns {Object} Returns `object`.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {Function} Returns the new range function.
*/
- function copySymbolsIn(source, object) {
- return copyObject(source, getSymbolsIn(source), object);
+ function createRange(fromRight) {
+ return function(start, end, step) {
+ if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
+ end = step = undefined;
+ }
+ // Ensure the sign of `-0` is preserved.
+ start = toFinite(start);
+ if (end === undefined) {
+ end = start;
+ start = 0;
+ } else {
+ end = toFinite(end);
+ }
+ step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
+ return baseRange(start, end, step, fromRight);
+ };
}
/**
- * Creates a function like `_.groupBy`.
+ * Creates a function that performs a relational operation on two values.
*
* @private
- * @param {Function} setter The function to set accumulator values.
- * @param {Function} [initializer] The accumulator object initializer.
- * @returns {Function} Returns the new aggregator function.
+ * @param {Function} operator The function to perform the operation.
+ * @returns {Function} Returns the new relational operation function.
*/
- function createAggregator(setter, initializer) {
- return function(collection, iteratee) {
- var func = isArray(collection) ? arrayAggregator : baseAggregator,
- accumulator = initializer ? initializer() : {};
-
- return func(collection, setter, getIteratee(iteratee, 2), accumulator);
+ function createRelationalOperation(operator) {
+ return function(value, other) {
+ if (!(typeof value == 'string' && typeof other == 'string')) {
+ value = toNumber(value);
+ other = toNumber(other);
+ }
+ return operator(value, other);
};
}
/**
- * Creates a function like `_.assign`.
+ * Creates a function that wraps `func` to continue currying.
*
* @private
- * @param {Function} assigner The function to assign values.
- * @returns {Function} Returns the new assigner function.
+ * @param {Function} func The function to wrap.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {Function} wrapFunc The function to create the `func` wrapper.
+ * @param {*} placeholder The placeholder value.
+ * @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to prepend to those provided to
+ * the new function.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
+ * @returns {Function} Returns the new wrapped function.
*/
- function createAssigner(assigner) {
- return baseRest(function(object, sources) {
- var index = -1,
- length = sources.length,
- customizer = length > 1 ? sources[length - 1] : undefined,
- guard = length > 2 ? sources[2] : undefined;
+ function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
+ var isCurry = bitmask & WRAP_CURRY_FLAG,
+ newHolders = isCurry ? holders : undefined,
+ newHoldersRight = isCurry ? undefined : holders,
+ newPartials = isCurry ? partials : undefined,
+ newPartialsRight = isCurry ? undefined : partials;
- customizer = (assigner.length > 3 && typeof customizer == 'function')
- ? (length--, customizer)
- : undefined;
+ bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);
+ bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);
- if (guard && isIterateeCall(sources[0], sources[1], guard)) {
- customizer = length < 3 ? undefined : customizer;
- length = 1;
- }
- object = Object(object);
- while (++index < length) {
- var source = sources[index];
- if (source) {
- assigner(object, source, index, customizer);
- }
- }
- return object;
- });
+ if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
+ bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
+ }
+ var newData = [
+ func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
+ newHoldersRight, argPos, ary, arity
+ ];
+
+ var result = wrapFunc.apply(undefined, newData);
+ if (isLaziable(func)) {
+ setData(result, newData);
+ }
+ result.placeholder = placeholder;
+ return setWrapToString(result, func, bitmask);
}
/**
- * Creates a `baseEach` or `baseEachRight` function.
+ * Creates a function like `_.round`.
*
* @private
- * @param {Function} eachFunc The function to iterate over a collection.
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Function} Returns the new base function.
+ * @param {string} methodName The name of the `Math` method to use when rounding.
+ * @returns {Function} Returns the new round function.
*/
- function createBaseEach(eachFunc, fromRight) {
- return function(collection, iteratee) {
- if (collection == null) {
- return collection;
- }
- if (!isArrayLike(collection)) {
- return eachFunc(collection, iteratee);
- }
- var length = collection.length,
- index = fromRight ? length : -1,
- iterable = Object(collection);
+ function createRound(methodName) {
+ var func = Math[methodName];
+ return function(number, precision) {
+ number = toNumber(number);
+ precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);
+ if (precision) {
+ // Shift with exponential notation to avoid floating-point issues.
+ // See [MDN](https://mdn.io/round#Examples) for more details.
+ var pair = (toString(number) + 'e').split('e'),
+ value = func(pair[0] + 'e' + (+pair[1] + precision));
- while ((fromRight ? index-- : ++index < length)) {
- if (iteratee(iterable[index], index, iterable) === false) {
- break;
- }
+ pair = (toString(value) + 'e').split('e');
+ return +(pair[0] + 'e' + (+pair[1] - precision));
}
- return collection;
+ return func(number);
};
}
/**
- * Creates a base function for methods like `_.forIn` and `_.forOwn`.
+ * Creates a set object of `values`.
*
* @private
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Function} Returns the new base function.
+ * @param {Array} values The values to add to the set.
+ * @returns {Object} Returns the new set.
*/
- function createBaseFor(fromRight) {
- return function(object, iteratee, keysFunc) {
- var index = -1,
- iterable = Object(object),
- props = keysFunc(object),
- length = props.length;
+ var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
+ return new Set(values);
+ };
- while (length--) {
- var key = props[fromRight ? length : ++index];
- if (iteratee(iterable[key], key, iterable) === false) {
- break;
- }
+ /**
+ * Creates a `_.toPairs` or `_.toPairsIn` function.
+ *
+ * @private
+ * @param {Function} keysFunc The function to get the keys of a given object.
+ * @returns {Function} Returns the new pairs function.
+ */
+ function createToPairs(keysFunc) {
+ return function(object) {
+ var tag = getTag(object);
+ if (tag == mapTag) {
+ return mapToArray(object);
}
- return object;
+ if (tag == setTag) {
+ return setToPairs(object);
+ }
+ return baseToPairs(object, keysFunc(object));
};
}
/**
- * Creates a function that wraps `func` to invoke it with the optional `this`
- * binding of `thisArg`.
+ * Creates a function that either curries or invokes `func` with optional
+ * `this` binding and partially applied arguments.
*
* @private
- * @param {Function} func The function to wrap.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @param {Function|string} func The function or method name to wrap.
+ * @param {number} bitmask The bitmask flags.
+ * 1 - `_.bind`
+ * 2 - `_.bindKey`
+ * 4 - `_.curry` or `_.curryRight` of a bound function
+ * 8 - `_.curry`
+ * 16 - `_.curryRight`
+ * 32 - `_.partial`
+ * 64 - `_.partialRight`
+ * 128 - `_.rearg`
+ * 256 - `_.ary`
+ * 512 - `_.flip`
* @param {*} [thisArg] The `this` binding of `func`.
+ * @param {Array} [partials] The arguments to be partially applied.
+ * @param {Array} [holders] The `partials` placeholder indexes.
+ * @param {Array} [argPos] The argument positions of the new function.
+ * @param {number} [ary] The arity cap of `func`.
+ * @param {number} [arity] The arity of `func`.
* @returns {Function} Returns the new wrapped function.
*/
- function createBind(func, bitmask, thisArg) {
- var isBind = bitmask & WRAP_BIND_FLAG,
- Ctor = createCtor(func);
+ function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
+ var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
+ if (!isBindKey && typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var length = partials ? partials.length : 0;
+ if (!length) {
+ bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);
+ partials = holders = undefined;
+ }
+ ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
+ arity = arity === undefined ? arity : toInteger(arity);
+ length -= holders ? holders.length : 0;
- function wrapper() {
- var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
- return fn.apply(isBind ? thisArg : this, arguments);
+ if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {
+ var partialsRight = partials,
+ holdersRight = holders;
+
+ partials = holders = undefined;
}
- return wrapper;
+ var data = isBindKey ? undefined : getData(func);
+
+ var newData = [
+ func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
+ argPos, ary, arity
+ ];
+
+ if (data) {
+ mergeData(newData, data);
+ }
+ func = newData[0];
+ bitmask = newData[1];
+ thisArg = newData[2];
+ partials = newData[3];
+ holders = newData[4];
+ arity = newData[9] = newData[9] === undefined
+ ? (isBindKey ? 0 : func.length)
+ : nativeMax(newData[9] - length, 0);
+
+ if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
+ bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
+ }
+ if (!bitmask || bitmask == WRAP_BIND_FLAG) {
+ var result = createBind(func, bitmask, thisArg);
+ } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
+ result = createCurry(func, bitmask, arity);
+ } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
+ result = createPartial(func, bitmask, thisArg, partials);
+ } else {
+ result = createHybrid.apply(undefined, newData);
+ }
+ var setter = data ? baseSetData : setData;
+ return setWrapToString(setter(result, newData), func, bitmask);
}
/**
- * Creates a function like `_.lowerFirst`.
+ * Used by `_.defaults` to customize its `_.assignIn` use to assign properties
+ * of source objects to the destination object for all destination properties
+ * that resolve to `undefined`.
*
* @private
- * @param {string} methodName The name of the `String` case method to use.
- * @returns {Function} Returns the new case function.
+ * @param {*} objValue The destination value.
+ * @param {*} srcValue The source value.
+ * @param {string} key The key of the property to assign.
+ * @param {Object} object The parent object of `objValue`.
+ * @returns {*} Returns the value to assign.
*/
- function createCaseFirst(methodName) {
- return function(string) {
- string = toString(string);
-
- var strSymbols = hasUnicode(string)
- ? stringToArray(string)
- : undefined;
-
- var chr = strSymbols
- ? strSymbols[0]
- : string.charAt(0);
-
- var trailing = strSymbols
- ? castSlice(strSymbols, 1).join('')
- : string.slice(1);
+ function customDefaultsAssignIn(objValue, srcValue, key, object) {
+ if (objValue === undefined ||
+ (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {
+ return srcValue;
+ }
+ return objValue;
+ }
- return chr[methodName]() + trailing;
- };
+ /**
+ * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source
+ * objects into destination objects that are passed thru.
+ *
+ * @private
+ * @param {*} objValue The destination value.
+ * @param {*} srcValue The source value.
+ * @param {string} key The key of the property to merge.
+ * @param {Object} object The parent object of `objValue`.
+ * @param {Object} source The parent object of `srcValue`.
+ * @param {Object} [stack] Tracks traversed source values and their merged
+ * counterparts.
+ * @returns {*} Returns the value to assign.
+ */
+ function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
+ if (isObject(objValue) && isObject(srcValue)) {
+ // Recursively merge objects and arrays (susceptible to call stack limits).
+ stack.set(srcValue, objValue);
+ baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);
+ stack['delete'](srcValue);
+ }
+ return objValue;
}
/**
- * Creates a function like `_.camelCase`.
+ * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
+ * objects.
*
* @private
- * @param {Function} callback The function to combine each word.
- * @returns {Function} Returns the new compounder function.
+ * @param {*} value The value to inspect.
+ * @param {string} key The key of the property to inspect.
+ * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
*/
- function createCompounder(callback) {
- return function(string) {
- return arrayReduce(words(deburr(string).replace(reApos, '')), callback, '');
- };
+ function customOmitClone(value) {
+ return isPlainObject(value) ? undefined : value;
}
/**
- * Creates a function that produces an instance of `Ctor` regardless of
- * whether it was invoked as part of a `new` expression or by `call` or `apply`.
+ * A specialized version of `baseIsEqualDeep` for arrays with support for
+ * partial deep comparisons.
*
* @private
- * @param {Function} Ctor The constructor to wrap.
- * @returns {Function} Returns the new wrapped function.
+ * @param {Array} array The array to compare.
+ * @param {Array} other The other array to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} stack Tracks traversed `array` and `other` objects.
+ * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
*/
- function createCtor(Ctor) {
- return function() {
- // Use a `switch` statement to work with class constructors. See
- // http://ecma-international.org/ecma-262/7.0/#sec-ecmascript-function-objects-call-thisargument-argumentslist
- // for more details.
- var args = arguments;
- switch (args.length) {
- case 0: return new Ctor;
- case 1: return new Ctor(args[0]);
- case 2: return new Ctor(args[0], args[1]);
- case 3: return new Ctor(args[0], args[1], args[2]);
- case 4: return new Ctor(args[0], args[1], args[2], args[3]);
- case 5: return new Ctor(args[0], args[1], args[2], args[3], args[4]);
- case 6: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5]);
- case 7: return new Ctor(args[0], args[1], args[2], args[3], args[4], args[5], args[6]);
+ function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
+ var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+ arrLength = array.length,
+ othLength = other.length;
+
+ if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(array);
+ if (stacked && stack.get(other)) {
+ return stacked == other;
+ }
+ var index = -1,
+ result = true,
+ seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;
+
+ stack.set(array, other);
+ stack.set(other, array);
+
+ // Ignore non-index properties.
+ while (++index < arrLength) {
+ var arrValue = array[index],
+ othValue = other[index];
+
+ if (customizer) {
+ var compared = isPartial
+ ? customizer(othValue, arrValue, index, other, array, stack)
+ : customizer(arrValue, othValue, index, array, other, stack);
+ }
+ if (compared !== undefined) {
+ if (compared) {
+ continue;
+ }
+ result = false;
+ break;
+ }
+ // Recursively compare arrays (susceptible to call stack limits).
+ if (seen) {
+ if (!arraySome(other, function(othValue, othIndex) {
+ if (!cacheHas(seen, othIndex) &&
+ (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
+ return seen.push(othIndex);
+ }
+ })) {
+ result = false;
+ break;
+ }
+ } else if (!(
+ arrValue === othValue ||
+ equalFunc(arrValue, othValue, bitmask, customizer, stack)
+ )) {
+ result = false;
+ break;
}
- var thisBinding = baseCreate(Ctor.prototype),
- result = Ctor.apply(thisBinding, args);
-
- // Mimic the constructor's `return` behavior.
- // See https://es5.github.io/#x13.2.2 for more details.
- return isObject(result) ? result : thisBinding;
- };
+ }
+ stack['delete'](array);
+ stack['delete'](other);
+ return result;
}
/**
- * Creates a function that wraps `func` to enable currying.
+ * A specialized version of `baseIsEqualDeep` for comparing objects of
+ * the same `toStringTag`.
+ *
+ * **Note:** This function only supports comparing values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
*
* @private
- * @param {Function} func The function to wrap.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
- * @param {number} arity The arity of `func`.
- * @returns {Function} Returns the new wrapped function.
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {string} tag The `toStringTag` of the objects to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} stack Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
- function createCurry(func, bitmask, arity) {
- var Ctor = createCtor(func);
+ function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
+ switch (tag) {
+ case dataViewTag:
+ if ((object.byteLength != other.byteLength) ||
+ (object.byteOffset != other.byteOffset)) {
+ return false;
+ }
+ object = object.buffer;
+ other = other.buffer;
- function wrapper() {
- var length = arguments.length,
- args = Array(length),
- index = length,
- placeholder = getHolder(wrapper);
+ case arrayBufferTag:
+ if ((object.byteLength != other.byteLength) ||
+ !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
+ return false;
+ }
+ return true;
- while (index--) {
- args[index] = arguments[index];
- }
- var holders = (length < 3 && args[0] !== placeholder && args[length - 1] !== placeholder)
- ? []
- : replaceHolders(args, placeholder);
+ case boolTag:
+ case dateTag:
+ case numberTag:
+ // Coerce booleans to `1` or `0` and dates to milliseconds.
+ // Invalid dates are coerced to `NaN`.
+ return eq(+object, +other);
- length -= holders.length;
- if (length < arity) {
- return createRecurry(
- func, bitmask, createHybrid, wrapper.placeholder, undefined,
- args, holders, undefined, undefined, arity - length);
- }
- var fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
- return apply(fn, this, args);
+ case errorTag:
+ return object.name == other.name && object.message == other.message;
+
+ case regexpTag:
+ case stringTag:
+ // Coerce regexes to strings and treat strings, primitives and objects,
+ // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
+ // for more details.
+ return object == (other + '');
+
+ case mapTag:
+ var convert = mapToArray;
+
+ case setTag:
+ var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
+ convert || (convert = setToArray);
+
+ if (object.size != other.size && !isPartial) {
+ return false;
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(object);
+ if (stacked) {
+ return stacked == other;
+ }
+ bitmask |= COMPARE_UNORDERED_FLAG;
+
+ // Recursively compare objects (susceptible to call stack limits).
+ stack.set(object, other);
+ var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
+ stack['delete'](object);
+ return result;
+
+ case symbolTag:
+ if (symbolValueOf) {
+ return symbolValueOf.call(object) == symbolValueOf.call(other);
+ }
}
- return wrapper;
+ return false;
}
/**
- * Creates a `_.find` or `_.findLast` function.
+ * A specialized version of `baseIsEqualDeep` for objects with support for
+ * partial deep comparisons.
*
* @private
- * @param {Function} findIndexFunc The function to find the collection index.
- * @returns {Function} Returns the new find function.
+ * @param {Object} object The object to compare.
+ * @param {Object} other The other object to compare.
+ * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
+ * @param {Function} customizer The function to customize comparisons.
+ * @param {Function} equalFunc The function to determine equivalents of values.
+ * @param {Object} stack Tracks traversed `object` and `other` objects.
+ * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
- function createFind(findIndexFunc) {
- return function(collection, predicate, fromIndex) {
- var iterable = Object(collection);
- if (!isArrayLike(collection)) {
- var iteratee = getIteratee(predicate, 3);
- collection = keys(collection);
- predicate = function(key) { return iteratee(iterable[key], key, iterable); };
+ function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
+ var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
+ objProps = getAllKeys(object),
+ objLength = objProps.length,
+ othProps = getAllKeys(other),
+ othLength = othProps.length;
+
+ if (objLength != othLength && !isPartial) {
+ return false;
+ }
+ var index = objLength;
+ while (index--) {
+ var key = objProps[index];
+ if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
+ return false;
}
- var index = findIndexFunc(collection, predicate, fromIndex);
- return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined;
- };
- }
+ }
+ // Assume cyclic values are equal.
+ var stacked = stack.get(object);
+ if (stacked && stack.get(other)) {
+ return stacked == other;
+ }
+ var result = true;
+ stack.set(object, other);
+ stack.set(other, object);
- /**
- * Creates a `_.flow` or `_.flowRight` function.
- *
- * @private
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Function} Returns the new flow function.
- */
- function createFlow(fromRight) {
- return flatRest(function(funcs) {
- var length = funcs.length,
- index = length,
- prereq = LodashWrapper.prototype.thru;
+ var skipCtor = isPartial;
+ while (++index < objLength) {
+ key = objProps[index];
+ var objValue = object[key],
+ othValue = other[key];
- if (fromRight) {
- funcs.reverse();
+ if (customizer) {
+ var compared = isPartial
+ ? customizer(othValue, objValue, key, other, object, stack)
+ : customizer(objValue, othValue, key, object, other, stack);
}
- while (index--) {
- var func = funcs[index];
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- if (prereq && !wrapper && getFuncName(func) == 'wrapper') {
- var wrapper = new LodashWrapper([], true);
- }
+ // Recursively compare objects (susceptible to call stack limits).
+ if (!(compared === undefined
+ ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
+ : compared
+ )) {
+ result = false;
+ break;
}
- index = wrapper ? index : length;
- while (++index < length) {
- func = funcs[index];
-
- var funcName = getFuncName(func),
- data = funcName == 'wrapper' ? getData(func) : undefined;
+ skipCtor || (skipCtor = key == 'constructor');
+ }
+ if (result && !skipCtor) {
+ var objCtor = object.constructor,
+ othCtor = other.constructor;
- if (data && isLaziable(data[0]) &&
- data[1] == (WRAP_ARY_FLAG | WRAP_CURRY_FLAG | WRAP_PARTIAL_FLAG | WRAP_REARG_FLAG) &&
- !data[4].length && data[9] == 1
- ) {
- wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
- } else {
- wrapper = (func.length == 1 && isLaziable(func))
- ? wrapper[funcName]()
- : wrapper.thru(func);
- }
+ // Non `Object` object instances with different constructors are not equal.
+ if (objCtor != othCtor &&
+ ('constructor' in object && 'constructor' in other) &&
+ !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
+ typeof othCtor == 'function' && othCtor instanceof othCtor)) {
+ result = false;
}
- return function() {
- var args = arguments,
- value = args[0];
-
- if (wrapper && args.length == 1 && isArray(value)) {
- return wrapper.plant(value).value();
- }
- var index = 0,
- result = length ? funcs[index].apply(this, args) : value;
+ }
+ stack['delete'](object);
+ stack['delete'](other);
+ return result;
+ }
- while (++index < length) {
- result = funcs[index].call(this, result);
- }
- return result;
- };
- });
+ /**
+ * A specialized version of `baseRest` which flattens the rest array.
+ *
+ * @private
+ * @param {Function} func The function to apply a rest parameter to.
+ * @returns {Function} Returns the new function.
+ */
+ function flatRest(func) {
+ return setToString(overRest(func, undefined, flatten), func + '');
}
/**
- * Creates a function that wraps `func` to invoke it with optional `this`
- * binding of `thisArg`, partial application, and currying.
+ * Creates an array of own enumerable property names and symbols of `object`.
*
* @private
- * @param {Function|string} func The function or method name to wrap.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
- * @param {*} [thisArg] The `this` binding of `func`.
- * @param {Array} [partials] The arguments to prepend to those provided to
- * the new function.
- * @param {Array} [holders] The `partials` placeholder indexes.
- * @param {Array} [partialsRight] The arguments to append to those provided
- * to the new function.
- * @param {Array} [holdersRight] The `partialsRight` placeholder indexes.
- * @param {Array} [argPos] The argument positions of the new function.
- * @param {number} [ary] The arity cap of `func`.
- * @param {number} [arity] The arity of `func`.
- * @returns {Function} Returns the new wrapped function.
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names and symbols.
*/
- function createHybrid(func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity) {
- var isAry = bitmask & WRAP_ARY_FLAG,
- isBind = bitmask & WRAP_BIND_FLAG,
- isBindKey = bitmask & WRAP_BIND_KEY_FLAG,
- isCurried = bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG),
- isFlip = bitmask & WRAP_FLIP_FLAG,
- Ctor = isBindKey ? undefined : createCtor(func);
+ function getAllKeys(object) {
+ return baseGetAllKeys(object, keys, getSymbols);
+ }
- function wrapper() {
- var length = arguments.length,
- args = Array(length),
- index = length;
+ /**
+ * Creates an array of own and inherited enumerable property names and
+ * symbols of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names and symbols.
+ */
+ function getAllKeysIn(object) {
+ return baseGetAllKeys(object, keysIn, getSymbolsIn);
+ }
- while (index--) {
- args[index] = arguments[index];
- }
- if (isCurried) {
- var placeholder = getHolder(wrapper),
- holdersCount = countHolders(args, placeholder);
- }
- if (partials) {
- args = composeArgs(args, partials, holders, isCurried);
- }
- if (partialsRight) {
- args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
- }
- length -= holdersCount;
- if (isCurried && length < arity) {
- var newHolders = replaceHolders(args, placeholder);
- return createRecurry(
- func, bitmask, createHybrid, wrapper.placeholder, thisArg,
- args, newHolders, argPos, ary, arity - length
- );
- }
- var thisBinding = isBind ? thisArg : this,
- fn = isBindKey ? thisBinding[func] : func;
+ /**
+ * Gets metadata for `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {*} Returns the metadata for `func`.
+ */
+ var getData = !metaMap ? noop : function(func) {
+ return metaMap.get(func);
+ };
- length = args.length;
- if (argPos) {
- args = reorder(args, argPos);
- } else if (isFlip && length > 1) {
- args.reverse();
- }
- if (isAry && ary < length) {
- args.length = ary;
- }
- if (this && this !== root && this instanceof wrapper) {
- fn = Ctor || createCtor(fn);
+ /**
+ * Gets the name of `func`.
+ *
+ * @private
+ * @param {Function} func The function to query.
+ * @returns {string} Returns the function name.
+ */
+ function getFuncName(func) {
+ var result = (func.name + ''),
+ array = realNames[result],
+ length = hasOwnProperty.call(realNames, result) ? array.length : 0;
+
+ while (length--) {
+ var data = array[length],
+ otherFunc = data.func;
+ if (otherFunc == null || otherFunc == func) {
+ return data.name;
}
- return fn.apply(thisBinding, args);
}
- return wrapper;
+ return result;
}
/**
- * Creates a function like `_.invertBy`.
+ * Gets the argument placeholder value for `func`.
*
* @private
- * @param {Function} setter The function to set accumulator values.
- * @param {Function} toIteratee The function to resolve iteratees.
- * @returns {Function} Returns the new inverter function.
+ * @param {Function} func The function to inspect.
+ * @returns {*} Returns the placeholder value.
*/
- function createInverter(setter, toIteratee) {
- return function(object, iteratee) {
- return baseInverter(object, setter, toIteratee(iteratee), {});
- };
+ function getHolder(func) {
+ var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
+ return object.placeholder;
}
/**
- * Creates a function that performs a mathematical operation on two values.
+ * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
+ * this function returns the custom method, otherwise it returns `baseIteratee`.
+ * If arguments are provided, the chosen function is invoked with them and
+ * its result is returned.
*
* @private
- * @param {Function} operator The function to perform the operation.
- * @param {number} [defaultValue] The value used for `undefined` arguments.
- * @returns {Function} Returns the new mathematical operation function.
+ * @param {*} [value] The value to convert to an iteratee.
+ * @param {number} [arity] The arity of the created iteratee.
+ * @returns {Function} Returns the chosen function or its result.
*/
- function createMathOperation(operator, defaultValue) {
- return function(value, other) {
- var result;
- if (value === undefined && other === undefined) {
- return defaultValue;
- }
- if (value !== undefined) {
- result = value;
- }
- if (other !== undefined) {
- if (result === undefined) {
- return other;
- }
- if (typeof value == 'string' || typeof other == 'string') {
- value = baseToString(value);
- other = baseToString(other);
- } else {
- value = baseToNumber(value);
- other = baseToNumber(other);
- }
- result = operator(value, other);
- }
- return result;
- };
+ function getIteratee() {
+ var result = lodash.iteratee || iteratee;
+ result = result === iteratee ? baseIteratee : result;
+ return arguments.length ? result(arguments[0], arguments[1]) : result;
}
/**
- * Creates a function like `_.over`.
+ * Gets the data for `map`.
*
* @private
- * @param {Function} arrayFunc The function to iterate over iteratees.
- * @returns {Function} Returns the new over function.
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
*/
- function createOver(arrayFunc) {
- return flatRest(function(iteratees) {
- iteratees = arrayMap(iteratees, baseUnary(getIteratee()));
- return baseRest(function(args) {
- var thisArg = this;
- return arrayFunc(iteratees, function(iteratee) {
- return apply(iteratee, thisArg, args);
- });
- });
- });
+ function getMapData(map, key) {
+ var data = map.__data__;
+ return isKeyable(key)
+ ? data[typeof key == 'string' ? 'string' : 'hash']
+ : data.map;
}
/**
- * Creates the padding for `string` based on `length`. The `chars` string
- * is truncated if the number of characters exceeds `length`.
+ * Gets the property names, values, and compare flags of `object`.
*
* @private
- * @param {number} length The padding length.
- * @param {string} [chars=' '] The string used as padding.
- * @returns {string} Returns the padding for `string`.
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the match data of `object`.
*/
- function createPadding(length, chars) {
- chars = chars === undefined ? ' ' : baseToString(chars);
+ function getMatchData(object) {
+ var result = keys(object),
+ length = result.length;
- var charsLength = chars.length;
- if (charsLength < 2) {
- return charsLength ? baseRepeat(chars, length) : chars;
+ while (length--) {
+ var key = result[length],
+ value = object[key];
+
+ result[length] = [key, value, isStrictComparable(value)];
}
- var result = baseRepeat(chars, nativeCeil(length / stringSize(chars)));
- return hasUnicode(chars)
- ? castSlice(stringToArray(result), 0, length).join('')
- : result.slice(0, length);
+ return result;
}
/**
- * Creates a function that wraps `func` to invoke it with the `this` binding
- * of `thisArg` and `partials` prepended to the arguments it receives.
+ * Gets the native function at `key` of `object`.
*
* @private
- * @param {Function} func The function to wrap.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
- * @param {*} thisArg The `this` binding of `func`.
- * @param {Array} partials The arguments to prepend to those provided to
- * the new function.
- * @returns {Function} Returns the new wrapped function.
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the method to get.
+ * @returns {*} Returns the function if it's native, else `undefined`.
*/
- function createPartial(func, bitmask, thisArg, partials) {
- var isBind = bitmask & WRAP_BIND_FLAG,
- Ctor = createCtor(func);
+ function getNative(object, key) {
+ var value = getValue(object, key);
+ return baseIsNative(value) ? value : undefined;
+ }
- function wrapper() {
- var argsIndex = -1,
- argsLength = arguments.length,
- leftIndex = -1,
- leftLength = partials.length,
- args = Array(leftLength + argsLength),
- fn = (this && this !== root && this instanceof wrapper) ? Ctor : func;
+ /**
+ * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the raw `toStringTag`.
+ */
+ function getRawTag(value) {
+ var isOwn = hasOwnProperty.call(value, symToStringTag),
+ tag = value[symToStringTag];
- while (++leftIndex < leftLength) {
- args[leftIndex] = partials[leftIndex];
- }
- while (argsLength--) {
- args[leftIndex++] = arguments[++argsIndex];
+ try {
+ value[symToStringTag] = undefined;
+ var unmasked = true;
+ } catch (e) {}
+
+ var result = nativeObjectToString.call(value);
+ if (unmasked) {
+ if (isOwn) {
+ value[symToStringTag] = tag;
+ } else {
+ delete value[symToStringTag];
}
- return apply(fn, isBind ? thisArg : this, args);
}
- return wrapper;
+ return result;
}
/**
- * Creates a `_.range` or `_.rangeRight` function.
+ * Creates an array of the own enumerable symbols of `object`.
*
* @private
- * @param {boolean} [fromRight] Specify iterating from right to left.
- * @returns {Function} Returns the new range function.
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of symbols.
*/
- function createRange(fromRight) {
- return function(start, end, step) {
- if (step && typeof step != 'number' && isIterateeCall(start, end, step)) {
- end = step = undefined;
- }
- // Ensure the sign of `-0` is preserved.
- start = toFinite(start);
- if (end === undefined) {
- end = start;
- start = 0;
- } else {
- end = toFinite(end);
- }
- step = step === undefined ? (start < end ? 1 : -1) : toFinite(step);
- return baseRange(start, end, step, fromRight);
- };
- }
+ var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
+ if (object == null) {
+ return [];
+ }
+ object = Object(object);
+ return arrayFilter(nativeGetSymbols(object), function(symbol) {
+ return propertyIsEnumerable.call(object, symbol);
+ });
+ };
/**
- * Creates a function that performs a relational operation on two values.
+ * Creates an array of the own and inherited enumerable symbols of `object`.
*
* @private
- * @param {Function} operator The function to perform the operation.
- * @returns {Function} Returns the new relational operation function.
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of symbols.
*/
- function createRelationalOperation(operator) {
- return function(value, other) {
- if (!(typeof value == 'string' && typeof other == 'string')) {
- value = toNumber(value);
- other = toNumber(other);
+ var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
+ var result = [];
+ while (object) {
+ arrayPush(result, getSymbols(object));
+ object = getPrototype(object);
+ }
+ return result;
+ };
+
+ /**
+ * Gets the `toStringTag` of `value`.
+ *
+ * @private
+ * @param {*} value The value to query.
+ * @returns {string} Returns the `toStringTag`.
+ */
+ var getTag = baseGetTag;
+
+ // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
+ if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
+ (Map && getTag(new Map) != mapTag) ||
+ (Promise && getTag(Promise.resolve()) != promiseTag) ||
+ (Set && getTag(new Set) != setTag) ||
+ (WeakMap && getTag(new WeakMap) != weakMapTag)) {
+ getTag = function(value) {
+ var result = baseGetTag(value),
+ Ctor = result == objectTag ? value.constructor : undefined,
+ ctorString = Ctor ? toSource(Ctor) : '';
+
+ if (ctorString) {
+ switch (ctorString) {
+ case dataViewCtorString: return dataViewTag;
+ case mapCtorString: return mapTag;
+ case promiseCtorString: return promiseTag;
+ case setCtorString: return setTag;
+ case weakMapCtorString: return weakMapTag;
+ }
}
- return operator(value, other);
+ return result;
};
}
/**
- * Creates a function that wraps `func` to continue currying.
+ * Gets the view, applying any `transforms` to the `start` and `end` positions.
*
* @private
- * @param {Function} func The function to wrap.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
- * @param {Function} wrapFunc The function to create the `func` wrapper.
- * @param {*} placeholder The placeholder value.
- * @param {*} [thisArg] The `this` binding of `func`.
- * @param {Array} [partials] The arguments to prepend to those provided to
- * the new function.
- * @param {Array} [holders] The `partials` placeholder indexes.
- * @param {Array} [argPos] The argument positions of the new function.
- * @param {number} [ary] The arity cap of `func`.
- * @param {number} [arity] The arity of `func`.
- * @returns {Function} Returns the new wrapped function.
+ * @param {number} start The start of the view.
+ * @param {number} end The end of the view.
+ * @param {Array} transforms The transformations to apply to the view.
+ * @returns {Object} Returns an object containing the `start` and `end`
+ * positions of the view.
*/
- function createRecurry(func, bitmask, wrapFunc, placeholder, thisArg, partials, holders, argPos, ary, arity) {
- var isCurry = bitmask & WRAP_CURRY_FLAG,
- newHolders = isCurry ? holders : undefined,
- newHoldersRight = isCurry ? undefined : holders,
- newPartials = isCurry ? partials : undefined,
- newPartialsRight = isCurry ? undefined : partials;
-
- bitmask |= (isCurry ? WRAP_PARTIAL_FLAG : WRAP_PARTIAL_RIGHT_FLAG);
- bitmask &= ~(isCurry ? WRAP_PARTIAL_RIGHT_FLAG : WRAP_PARTIAL_FLAG);
+ function getView(start, end, transforms) {
+ var index = -1,
+ length = transforms.length;
- if (!(bitmask & WRAP_CURRY_BOUND_FLAG)) {
- bitmask &= ~(WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG);
- }
- var newData = [
- func, bitmask, thisArg, newPartials, newHolders, newPartialsRight,
- newHoldersRight, argPos, ary, arity
- ];
+ while (++index < length) {
+ var data = transforms[index],
+ size = data.size;
- var result = wrapFunc.apply(undefined, newData);
- if (isLaziable(func)) {
- setData(result, newData);
+ switch (data.type) {
+ case 'drop': start += size; break;
+ case 'dropRight': end -= size; break;
+ case 'take': end = nativeMin(end, start + size); break;
+ case 'takeRight': start = nativeMax(start, end - size); break;
+ }
}
- result.placeholder = placeholder;
- return setWrapToString(result, func, bitmask);
+ return { 'start': start, 'end': end };
}
/**
- * Creates a function like `_.round`.
+ * Extracts wrapper details from the `source` body comment.
*
* @private
- * @param {string} methodName The name of the `Math` method to use when rounding.
- * @returns {Function} Returns the new round function.
+ * @param {string} source The source to inspect.
+ * @returns {Array} Returns the wrapper details.
*/
- function createRound(methodName) {
- var func = Math[methodName];
- return function(number, precision) {
- number = toNumber(number);
- precision = precision == null ? 0 : nativeMin(toInteger(precision), 292);
- if (precision && nativeIsFinite(number)) {
- // Shift with exponential notation to avoid floating-point issues.
- // See [MDN](https://mdn.io/round#Examples) for more details.
- var pair = (toString(number) + 'e').split('e'),
- value = func(pair[0] + 'e' + (+pair[1] + precision));
-
- pair = (toString(value) + 'e').split('e');
- return +(pair[0] + 'e' + (+pair[1] - precision));
- }
- return func(number);
- };
+ function getWrapDetails(source) {
+ var match = source.match(reWrapDetails);
+ return match ? match[1].split(reSplitDetails) : [];
}
/**
- * Creates a set object of `values`.
+ * Checks if `path` exists on `object`.
*
* @private
- * @param {Array} values The values to add to the set.
- * @returns {Object} Returns the new set.
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @param {Function} hasFunc The function to check properties.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
*/
- var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
- return new Set(values);
- };
+ function hasPath(object, path, hasFunc) {
+ path = castPath(path, object);
+
+ var index = -1,
+ length = path.length,
+ result = false;
+
+ while (++index < length) {
+ var key = toKey(path[index]);
+ if (!(result = object != null && hasFunc(object, key))) {
+ break;
+ }
+ object = object[key];
+ }
+ if (result || ++index != length) {
+ return result;
+ }
+ length = object == null ? 0 : object.length;
+ return !!length && isLength(length) && isIndex(key, length) &&
+ (isArray(object) || isArguments(object));
+ }
/**
- * Creates a `_.toPairs` or `_.toPairsIn` function.
+ * Initializes an array clone.
*
* @private
- * @param {Function} keysFunc The function to get the keys of a given object.
- * @returns {Function} Returns the new pairs function.
+ * @param {Array} array The array to clone.
+ * @returns {Array} Returns the initialized clone.
*/
- function createToPairs(keysFunc) {
- return function(object) {
- var tag = getTag(object);
- if (tag == mapTag) {
- return mapToArray(object);
- }
- if (tag == setTag) {
- return setToPairs(object);
- }
- return baseToPairs(object, keysFunc(object));
- };
+ function initCloneArray(array) {
+ var length = array.length,
+ result = array.constructor(length);
+
+ // Add properties assigned by `RegExp#exec`.
+ if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
+ result.index = array.index;
+ result.input = array.input;
+ }
+ return result;
}
/**
- * Creates a function that either curries or invokes `func` with optional
- * `this` binding and partially applied arguments.
+ * Initializes an object clone.
*
* @private
- * @param {Function|string} func The function or method name to wrap.
- * @param {number} bitmask The bitmask flags.
- * 1 - `_.bind`
- * 2 - `_.bindKey`
- * 4 - `_.curry` or `_.curryRight` of a bound function
- * 8 - `_.curry`
- * 16 - `_.curryRight`
- * 32 - `_.partial`
- * 64 - `_.partialRight`
- * 128 - `_.rearg`
- * 256 - `_.ary`
- * 512 - `_.flip`
- * @param {*} [thisArg] The `this` binding of `func`.
- * @param {Array} [partials] The arguments to be partially applied.
- * @param {Array} [holders] The `partials` placeholder indexes.
- * @param {Array} [argPos] The argument positions of the new function.
- * @param {number} [ary] The arity cap of `func`.
- * @param {number} [arity] The arity of `func`.
- * @returns {Function} Returns the new wrapped function.
+ * @param {Object} object The object to clone.
+ * @returns {Object} Returns the initialized clone.
+ */
+ function initCloneObject(object) {
+ return (typeof object.constructor == 'function' && !isPrototype(object))
+ ? baseCreate(getPrototype(object))
+ : {};
+ }
+
+ /**
+ * Initializes an object clone based on its `toStringTag`.
+ *
+ * **Note:** This function only supports cloning values with tags of
+ * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ *
+ * @private
+ * @param {Object} object The object to clone.
+ * @param {string} tag The `toStringTag` of the object to clone.
+ * @param {Function} cloneFunc The function to clone values.
+ * @param {boolean} [isDeep] Specify a deep clone.
+ * @returns {Object} Returns the initialized clone.
*/
- function createWrap(func, bitmask, thisArg, partials, holders, argPos, ary, arity) {
- var isBindKey = bitmask & WRAP_BIND_KEY_FLAG;
- if (!isBindKey && typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- var length = partials ? partials.length : 0;
- if (!length) {
- bitmask &= ~(WRAP_PARTIAL_FLAG | WRAP_PARTIAL_RIGHT_FLAG);
- partials = holders = undefined;
- }
- ary = ary === undefined ? ary : nativeMax(toInteger(ary), 0);
- arity = arity === undefined ? arity : toInteger(arity);
- length -= holders ? holders.length : 0;
+ function initCloneByTag(object, tag, cloneFunc, isDeep) {
+ var Ctor = object.constructor;
+ switch (tag) {
+ case arrayBufferTag:
+ return cloneArrayBuffer(object);
- if (bitmask & WRAP_PARTIAL_RIGHT_FLAG) {
- var partialsRight = partials,
- holdersRight = holders;
+ case boolTag:
+ case dateTag:
+ return new Ctor(+object);
- partials = holders = undefined;
- }
- var data = isBindKey ? undefined : getData(func);
+ case dataViewTag:
+ return cloneDataView(object, isDeep);
- var newData = [
- func, bitmask, thisArg, partials, holders, partialsRight, holdersRight,
- argPos, ary, arity
- ];
+ case float32Tag: case float64Tag:
+ case int8Tag: case int16Tag: case int32Tag:
+ case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
+ return cloneTypedArray(object, isDeep);
- if (data) {
- mergeData(newData, data);
- }
- func = newData[0];
- bitmask = newData[1];
- thisArg = newData[2];
- partials = newData[3];
- holders = newData[4];
- arity = newData[9] = newData[9] === undefined
- ? (isBindKey ? 0 : func.length)
- : nativeMax(newData[9] - length, 0);
+ case mapTag:
+ return cloneMap(object, isDeep, cloneFunc);
- if (!arity && bitmask & (WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG)) {
- bitmask &= ~(WRAP_CURRY_FLAG | WRAP_CURRY_RIGHT_FLAG);
- }
- if (!bitmask || bitmask == WRAP_BIND_FLAG) {
- var result = createBind(func, bitmask, thisArg);
- } else if (bitmask == WRAP_CURRY_FLAG || bitmask == WRAP_CURRY_RIGHT_FLAG) {
- result = createCurry(func, bitmask, arity);
- } else if ((bitmask == WRAP_PARTIAL_FLAG || bitmask == (WRAP_BIND_FLAG | WRAP_PARTIAL_FLAG)) && !holders.length) {
- result = createPartial(func, bitmask, thisArg, partials);
- } else {
- result = createHybrid.apply(undefined, newData);
+ case numberTag:
+ case stringTag:
+ return new Ctor(object);
+
+ case regexpTag:
+ return cloneRegExp(object);
+
+ case setTag:
+ return cloneSet(object, isDeep, cloneFunc);
+
+ case symbolTag:
+ return cloneSymbol(object);
}
- var setter = data ? baseSetData : setData;
- return setWrapToString(setter(result, newData), func, bitmask);
}
/**
- * Used by `_.defaults` to customize its `_.assignIn` use to assign properties
- * of source objects to the destination object for all destination properties
- * that resolve to `undefined`.
+ * Inserts wrapper `details` in a comment at the top of the `source` body.
*
* @private
- * @param {*} objValue The destination value.
- * @param {*} srcValue The source value.
- * @param {string} key The key of the property to assign.
- * @param {Object} object The parent object of `objValue`.
- * @returns {*} Returns the value to assign.
+ * @param {string} source The source to modify.
+ * @returns {Array} details The details to insert.
+ * @returns {string} Returns the modified source.
*/
- function customDefaultsAssignIn(objValue, srcValue, key, object) {
- if (objValue === undefined ||
- (eq(objValue, objectProto[key]) && !hasOwnProperty.call(object, key))) {
- return srcValue;
+ function insertWrapDetails(source, details) {
+ var length = details.length;
+ if (!length) {
+ return source;
}
- return objValue;
+ var lastIndex = length - 1;
+ details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
+ details = details.join(length > 2 ? ', ' : ' ');
+ return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
}
/**
- * Used by `_.defaultsDeep` to customize its `_.merge` use to merge source
- * objects into destination objects that are passed thru.
+ * Checks if `value` is a flattenable `arguments` object or array.
*
* @private
- * @param {*} objValue The destination value.
- * @param {*} srcValue The source value.
- * @param {string} key The key of the property to merge.
- * @param {Object} object The parent object of `objValue`.
- * @param {Object} source The parent object of `srcValue`.
- * @param {Object} [stack] Tracks traversed source values and their merged
- * counterparts.
- * @returns {*} Returns the value to assign.
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
*/
- function customDefaultsMerge(objValue, srcValue, key, object, source, stack) {
- if (isObject(objValue) && isObject(srcValue)) {
- // Recursively merge objects and arrays (susceptible to call stack limits).
- stack.set(srcValue, objValue);
- baseMerge(objValue, srcValue, undefined, customDefaultsMerge, stack);
- stack['delete'](srcValue);
- }
- return objValue;
+ function isFlattenable(value) {
+ return isArray(value) || isArguments(value) ||
+ !!(spreadableSymbol && value && value[spreadableSymbol]);
}
/**
- * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
- * objects.
+ * Checks if `value` is a valid array-like index.
*
* @private
- * @param {*} value The value to inspect.
- * @param {string} key The key of the property to inspect.
- * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
+ * @param {*} value The value to check.
+ * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+ * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
*/
- function customOmitClone(value) {
- return isPlainObject(value) ? undefined : value;
+ function isIndex(value, length) {
+ length = length == null ? MAX_SAFE_INTEGER : length;
+ return !!length &&
+ (typeof value == 'number' || reIsUint.test(value)) &&
+ (value > -1 && value % 1 == 0 && value < length);
}
/**
- * A specialized version of `baseIsEqualDeep` for arrays with support for
- * partial deep comparisons.
+ * Checks if the given arguments are from an iteratee call.
*
* @private
- * @param {Array} array The array to compare.
- * @param {Array} other The other array to compare.
- * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
- * @param {Function} customizer The function to customize comparisons.
- * @param {Function} equalFunc The function to determine equivalents of values.
- * @param {Object} stack Tracks traversed `array` and `other` objects.
- * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
+ * @param {*} value The potential iteratee value argument.
+ * @param {*} index The potential iteratee index or key argument.
+ * @param {*} object The potential iteratee object argument.
+ * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
+ * else `false`.
*/
- function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
- var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
- arrLength = array.length,
- othLength = other.length;
-
- if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
+ function isIterateeCall(value, index, object) {
+ if (!isObject(object)) {
return false;
}
- // Check that cyclic values are equal.
- var arrStacked = stack.get(array);
- var othStacked = stack.get(other);
- if (arrStacked && othStacked) {
- return arrStacked == other && othStacked == array;
+ var type = typeof index;
+ if (type == 'number'
+ ? (isArrayLike(object) && isIndex(index, object.length))
+ : (type == 'string' && index in object)
+ ) {
+ return eq(object[index], value);
}
- var index = -1,
- result = true,
- seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined;
+ return false;
+ }
- stack.set(array, other);
- stack.set(other, array);
+ /**
+ * Checks if `value` is a property name and not a property path.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @param {Object} [object] The object to query keys on.
+ * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
+ */
+ function isKey(value, object) {
+ if (isArray(value)) {
+ return false;
+ }
+ var type = typeof value;
+ if (type == 'number' || type == 'symbol' || type == 'boolean' ||
+ value == null || isSymbol(value)) {
+ return true;
+ }
+ return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
+ (object != null && value in Object(object));
+ }
- // Ignore non-index properties.
- while (++index < arrLength) {
- var arrValue = array[index],
- othValue = other[index];
+ /**
+ * Checks if `value` is suitable for use as unique object key.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ */
+ function isKeyable(value) {
+ var type = typeof value;
+ return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+ ? (value !== '__proto__')
+ : (value === null);
+ }
- if (customizer) {
- var compared = isPartial
- ? customizer(othValue, arrValue, index, other, array, stack)
- : customizer(arrValue, othValue, index, array, other, stack);
- }
- if (compared !== undefined) {
- if (compared) {
- continue;
- }
- result = false;
- break;
- }
- // Recursively compare arrays (susceptible to call stack limits).
- if (seen) {
- if (!arraySome(other, function(othValue, othIndex) {
- if (!cacheHas(seen, othIndex) &&
- (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
- return seen.push(othIndex);
- }
- })) {
- result = false;
- break;
- }
- } else if (!(
- arrValue === othValue ||
- equalFunc(arrValue, othValue, bitmask, customizer, stack)
- )) {
- result = false;
- break;
- }
+ /**
+ * Checks if `func` has a lazy counterpart.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
+ * else `false`.
+ */
+ function isLaziable(func) {
+ var funcName = getFuncName(func),
+ other = lodash[funcName];
+
+ if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
+ return false;
}
- stack['delete'](array);
- stack['delete'](other);
- return result;
+ if (func === other) {
+ return true;
+ }
+ var data = getData(other);
+ return !!data && func === data[0];
}
/**
- * A specialized version of `baseIsEqualDeep` for comparing objects of
- * the same `toStringTag`.
- *
- * **Note:** This function only supports comparing values with tags of
- * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
+ * Checks if `func` has its source masked.
*
* @private
- * @param {Object} object The object to compare.
- * @param {Object} other The other object to compare.
- * @param {string} tag The `toStringTag` of the objects to compare.
- * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
- * @param {Function} customizer The function to customize comparisons.
- * @param {Function} equalFunc The function to determine equivalents of values.
- * @param {Object} stack Tracks traversed `object` and `other` objects.
- * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` is masked, else `false`.
*/
- function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
- switch (tag) {
- case dataViewTag:
- if ((object.byteLength != other.byteLength) ||
- (object.byteOffset != other.byteOffset)) {
- return false;
- }
- object = object.buffer;
- other = other.buffer;
-
- case arrayBufferTag:
- if ((object.byteLength != other.byteLength) ||
- !equalFunc(new Uint8Array(object), new Uint8Array(other))) {
- return false;
- }
- return true;
-
- case boolTag:
- case dateTag:
- case numberTag:
- // Coerce booleans to `1` or `0` and dates to milliseconds.
- // Invalid dates are coerced to `NaN`.
- return eq(+object, +other);
+ function isMasked(func) {
+ return !!maskSrcKey && (maskSrcKey in func);
+ }
- case errorTag:
- return object.name == other.name && object.message == other.message;
+ /**
+ * Checks if `func` is capable of being masked.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `func` is maskable, else `false`.
+ */
+ var isMaskable = coreJsData ? isFunction : stubFalse;
- case regexpTag:
- case stringTag:
- // Coerce regexes to strings and treat strings, primitives and objects,
- // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
- // for more details.
- return object == (other + '');
+ /**
+ * Checks if `value` is likely a prototype object.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+ */
+ function isPrototype(value) {
+ var Ctor = value && value.constructor,
+ proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
- case mapTag:
- var convert = mapToArray;
+ return value === proto;
+ }
- case setTag:
- var isPartial = bitmask & COMPARE_PARTIAL_FLAG;
- convert || (convert = setToArray);
+ /**
+ * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` if suitable for strict
+ * equality comparisons, else `false`.
+ */
+ function isStrictComparable(value) {
+ return value === value && !isObject(value);
+ }
- if (object.size != other.size && !isPartial) {
- return false;
- }
- // Assume cyclic values are equal.
- var stacked = stack.get(object);
- if (stacked) {
- return stacked == other;
- }
- bitmask |= COMPARE_UNORDERED_FLAG;
+ /**
+ * A specialized version of `matchesProperty` for source values suitable
+ * for strict equality comparisons, i.e. `===`.
+ *
+ * @private
+ * @param {string} key The key of the property to get.
+ * @param {*} srcValue The value to match.
+ * @returns {Function} Returns the new spec function.
+ */
+ function matchesStrictComparable(key, srcValue) {
+ return function(object) {
+ if (object == null) {
+ return false;
+ }
+ return object[key] === srcValue &&
+ (srcValue !== undefined || (key in Object(object)));
+ };
+ }
- // Recursively compare objects (susceptible to call stack limits).
- stack.set(object, other);
- var result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
- stack['delete'](object);
- return result;
+ /**
+ * A specialized version of `_.memoize` which clears the memoized function's
+ * cache when it exceeds `MAX_MEMOIZE_SIZE`.
+ *
+ * @private
+ * @param {Function} func The function to have its output memoized.
+ * @returns {Function} Returns the new memoized function.
+ */
+ function memoizeCapped(func) {
+ var result = memoize(func, function(key) {
+ if (cache.size === MAX_MEMOIZE_SIZE) {
+ cache.clear();
+ }
+ return key;
+ });
- case symbolTag:
- if (symbolValueOf) {
- return symbolValueOf.call(object) == symbolValueOf.call(other);
- }
- }
- return false;
+ var cache = result.cache;
+ return result;
}
/**
- * A specialized version of `baseIsEqualDeep` for objects with support for
- * partial deep comparisons.
+ * Merges the function metadata of `source` into `data`.
+ *
+ * Merging metadata reduces the number of wrappers used to invoke a function.
+ * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
+ * may be applied regardless of execution order. Methods like `_.ary` and
+ * `_.rearg` modify function arguments, making the order in which they are
+ * executed important, preventing the merging of metadata. However, we make
+ * an exception for a safe combined case where curried functions have `_.ary`
+ * and or `_.rearg` applied.
*
* @private
- * @param {Object} object The object to compare.
- * @param {Object} other The other object to compare.
- * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
- * @param {Function} customizer The function to customize comparisons.
- * @param {Function} equalFunc The function to determine equivalents of values.
- * @param {Object} stack Tracks traversed `object` and `other` objects.
- * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
+ * @param {Array} data The destination metadata.
+ * @param {Array} source The source metadata.
+ * @returns {Array} Returns `data`.
*/
- function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
- var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
- objProps = getAllKeys(object),
- objLength = objProps.length,
- othProps = getAllKeys(other),
- othLength = othProps.length;
+ function mergeData(data, source) {
+ var bitmask = data[1],
+ srcBitmask = source[1],
+ newBitmask = bitmask | srcBitmask,
+ isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);
- if (objLength != othLength && !isPartial) {
- return false;
+ var isCombo =
+ ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) ||
+ ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) ||
+ ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG));
+
+ // Exit early if metadata can't be merged.
+ if (!(isCommon || isCombo)) {
+ return data;
}
- var index = objLength;
- while (index--) {
- var key = objProps[index];
- if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) {
- return false;
- }
+ // Use source `thisArg` if available.
+ if (srcBitmask & WRAP_BIND_FLAG) {
+ data[2] = source[2];
+ // Set when currying a bound function.
+ newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;
}
- // Check that cyclic values are equal.
- var objStacked = stack.get(object);
- var othStacked = stack.get(other);
- if (objStacked && othStacked) {
- return objStacked == other && othStacked == object;
+ // Compose partial arguments.
+ var value = source[3];
+ if (value) {
+ var partials = data[3];
+ data[3] = partials ? composeArgs(partials, value, source[4]) : value;
+ data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
}
- var result = true;
- stack.set(object, other);
- stack.set(other, object);
-
- var skipCtor = isPartial;
- while (++index < objLength) {
- key = objProps[index];
- var objValue = object[key],
- othValue = other[key];
-
- if (customizer) {
- var compared = isPartial
- ? customizer(othValue, objValue, key, other, object, stack)
- : customizer(objValue, othValue, key, object, other, stack);
- }
- // Recursively compare objects (susceptible to call stack limits).
- if (!(compared === undefined
- ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
- : compared
- )) {
- result = false;
- break;
- }
- skipCtor || (skipCtor = key == 'constructor');
+ // Compose partial right arguments.
+ value = source[5];
+ if (value) {
+ partials = data[5];
+ data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
+ data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
+ }
+ // Use source `argPos` if available.
+ value = source[7];
+ if (value) {
+ data[7] = value;
+ }
+ // Use source `ary` if it's smaller.
+ if (srcBitmask & WRAP_ARY_FLAG) {
+ data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
}
- if (result && !skipCtor) {
- var objCtor = object.constructor,
- othCtor = other.constructor;
+ // Use source `arity` if one is not provided.
+ if (data[9] == null) {
+ data[9] = source[9];
+ }
+ // Use source `func` and merge bitmasks.
+ data[0] = source[0];
+ data[1] = newBitmask;
- // Non `Object` object instances with different constructors are not equal.
- if (objCtor != othCtor &&
- ('constructor' in object && 'constructor' in other) &&
- !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
- typeof othCtor == 'function' && othCtor instanceof othCtor)) {
- result = false;
+ return data;
+ }
+
+ /**
+ * This function is like
+ * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
+ * except that it includes inherited enumerable properties.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
+ */
+ function nativeKeysIn(object) {
+ var result = [];
+ if (object != null) {
+ for (var key in Object(object)) {
+ result.push(key);
}
}
- stack['delete'](object);
- stack['delete'](other);
return result;
}
/**
- * A specialized version of `baseRest` which flattens the rest array.
+ * Converts `value` to a string using `Object.prototype.toString`.
+ *
+ * @private
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
+ */
+ function objectToString(value) {
+ return nativeObjectToString.call(value);
+ }
+
+ /**
+ * A specialized version of `baseRest` which transforms the rest array.
*
* @private
* @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @param {Function} transform The rest array transform.
* @returns {Function} Returns the new function.
*/
- function flatRest(func) {
- return setToString(overRest(func, undefined, flatten), func + '');
+ function overRest(func, start, transform) {
+ start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
+ return function() {
+ var args = arguments,
+ index = -1,
+ length = nativeMax(args.length - start, 0),
+ array = Array(length);
+
+ while (++index < length) {
+ array[index] = args[start + index];
+ }
+ index = -1;
+ var otherArgs = Array(start + 1);
+ while (++index < start) {
+ otherArgs[index] = args[index];
+ }
+ otherArgs[start] = transform(array);
+ return apply(func, this, otherArgs);
+ };
}
/**
- * Creates an array of own enumerable property names and symbols of `object`.
+ * Gets the parent value at `path` of `object`.
*
* @private
* @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names and symbols.
+ * @param {Array} path The path to get the parent value of.
+ * @returns {*} Returns the parent value.
*/
- function getAllKeys(object) {
- return baseGetAllKeys(object, keys, getSymbols);
+ function parent(object, path) {
+ return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
}
/**
- * Creates an array of own and inherited enumerable property names and
- * symbols of `object`.
+ * Reorder `array` according to the specified indexes where the element at
+ * the first index is assigned as the first element, the element at
+ * the second index is assigned as the second element, and so on.
*
* @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names and symbols.
+ * @param {Array} array The array to reorder.
+ * @param {Array} indexes The arranged array indexes.
+ * @returns {Array} Returns `array`.
*/
- function getAllKeysIn(object) {
- return baseGetAllKeys(object, keysIn, getSymbolsIn);
+ function reorder(array, indexes) {
+ var arrLength = array.length,
+ length = nativeMin(indexes.length, arrLength),
+ oldArray = copyArray(array);
+
+ while (length--) {
+ var index = indexes[length];
+ array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
+ }
+ return array;
}
/**
- * Gets metadata for `func`.
+ * Sets metadata for `func`.
+ *
+ * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
+ * period of time, it will trip its breaker and transition to an identity
+ * function to avoid garbage collection pauses in V8. See
+ * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
+ * for more details.
*
* @private
- * @param {Function} func The function to query.
- * @returns {*} Returns the metadata for `func`.
+ * @param {Function} func The function to associate metadata with.
+ * @param {*} data The metadata.
+ * @returns {Function} Returns `func`.
*/
- var getData = !metaMap ? noop : function(func) {
- return metaMap.get(func);
+ var setData = shortOut(baseSetData);
+
+ /**
+ * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).
+ *
+ * @private
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @returns {number|Object} Returns the timer id or timeout object.
+ */
+ var setTimeout = ctxSetTimeout || function(func, wait) {
+ return root.setTimeout(func, wait);
};
/**
- * Gets the name of `func`.
+ * Sets the `toString` method of `func` to return `string`.
*
* @private
- * @param {Function} func The function to query.
- * @returns {string} Returns the function name.
+ * @param {Function} func The function to modify.
+ * @param {Function} string The `toString` result.
+ * @returns {Function} Returns `func`.
*/
- function getFuncName(func) {
- var result = (func.name + ''),
- array = realNames[result],
- length = hasOwnProperty.call(realNames, result) ? array.length : 0;
+ var setToString = shortOut(baseSetToString);
- while (length--) {
- var data = array[length],
- otherFunc = data.func;
- if (otherFunc == null || otherFunc == func) {
- return data.name;
+ /**
+ * Sets the `toString` method of `wrapper` to mimic the source of `reference`
+ * with wrapper details in a comment at the top of the source body.
+ *
+ * @private
+ * @param {Function} wrapper The function to modify.
+ * @param {Function} reference The reference function.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @returns {Function} Returns `wrapper`.
+ */
+ function setWrapToString(wrapper, reference, bitmask) {
+ var source = (reference + '');
+ return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
+ }
+
+ /**
+ * Creates a function that'll short out and invoke `identity` instead
+ * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
+ * milliseconds.
+ *
+ * @private
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new shortable function.
+ */
+ function shortOut(func) {
+ var count = 0,
+ lastCalled = 0;
+
+ return function() {
+ var stamp = nativeNow(),
+ remaining = HOT_SPAN - (stamp - lastCalled);
+
+ lastCalled = stamp;
+ if (remaining > 0) {
+ if (++count >= HOT_COUNT) {
+ return arguments[0];
+ }
+ } else {
+ count = 0;
}
+ return func.apply(undefined, arguments);
+ };
+ }
+
+ /**
+ * A specialized version of `_.shuffle` which mutates and sets the size of `array`.
+ *
+ * @private
+ * @param {Array} array The array to shuffle.
+ * @param {number} [size=array.length] The size of `array`.
+ * @returns {Array} Returns `array`.
+ */
+ function shuffleSelf(array, size) {
+ var index = -1,
+ length = array.length,
+ lastIndex = length - 1;
+
+ size = size === undefined ? length : size;
+ while (++index < size) {
+ var rand = baseRandom(index, lastIndex),
+ value = array[rand];
+
+ array[rand] = array[index];
+ array[index] = value;
}
- return result;
+ array.length = size;
+ return array;
}
/**
- * Gets the argument placeholder value for `func`.
+ * Converts `string` to a property path array.
*
* @private
- * @param {Function} func The function to inspect.
- * @returns {*} Returns the placeholder value.
+ * @param {string} string The string to convert.
+ * @returns {Array} Returns the property path array.
*/
- function getHolder(func) {
- var object = hasOwnProperty.call(lodash, 'placeholder') ? lodash : func;
- return object.placeholder;
+ var stringToPath = memoizeCapped(function(string) {
+ var result = [];
+ if (reLeadingDot.test(string)) {
+ result.push('');
+ }
+ string.replace(rePropName, function(match, number, quote, string) {
+ result.push(quote ? string.replace(reEscapeChar, '$1') : (number || match));
+ });
+ return result;
+ });
+
+ /**
+ * Converts `value` to a string key if it's not a string or symbol.
+ *
+ * @private
+ * @param {*} value The value to inspect.
+ * @returns {string|symbol} Returns the key.
+ */
+ function toKey(value) {
+ if (typeof value == 'string' || isSymbol(value)) {
+ return value;
+ }
+ var result = (value + '');
+ return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}
/**
- * Gets the appropriate "iteratee" function. If `_.iteratee` is customized,
- * this function returns the custom method, otherwise it returns `baseIteratee`.
- * If arguments are provided, the chosen function is invoked with them and
- * its result is returned.
+ * Converts `func` to its source code.
*
* @private
- * @param {*} [value] The value to convert to an iteratee.
- * @param {number} [arity] The arity of the created iteratee.
- * @returns {Function} Returns the chosen function or its result.
+ * @param {Function} func The function to convert.
+ * @returns {string} Returns the source code.
*/
- function getIteratee() {
- var result = lodash.iteratee || iteratee;
- result = result === iteratee ? baseIteratee : result;
- return arguments.length ? result(arguments[0], arguments[1]) : result;
+ function toSource(func) {
+ if (func != null) {
+ try {
+ return funcToString.call(func);
+ } catch (e) {}
+ try {
+ return (func + '');
+ } catch (e) {}
+ }
+ return '';
}
/**
- * Gets the data for `map`.
+ * Updates wrapper `details` based on `bitmask` flags.
*
* @private
- * @param {Object} map The map to query.
- * @param {string} key The reference key.
- * @returns {*} Returns the map data.
+ * @returns {Array} details The details to modify.
+ * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
+ * @returns {Array} Returns `details`.
*/
- function getMapData(map, key) {
- var data = map.__data__;
- return isKeyable(key)
- ? data[typeof key == 'string' ? 'string' : 'hash']
- : data.map;
+ function updateWrapDetails(details, bitmask) {
+ arrayEach(wrapFlags, function(pair) {
+ var value = '_.' + pair[0];
+ if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {
+ details.push(value);
+ }
+ });
+ return details.sort();
}
/**
- * Gets the property names, values, and compare flags of `object`.
+ * Creates a clone of `wrapper`.
*
* @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the match data of `object`.
+ * @param {Object} wrapper The wrapper to clone.
+ * @returns {Object} Returns the cloned wrapper.
*/
- function getMatchData(object) {
- var result = keys(object),
- length = result.length;
-
- while (length--) {
- var key = result[length],
- value = object[key];
-
- result[length] = [key, value, isStrictComparable(value)];
+ function wrapperClone(wrapper) {
+ if (wrapper instanceof LazyWrapper) {
+ return wrapper.clone();
}
+ var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
+ result.__actions__ = copyArray(wrapper.__actions__);
+ result.__index__ = wrapper.__index__;
+ result.__values__ = wrapper.__values__;
return result;
}
+ /*------------------------------------------------------------------------*/
+
/**
- * Gets the native function at `key` of `object`.
+ * Creates an array of elements split into groups the length of `size`.
+ * If `array` can't be split evenly, the final chunk will be the remaining
+ * elements.
*
- * @private
- * @param {Object} object The object to query.
- * @param {string} key The key of the method to get.
- * @returns {*} Returns the function if it's native, else `undefined`.
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to process.
+ * @param {number} [size=1] The length of each chunk
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the new array of chunks.
+ * @example
+ *
+ * _.chunk(['a', 'b', 'c', 'd'], 2);
+ * // => [['a', 'b'], ['c', 'd']]
+ *
+ * _.chunk(['a', 'b', 'c', 'd'], 3);
+ * // => [['a', 'b', 'c'], ['d']]
*/
- function getNative(object, key) {
- var value = getValue(object, key);
- return baseIsNative(value) ? value : undefined;
+ function chunk(array, size, guard) {
+ if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
+ size = 1;
+ } else {
+ size = nativeMax(toInteger(size), 0);
+ }
+ var length = array == null ? 0 : array.length;
+ if (!length || size < 1) {
+ return [];
+ }
+ var index = 0,
+ resIndex = 0,
+ result = Array(nativeCeil(length / size));
+
+ while (index < length) {
+ result[resIndex++] = baseSlice(array, index, (index += size));
+ }
+ return result;
}
/**
- * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+ * Creates an array with all falsey values removed. The values `false`, `null`,
+ * `0`, `""`, `undefined`, and `NaN` are falsey.
*
- * @private
- * @param {*} value The value to query.
- * @returns {string} Returns the raw `toStringTag`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to compact.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.compact([0, 1, false, 2, '', 3]);
+ * // => [1, 2, 3]
*/
- function getRawTag(value) {
- var isOwn = hasOwnProperty.call(value, symToStringTag),
- tag = value[symToStringTag];
-
- try {
- value[symToStringTag] = undefined;
- var unmasked = true;
- } catch (e) {}
+ function compact(array) {
+ var index = -1,
+ length = array == null ? 0 : array.length,
+ resIndex = 0,
+ result = [];
- var result = nativeObjectToString.call(value);
- if (unmasked) {
- if (isOwn) {
- value[symToStringTag] = tag;
- } else {
- delete value[symToStringTag];
+ while (++index < length) {
+ var value = array[index];
+ if (value) {
+ result[resIndex++] = value;
}
}
return result;
}
/**
- * Creates an array of the own enumerable symbols of `object`.
+ * Creates a new array concatenating `array` with any additional arrays
+ * and/or values.
*
- * @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of symbols.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to concatenate.
+ * @param {...*} [values] The values to concatenate.
+ * @returns {Array} Returns the new concatenated array.
+ * @example
+ *
+ * var array = [1];
+ * var other = _.concat(array, 2, [3], [[4]]);
+ *
+ * console.log(other);
+ * // => [1, 2, 3, [4]]
+ *
+ * console.log(array);
+ * // => [1]
*/
- var getSymbols = !nativeGetSymbols ? stubArray : function(object) {
- if (object == null) {
+ function concat() {
+ var length = arguments.length;
+ if (!length) {
return [];
}
- object = Object(object);
- return arrayFilter(nativeGetSymbols(object), function(symbol) {
- return propertyIsEnumerable.call(object, symbol);
- });
- };
+ var args = Array(length - 1),
+ array = arguments[0],
+ index = length;
+
+ while (index--) {
+ args[index - 1] = arguments[index];
+ }
+ return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
+ }
/**
- * Creates an array of the own and inherited enumerable symbols of `object`.
+ * Creates an array of `array` values not included in the other given arrays
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. The order and references of result values are
+ * determined by the first array.
+ *
+ * **Note:** Unlike `_.pullAll`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.without, _.xor
+ * @example
+ *
+ * _.difference([2, 1], [2, 3]);
+ * // => [1]
+ */
+ var difference = baseRest(function(array, values) {
+ return isArrayLikeObject(array)
+ ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
+ : [];
+ });
+
+ /**
+ * This method is like `_.difference` except that it accepts `iteratee` which
+ * is invoked for each element of `array` and `values` to generate the criterion
+ * by which they're compared. The order and references of result values are
+ * determined by the first array. The iteratee is invoked with one argument:
+ * (value).
+ *
+ * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
+ *
+ * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+ * // => [1.2]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
+ * // => [{ 'x': 2 }]
+ */
+ var differenceBy = baseRest(function(array, values) {
+ var iteratee = last(values);
+ if (isArrayLikeObject(iteratee)) {
+ iteratee = undefined;
+ }
+ return isArrayLikeObject(array)
+ ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2))
+ : [];
+ });
+
+ /**
+ * This method is like `_.difference` except that it accepts `comparator`
+ * which is invoked to compare elements of `array` to `values`. The order and
+ * references of result values are determined by the first array. The comparator
+ * is invoked with two arguments: (arrVal, othVal).
+ *
+ * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {...Array} [values] The values to exclude.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
+ * @example
*
- * @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of symbols.
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ *
+ * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
+ * // => [{ 'x': 2, 'y': 1 }]
*/
- var getSymbolsIn = !nativeGetSymbols ? stubArray : function(object) {
- var result = [];
- while (object) {
- arrayPush(result, getSymbols(object));
- object = getPrototype(object);
+ var differenceWith = baseRest(function(array, values) {
+ var comparator = last(values);
+ if (isArrayLikeObject(comparator)) {
+ comparator = undefined;
}
- return result;
- };
+ return isArrayLikeObject(array)
+ ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)
+ : [];
+ });
/**
- * Gets the `toStringTag` of `value`.
+ * Creates a slice of `array` with `n` elements dropped from the beginning.
*
- * @private
- * @param {*} value The value to query.
- * @returns {string} Returns the `toStringTag`.
+ * @static
+ * @memberOf _
+ * @since 0.5.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.drop([1, 2, 3]);
+ * // => [2, 3]
+ *
+ * _.drop([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.drop([1, 2, 3], 5);
+ * // => []
+ *
+ * _.drop([1, 2, 3], 0);
+ * // => [1, 2, 3]
*/
- var getTag = baseGetTag;
-
- // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
- if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
- (Map && getTag(new Map) != mapTag) ||
- (Promise && getTag(Promise.resolve()) != promiseTag) ||
- (Set && getTag(new Set) != setTag) ||
- (WeakMap && getTag(new WeakMap) != weakMapTag)) {
- getTag = function(value) {
- var result = baseGetTag(value),
- Ctor = result == objectTag ? value.constructor : undefined,
- ctorString = Ctor ? toSource(Ctor) : '';
-
- if (ctorString) {
- switch (ctorString) {
- case dataViewCtorString: return dataViewTag;
- case mapCtorString: return mapTag;
- case promiseCtorString: return promiseTag;
- case setCtorString: return setTag;
- case weakMapCtorString: return weakMapTag;
- }
- }
- return result;
- };
+ function drop(array, n, guard) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
+ }
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ return baseSlice(array, n < 0 ? 0 : n, length);
}
/**
- * Gets the view, applying any `transforms` to the `start` and `end` positions.
+ * Creates a slice of `array` with `n` elements dropped from the end.
*
- * @private
- * @param {number} start The start of the view.
- * @param {number} end The end of the view.
- * @param {Array} transforms The transformations to apply to the view.
- * @returns {Object} Returns an object containing the `start` and `end`
- * positions of the view.
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropRight([1, 2, 3]);
+ * // => [1, 2]
+ *
+ * _.dropRight([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.dropRight([1, 2, 3], 5);
+ * // => []
+ *
+ * _.dropRight([1, 2, 3], 0);
+ * // => [1, 2, 3]
*/
- function getView(start, end, transforms) {
- var index = -1,
- length = transforms.length;
-
- while (++index < length) {
- var data = transforms[index],
- size = data.size;
-
- switch (data.type) {
- case 'drop': start += size; break;
- case 'dropRight': end -= size; break;
- case 'take': end = nativeMin(end, start + size); break;
- case 'takeRight': start = nativeMax(start, end - size); break;
- }
+ function dropRight(array, n, guard) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
}
- return { 'start': start, 'end': end };
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ n = length - n;
+ return baseSlice(array, 0, n < 0 ? 0 : n);
}
/**
- * Extracts wrapper details from the `source` body comment.
+ * Creates a slice of `array` excluding elements dropped from the end.
+ * Elements are dropped until `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index, array).
*
- * @private
- * @param {string} source The source to inspect.
- * @returns {Array} Returns the wrapper details.
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.dropRightWhile(users, function(o) { return !o.active; });
+ * // => objects for ['barney']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
+ * // => objects for ['barney', 'fred']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.dropRightWhile(users, ['active', false]);
+ * // => objects for ['barney']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.dropRightWhile(users, 'active');
+ * // => objects for ['barney', 'fred', 'pebbles']
*/
- function getWrapDetails(source) {
- var match = source.match(reWrapDetails);
- return match ? match[1].split(reSplitDetails) : [];
+ function dropRightWhile(array, predicate) {
+ return (array && array.length)
+ ? baseWhile(array, getIteratee(predicate, 3), true, true)
+ : [];
}
/**
- * Checks if `path` exists on `object`.
+ * Creates a slice of `array` excluding elements dropped from the beginning.
+ * Elements are dropped until `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index, array).
*
- * @private
- * @param {Object} object The object to query.
- * @param {Array|string} path The path to check.
- * @param {Function} hasFunc The function to check properties.
- * @returns {boolean} Returns `true` if `path` exists, else `false`.
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
+ *
+ * _.dropWhile(users, function(o) { return !o.active; });
+ * // => objects for ['pebbles']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.dropWhile(users, { 'user': 'barney', 'active': false });
+ * // => objects for ['fred', 'pebbles']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.dropWhile(users, ['active', false]);
+ * // => objects for ['pebbles']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.dropWhile(users, 'active');
+ * // => objects for ['barney', 'fred', 'pebbles']
*/
- function hasPath(object, path, hasFunc) {
- path = castPath(path, object);
-
- var index = -1,
- length = path.length,
- result = false;
-
- while (++index < length) {
- var key = toKey(path[index]);
- if (!(result = object != null && hasFunc(object, key))) {
- break;
- }
- object = object[key];
- }
- if (result || ++index != length) {
- return result;
- }
- length = object == null ? 0 : object.length;
- return !!length && isLength(length) && isIndex(key, length) &&
- (isArray(object) || isArguments(object));
+ function dropWhile(array, predicate) {
+ return (array && array.length)
+ ? baseWhile(array, getIteratee(predicate, 3), true)
+ : [];
}
/**
- * Initializes an array clone.
+ * Fills elements of `array` with `value` from `start` up to, but not
+ * including, `end`.
+ *
+ * **Note:** This method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.2.0
+ * @category Array
+ * @param {Array} array The array to fill.
+ * @param {*} value The value to fill `array` with.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3];
*
- * @private
- * @param {Array} array The array to clone.
- * @returns {Array} Returns the initialized clone.
+ * _.fill(array, 'a');
+ * console.log(array);
+ * // => ['a', 'a', 'a']
+ *
+ * _.fill(Array(3), 2);
+ * // => [2, 2, 2]
+ *
+ * _.fill([4, 6, 8, 10], '*', 1, 3);
+ * // => [4, '*', '*', 10]
*/
- function initCloneArray(array) {
- var length = array.length,
- result = new array.constructor(length);
-
- // Add properties assigned by `RegExp#exec`.
- if (length && typeof array[0] == 'string' && hasOwnProperty.call(array, 'index')) {
- result.index = array.index;
- result.input = array.input;
+ function fill(array, value, start, end) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
}
- return result;
+ if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
+ start = 0;
+ end = length;
+ }
+ return baseFill(array, value, start, end);
}
/**
- * Initializes an object clone.
+ * This method is like `_.find` except that it returns the index of the first
+ * element `predicate` returns truthy for instead of the element itself.
*
- * @private
- * @param {Object} object The object to clone.
- * @returns {Object} Returns the initialized clone.
- */
- function initCloneObject(object) {
- return (typeof object.constructor == 'function' && !isPrototype(object))
- ? baseCreate(getPrototype(object))
- : {};
- }
-
- /**
- * Initializes an object clone based on its `toStringTag`.
+ * @static
+ * @memberOf _
+ * @since 1.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
*
- * **Note:** This function only supports cloning values with tags of
- * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
*
- * @private
- * @param {Object} object The object to clone.
- * @param {string} tag The `toStringTag` of the object to clone.
- * @param {boolean} [isDeep] Specify a deep clone.
- * @returns {Object} Returns the initialized clone.
+ * _.findIndex(users, function(o) { return o.user == 'barney'; });
+ * // => 0
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findIndex(users, { 'user': 'fred', 'active': false });
+ * // => 1
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findIndex(users, ['active', false]);
+ * // => 0
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findIndex(users, 'active');
+ * // => 2
*/
- function initCloneByTag(object, tag, isDeep) {
- var Ctor = object.constructor;
- switch (tag) {
- case arrayBufferTag:
- return cloneArrayBuffer(object);
-
- case boolTag:
- case dateTag:
- return new Ctor(+object);
-
- case dataViewTag:
- return cloneDataView(object, isDeep);
-
- case float32Tag: case float64Tag:
- case int8Tag: case int16Tag: case int32Tag:
- case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag:
- return cloneTypedArray(object, isDeep);
-
- case mapTag:
- return new Ctor;
-
- case numberTag:
- case stringTag:
- return new Ctor(object);
-
- case regexpTag:
- return cloneRegExp(object);
-
- case setTag:
- return new Ctor;
-
- case symbolTag:
- return cloneSymbol(object);
+ function findIndex(array, predicate, fromIndex) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return -1;
+ }
+ var index = fromIndex == null ? 0 : toInteger(fromIndex);
+ if (index < 0) {
+ index = nativeMax(length + index, 0);
}
+ return baseFindIndex(array, getIteratee(predicate, 3), index);
}
/**
- * Inserts wrapper `details` in a comment at the top of the `source` body.
+ * This method is like `_.findIndex` except that it iterates over elements
+ * of `collection` from right to left.
*
- * @private
- * @param {string} source The source to modify.
- * @returns {Array} details The details to insert.
- * @returns {string} Returns the modified source.
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the found element, else `-1`.
+ * @example
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
+ * // => 2
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.findLastIndex(users, { 'user': 'barney', 'active': true });
+ * // => 0
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findLastIndex(users, ['active', false]);
+ * // => 2
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findLastIndex(users, 'active');
+ * // => 0
*/
- function insertWrapDetails(source, details) {
- var length = details.length;
+ function findLastIndex(array, predicate, fromIndex) {
+ var length = array == null ? 0 : array.length;
if (!length) {
- return source;
+ return -1;
}
- var lastIndex = length - 1;
- details[lastIndex] = (length > 1 ? '& ' : '') + details[lastIndex];
- details = details.join(length > 2 ? ', ' : ' ');
- return source.replace(reWrapComment, '{\n/* [wrapped with ' + details + '] */\n');
+ var index = length - 1;
+ if (fromIndex !== undefined) {
+ index = toInteger(fromIndex);
+ index = fromIndex < 0
+ ? nativeMax(length + index, 0)
+ : nativeMin(index, length - 1);
+ }
+ return baseFindIndex(array, getIteratee(predicate, 3), index, true);
}
/**
- * Checks if `value` is a flattenable `arguments` object or array.
+ * Flattens `array` a single level deep.
*
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flatten([1, [2, [3, [4]], 5]]);
+ * // => [1, 2, [3, [4]], 5]
*/
- function isFlattenable(value) {
- return isArray(value) || isArguments(value) ||
- !!(spreadableSymbol && value && value[spreadableSymbol]);
+ function flatten(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseFlatten(array, 1) : [];
}
/**
- * Checks if `value` is a valid array-like index.
+ * Recursively flattens `array`.
*
- * @private
- * @param {*} value The value to check.
- * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
- * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * _.flattenDeep([1, [2, [3, [4]], 5]]);
+ * // => [1, 2, 3, 4, 5]
*/
- function isIndex(value, length) {
- var type = typeof value;
- length = length == null ? MAX_SAFE_INTEGER : length;
-
- return !!length &&
- (type == 'number' ||
- (type != 'symbol' && reIsUint.test(value))) &&
- (value > -1 && value % 1 == 0 && value < length);
+ function flattenDeep(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseFlatten(array, INFINITY) : [];
}
/**
- * Checks if the given arguments are from an iteratee call.
+ * Recursively flatten `array` up to `depth` times.
*
- * @private
- * @param {*} value The potential iteratee value argument.
- * @param {*} index The potential iteratee index or key argument.
- * @param {*} object The potential iteratee object argument.
- * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
- * else `false`.
+ * @static
+ * @memberOf _
+ * @since 4.4.0
+ * @category Array
+ * @param {Array} array The array to flatten.
+ * @param {number} [depth=1] The maximum recursion depth.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * var array = [1, [2, [3, [4]], 5]];
+ *
+ * _.flattenDepth(array, 1);
+ * // => [1, 2, [3, [4]], 5]
+ *
+ * _.flattenDepth(array, 2);
+ * // => [1, 2, 3, [4], 5]
*/
- function isIterateeCall(value, index, object) {
- if (!isObject(object)) {
- return false;
- }
- var type = typeof index;
- if (type == 'number'
- ? (isArrayLike(object) && isIndex(index, object.length))
- : (type == 'string' && index in object)
- ) {
- return eq(object[index], value);
+ function flattenDepth(array, depth) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
}
- return false;
+ depth = depth === undefined ? 1 : toInteger(depth);
+ return baseFlatten(array, depth);
}
/**
- * Checks if `value` is a property name and not a property path.
+ * The inverse of `_.toPairs`; this method returns an object composed
+ * from key-value `pairs`.
*
- * @private
- * @param {*} value The value to check.
- * @param {Object} [object] The object to query keys on.
- * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} pairs The key-value pairs.
+ * @returns {Object} Returns the new object.
+ * @example
+ *
+ * _.fromPairs([['a', 1], ['b', 2]]);
+ * // => { 'a': 1, 'b': 2 }
*/
- function isKey(value, object) {
- if (isArray(value)) {
- return false;
- }
- var type = typeof value;
- if (type == 'number' || type == 'symbol' || type == 'boolean' ||
- value == null || isSymbol(value)) {
- return true;
+ function fromPairs(pairs) {
+ var index = -1,
+ length = pairs == null ? 0 : pairs.length,
+ result = {};
+
+ while (++index < length) {
+ var pair = pairs[index];
+ result[pair[0]] = pair[1];
}
- return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
- (object != null && value in Object(object));
+ return result;
}
/**
- * Checks if `value` is suitable for use as unique object key.
+ * Gets the first element of `array`.
*
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @alias first
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the first element of `array`.
+ * @example
+ *
+ * _.head([1, 2, 3]);
+ * // => 1
+ *
+ * _.head([]);
+ * // => undefined
*/
- function isKeyable(value) {
- var type = typeof value;
- return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
- ? (value !== '__proto__')
- : (value === null);
+ function head(array) {
+ return (array && array.length) ? array[0] : undefined;
}
/**
- * Checks if `func` has a lazy counterpart.
+ * Gets the index at which the first occurrence of `value` is found in `array`
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. If `fromIndex` is negative, it's used as the
+ * offset from the end of `array`.
*
- * @private
- * @param {Function} func The function to check.
- * @returns {boolean} Returns `true` if `func` has a lazy counterpart,
- * else `false`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.indexOf([1, 2, 1, 2], 2);
+ * // => 1
+ *
+ * // Search from the `fromIndex`.
+ * _.indexOf([1, 2, 1, 2], 2, 2);
+ * // => 3
*/
- function isLaziable(func) {
- var funcName = getFuncName(func),
- other = lodash[funcName];
-
- if (typeof other != 'function' || !(funcName in LazyWrapper.prototype)) {
- return false;
+ function indexOf(array, value, fromIndex) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return -1;
}
- if (func === other) {
- return true;
+ var index = fromIndex == null ? 0 : toInteger(fromIndex);
+ if (index < 0) {
+ index = nativeMax(length + index, 0);
}
- var data = getData(other);
- return !!data && func === data[0];
+ return baseIndexOf(array, value, index);
}
/**
- * Checks if `func` has its source masked.
+ * Gets all but the last element of `array`.
*
- * @private
- * @param {Function} func The function to check.
- * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.initial([1, 2, 3]);
+ * // => [1, 2]
*/
- function isMasked(func) {
- return !!maskSrcKey && (maskSrcKey in func);
+ function initial(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseSlice(array, 0, -1) : [];
}
/**
- * Checks if `func` is capable of being masked.
+ * Creates an array of unique values that are included in all given arrays
+ * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons. The order and references of result values are
+ * determined by the first array.
*
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `func` is maskable, else `false`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * _.intersection([2, 1], [2, 3]);
+ * // => [2]
*/
- var isMaskable = coreJsData ? isFunction : stubFalse;
+ var intersection = baseRest(function(arrays) {
+ var mapped = arrayMap(arrays, castArrayLikeObject);
+ return (mapped.length && mapped[0] === arrays[0])
+ ? baseIntersection(mapped)
+ : [];
+ });
/**
- * Checks if `value` is likely a prototype object.
+ * This method is like `_.intersection` except that it accepts `iteratee`
+ * which is invoked for each element of each `arrays` to generate the criterion
+ * by which they're compared. The order and references of result values are
+ * determined by the first array. The iteratee is invoked with one argument:
+ * (value).
*
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+ * // => [2.1]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }]
*/
- function isPrototype(value) {
- var Ctor = value && value.constructor,
- proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
+ var intersectionBy = baseRest(function(arrays) {
+ var iteratee = last(arrays),
+ mapped = arrayMap(arrays, castArrayLikeObject);
- return value === proto;
- }
+ if (iteratee === last(mapped)) {
+ iteratee = undefined;
+ } else {
+ mapped.pop();
+ }
+ return (mapped.length && mapped[0] === arrays[0])
+ ? baseIntersection(mapped, getIteratee(iteratee, 2))
+ : [];
+ });
/**
- * Checks if `value` is suitable for strict equality comparisons, i.e. `===`.
+ * This method is like `_.intersection` except that it accepts `comparator`
+ * which is invoked to compare elements of `arrays`. The order and references
+ * of result values are determined by the first array. The comparator is
+ * invoked with two arguments: (arrVal, othVal).
*
- * @private
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` if suitable for strict
- * equality comparisons, else `false`.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of intersecting values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.intersectionWith(objects, others, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }]
*/
- function isStrictComparable(value) {
- return value === value && !isObject(value);
- }
+ var intersectionWith = baseRest(function(arrays) {
+ var comparator = last(arrays),
+ mapped = arrayMap(arrays, castArrayLikeObject);
+
+ comparator = typeof comparator == 'function' ? comparator : undefined;
+ if (comparator) {
+ mapped.pop();
+ }
+ return (mapped.length && mapped[0] === arrays[0])
+ ? baseIntersection(mapped, undefined, comparator)
+ : [];
+ });
/**
- * A specialized version of `matchesProperty` for source values suitable
- * for strict equality comparisons, i.e. `===`.
+ * Converts all elements in `array` into a string separated by `separator`.
*
- * @private
- * @param {string} key The key of the property to get.
- * @param {*} srcValue The value to match.
- * @returns {Function} Returns the new spec function.
- */
- function matchesStrictComparable(key, srcValue) {
- return function(object) {
- if (object == null) {
- return false;
- }
- return object[key] === srcValue &&
- (srcValue !== undefined || (key in Object(object)));
- };
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to convert.
+ * @param {string} [separator=','] The element separator.
+ * @returns {string} Returns the joined string.
+ * @example
+ *
+ * _.join(['a', 'b', 'c'], '~');
+ * // => 'a~b~c'
+ */
+ function join(array, separator) {
+ return array == null ? '' : nativeJoin.call(array, separator);
}
/**
- * A specialized version of `_.memoize` which clears the memoized function's
- * cache when it exceeds `MAX_MEMOIZE_SIZE`.
+ * Gets the last element of `array`.
*
- * @private
- * @param {Function} func The function to have its output memoized.
- * @returns {Function} Returns the new memoized function.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {*} Returns the last element of `array`.
+ * @example
+ *
+ * _.last([1, 2, 3]);
+ * // => 3
*/
- function memoizeCapped(func) {
- var result = memoize(func, function(key) {
- if (cache.size === MAX_MEMOIZE_SIZE) {
- cache.clear();
- }
- return key;
- });
-
- var cache = result.cache;
- return result;
+ function last(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? array[length - 1] : undefined;
}
/**
- * Merges the function metadata of `source` into `data`.
+ * This method is like `_.indexOf` except that it iterates over elements of
+ * `array` from right to left.
*
- * Merging metadata reduces the number of wrappers used to invoke a function.
- * This is possible because methods like `_.bind`, `_.curry`, and `_.partial`
- * may be applied regardless of execution order. Methods like `_.ary` and
- * `_.rearg` modify function arguments, making the order in which they are
- * executed important, preventing the merging of metadata. However, we make
- * an exception for a safe combined case where curried functions have `_.ary`
- * and or `_.rearg` applied.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=array.length-1] The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
*
- * @private
- * @param {Array} data The destination metadata.
- * @param {Array} source The source metadata.
- * @returns {Array} Returns `data`.
+ * _.lastIndexOf([1, 2, 1, 2], 2);
+ * // => 3
+ *
+ * // Search from the `fromIndex`.
+ * _.lastIndexOf([1, 2, 1, 2], 2, 2);
+ * // => 1
*/
- function mergeData(data, source) {
- var bitmask = data[1],
- srcBitmask = source[1],
- newBitmask = bitmask | srcBitmask,
- isCommon = newBitmask < (WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG | WRAP_ARY_FLAG);
-
- var isCombo =
- ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_CURRY_FLAG)) ||
- ((srcBitmask == WRAP_ARY_FLAG) && (bitmask == WRAP_REARG_FLAG) && (data[7].length <= source[8])) ||
- ((srcBitmask == (WRAP_ARY_FLAG | WRAP_REARG_FLAG)) && (source[7].length <= source[8]) && (bitmask == WRAP_CURRY_FLAG));
-
- // Exit early if metadata can't be merged.
- if (!(isCommon || isCombo)) {
- return data;
- }
- // Use source `thisArg` if available.
- if (srcBitmask & WRAP_BIND_FLAG) {
- data[2] = source[2];
- // Set when currying a bound function.
- newBitmask |= bitmask & WRAP_BIND_FLAG ? 0 : WRAP_CURRY_BOUND_FLAG;
- }
- // Compose partial arguments.
- var value = source[3];
- if (value) {
- var partials = data[3];
- data[3] = partials ? composeArgs(partials, value, source[4]) : value;
- data[4] = partials ? replaceHolders(data[3], PLACEHOLDER) : source[4];
- }
- // Compose partial right arguments.
- value = source[5];
- if (value) {
- partials = data[5];
- data[5] = partials ? composeArgsRight(partials, value, source[6]) : value;
- data[6] = partials ? replaceHolders(data[5], PLACEHOLDER) : source[6];
- }
- // Use source `argPos` if available.
- value = source[7];
- if (value) {
- data[7] = value;
- }
- // Use source `ary` if it's smaller.
- if (srcBitmask & WRAP_ARY_FLAG) {
- data[8] = data[8] == null ? source[8] : nativeMin(data[8], source[8]);
+ function lastIndexOf(array, value, fromIndex) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return -1;
}
- // Use source `arity` if one is not provided.
- if (data[9] == null) {
- data[9] = source[9];
+ var index = length;
+ if (fromIndex !== undefined) {
+ index = toInteger(fromIndex);
+ index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
}
- // Use source `func` and merge bitmasks.
- data[0] = source[0];
- data[1] = newBitmask;
-
- return data;
+ return value === value
+ ? strictLastIndexOf(array, value, index)
+ : baseFindIndex(array, baseIsNaN, index, true);
}
/**
- * This function is like
- * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
- * except that it includes inherited enumerable properties.
+ * Gets the element at index `n` of `array`. If `n` is negative, the nth
+ * element from the end is returned.
*
- * @private
- * @param {Object} object The object to query.
- * @returns {Array} Returns the array of property names.
+ * @static
+ * @memberOf _
+ * @since 4.11.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=0] The index of the element to return.
+ * @returns {*} Returns the nth element of `array`.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'd'];
+ *
+ * _.nth(array, 1);
+ * // => 'b'
+ *
+ * _.nth(array, -2);
+ * // => 'c';
*/
- function nativeKeysIn(object) {
- var result = [];
- if (object != null) {
- for (var key in Object(object)) {
- result.push(key);
- }
- }
- return result;
+ function nth(array, n) {
+ return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;
}
/**
- * Converts `value` to a string using `Object.prototype.toString`.
+ * Removes all given values from `array` using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
*
- * @private
- * @param {*} value The value to convert.
- * @returns {string} Returns the converted string.
+ * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
+ * to remove elements from an array by predicate.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {...*} [values] The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+ *
+ * _.pull(array, 'a', 'c');
+ * console.log(array);
+ * // => ['b', 'b']
*/
- function objectToString(value) {
- return nativeObjectToString.call(value);
- }
+ var pull = baseRest(pullAll);
/**
- * A specialized version of `baseRest` which transforms the rest array.
+ * This method is like `_.pull` except that it accepts an array of values to remove.
*
- * @private
- * @param {Function} func The function to apply a rest parameter to.
- * @param {number} [start=func.length-1] The start position of the rest parameter.
- * @param {Function} transform The rest array transform.
- * @returns {Function} Returns the new function.
+ * **Note:** Unlike `_.difference`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+ *
+ * _.pullAll(array, ['a', 'c']);
+ * console.log(array);
+ * // => ['b', 'b']
*/
- function overRest(func, start, transform) {
- start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
- return function() {
- var args = arguments,
- index = -1,
- length = nativeMax(args.length - start, 0),
- array = Array(length);
+ function pullAll(array, values) {
+ return (array && array.length && values && values.length)
+ ? basePullAll(array, values)
+ : array;
+ }
- while (++index < length) {
- array[index] = args[start + index];
- }
- index = -1;
- var otherArgs = Array(start + 1);
- while (++index < start) {
- otherArgs[index] = args[index];
- }
- otherArgs[start] = transform(array);
- return apply(func, this, otherArgs);
- };
+ /**
+ * This method is like `_.pullAll` except that it accepts `iteratee` which is
+ * invoked for each element of `array` and `values` to generate the criterion
+ * by which they're compared. The iteratee is invoked with one argument: (value).
+ *
+ * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
+ *
+ * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
+ * console.log(array);
+ * // => [{ 'x': 2 }]
+ */
+ function pullAllBy(array, values, iteratee) {
+ return (array && array.length && values && values.length)
+ ? basePullAll(array, values, getIteratee(iteratee, 2))
+ : array;
}
/**
- * Gets the parent value at `path` of `object`.
+ * This method is like `_.pullAll` except that it accepts `comparator` which
+ * is invoked to compare elements of `array` to `values`. The comparator is
+ * invoked with two arguments: (arrVal, othVal).
*
- * @private
- * @param {Object} object The object to query.
- * @param {Array} path The path to get the parent value of.
- * @returns {*} Returns the parent value.
+ * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.6.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Array} values The values to remove.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
+ *
+ * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
+ * console.log(array);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
*/
- function parent(object, path) {
- return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
+ function pullAllWith(array, values, comparator) {
+ return (array && array.length && values && values.length)
+ ? basePullAll(array, values, undefined, comparator)
+ : array;
}
/**
- * Reorder `array` according to the specified indexes where the element at
- * the first index is assigned as the first element, the element at
- * the second index is assigned as the second element, and so on.
+ * Removes elements from `array` corresponding to `indexes` and returns an
+ * array of removed elements.
*
- * @private
- * @param {Array} array The array to reorder.
- * @param {Array} indexes The arranged array indexes.
- * @returns {Array} Returns `array`.
+ * **Note:** Unlike `_.at`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {...(number|number[])} [indexes] The indexes of elements to remove.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = ['a', 'b', 'c', 'd'];
+ * var pulled = _.pullAt(array, [1, 3]);
+ *
+ * console.log(array);
+ * // => ['a', 'c']
+ *
+ * console.log(pulled);
+ * // => ['b', 'd']
*/
- function reorder(array, indexes) {
- var arrLength = array.length,
- length = nativeMin(indexes.length, arrLength),
- oldArray = copyArray(array);
+ var pullAt = flatRest(function(array, indexes) {
+ var length = array == null ? 0 : array.length,
+ result = baseAt(array, indexes);
- while (length--) {
- var index = indexes[length];
- array[length] = isIndex(index, arrLength) ? oldArray[index] : undefined;
- }
- return array;
- }
+ basePullAt(array, arrayMap(indexes, function(index) {
+ return isIndex(index, length) ? +index : index;
+ }).sort(compareAscending));
+
+ return result;
+ });
/**
- * Gets the value at `key`, unless `key` is "__proto__" or "constructor".
+ * Removes all elements from `array` that `predicate` returns truthy for
+ * and returns an array of the removed elements. The predicate is invoked
+ * with three arguments: (value, index, array).
*
- * @private
- * @param {Object} object The object to query.
- * @param {string} key The key of the property to get.
- * @returns {*} Returns the property value.
+ * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
+ * to pull elements from an array by value.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = [1, 2, 3, 4];
+ * var evens = _.remove(array, function(n) {
+ * return n % 2 == 0;
+ * });
+ *
+ * console.log(array);
+ * // => [1, 3]
+ *
+ * console.log(evens);
+ * // => [2, 4]
*/
- function safeGet(object, key) {
- if (key === 'constructor' && typeof object[key] === 'function') {
- return;
+ function remove(array, predicate) {
+ var result = [];
+ if (!(array && array.length)) {
+ return result;
}
+ var index = -1,
+ indexes = [],
+ length = array.length;
- if (key == '__proto__') {
- return;
+ predicate = getIteratee(predicate, 3);
+ while (++index < length) {
+ var value = array[index];
+ if (predicate(value, index, array)) {
+ result.push(value);
+ indexes.push(index);
+ }
}
-
- return object[key];
+ basePullAt(array, indexes);
+ return result;
}
/**
- * Sets metadata for `func`.
+ * Reverses `array` so that the first element becomes the last, the second
+ * element becomes the second to last, and so on.
*
- * **Note:** If this function becomes hot, i.e. is invoked a lot in a short
- * period of time, it will trip its breaker and transition to an identity
- * function to avoid garbage collection pauses in V8. See
- * [V8 issue 2070](https://bugs.chromium.org/p/v8/issues/detail?id=2070)
- * for more details.
+ * **Note:** This method mutates `array` and is based on
+ * [`Array#reverse`](https://mdn.io/Array/reverse).
*
- * @private
- * @param {Function} func The function to associate metadata with.
- * @param {*} data The metadata.
- * @returns {Function} Returns `func`.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to modify.
+ * @returns {Array} Returns `array`.
+ * @example
+ *
+ * var array = [1, 2, 3];
+ *
+ * _.reverse(array);
+ * // => [3, 2, 1]
+ *
+ * console.log(array);
+ * // => [3, 2, 1]
*/
- var setData = shortOut(baseSetData);
+ function reverse(array) {
+ return array == null ? array : nativeReverse.call(array);
+ }
/**
- * A simple wrapper around the global [`setTimeout`](https://mdn.io/setTimeout).
+ * Creates a slice of `array` from `start` up to, but not including, `end`.
*
- * @private
- * @param {Function} func The function to delay.
- * @param {number} wait The number of milliseconds to delay invocation.
- * @returns {number|Object} Returns the timer id or timeout object.
+ * **Note:** This method is used instead of
+ * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
+ * returned.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The array to slice.
+ * @param {number} [start=0] The start position.
+ * @param {number} [end=array.length] The end position.
+ * @returns {Array} Returns the slice of `array`.
*/
- var setTimeout = ctxSetTimeout || function(func, wait) {
- return root.setTimeout(func, wait);
- };
+ function slice(array, start, end) {
+ var length = array == null ? 0 : array.length;
+ if (!length) {
+ return [];
+ }
+ if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
+ start = 0;
+ end = length;
+ }
+ else {
+ start = start == null ? 0 : toInteger(start);
+ end = end === undefined ? length : toInteger(end);
+ }
+ return baseSlice(array, start, end);
+ }
/**
- * Sets the `toString` method of `func` to return `string`.
+ * Uses a binary search to determine the lowest index at which `value`
+ * should be inserted into `array` in order to maintain its sort order.
*
- * @private
- * @param {Function} func The function to modify.
- * @param {Function} string The `toString` result.
- * @returns {Function} Returns `func`.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedIndex([30, 50], 40);
+ * // => 1
*/
- var setToString = shortOut(baseSetToString);
+ function sortedIndex(array, value) {
+ return baseSortedIndex(array, value);
+ }
/**
- * Sets the `toString` method of `wrapper` to mimic the source of `reference`
- * with wrapper details in a comment at the top of the source body.
+ * This method is like `_.sortedIndex` except that it accepts `iteratee`
+ * which is invoked for `value` and each element of `array` to compute their
+ * sort ranking. The iteratee is invoked with one argument: (value).
*
- * @private
- * @param {Function} wrapper The function to modify.
- * @param {Function} reference The reference function.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
- * @returns {Function} Returns `wrapper`.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * var objects = [{ 'x': 4 }, { 'x': 5 }];
+ *
+ * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
+ * // => 0
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
+ * // => 0
*/
- function setWrapToString(wrapper, reference, bitmask) {
- var source = (reference + '');
- return setToString(wrapper, insertWrapDetails(source, updateWrapDetails(getWrapDetails(source), bitmask)));
+ function sortedIndexBy(array, value, iteratee) {
+ return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));
}
/**
- * Creates a function that'll short out and invoke `identity` instead
- * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
- * milliseconds.
+ * This method is like `_.indexOf` except that it performs a binary
+ * search on a sorted `array`.
*
- * @private
- * @param {Function} func The function to restrict.
- * @returns {Function} Returns the new shortable function.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
+ * // => 1
*/
- function shortOut(func) {
- var count = 0,
- lastCalled = 0;
-
- return function() {
- var stamp = nativeNow(),
- remaining = HOT_SPAN - (stamp - lastCalled);
-
- lastCalled = stamp;
- if (remaining > 0) {
- if (++count >= HOT_COUNT) {
- return arguments[0];
- }
- } else {
- count = 0;
+ function sortedIndexOf(array, value) {
+ var length = array == null ? 0 : array.length;
+ if (length) {
+ var index = baseSortedIndex(array, value);
+ if (index < length && eq(array[index], value)) {
+ return index;
}
- return func.apply(undefined, arguments);
- };
+ }
+ return -1;
}
/**
- * A specialized version of `_.shuffle` which mutates and sets the size of `array`.
+ * This method is like `_.sortedIndex` except that it returns the highest
+ * index at which `value` should be inserted into `array` in order to
+ * maintain its sort order.
*
- * @private
- * @param {Array} array The array to shuffle.
- * @param {number} [size=array.length] The size of `array`.
- * @returns {Array} Returns `array`.
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
+ * // => 4
*/
- function shuffleSelf(array, size) {
- var index = -1,
- length = array.length,
- lastIndex = length - 1;
-
- size = size === undefined ? length : size;
- while (++index < size) {
- var rand = baseRandom(index, lastIndex),
- value = array[rand];
+ function sortedLastIndex(array, value) {
+ return baseSortedIndex(array, value, true);
+ }
- array[rand] = array[index];
- array[index] = value;
- }
- array.length = size;
- return array;
+ /**
+ * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
+ * which is invoked for `value` and each element of `array` to compute their
+ * sort ranking. The iteratee is invoked with one argument: (value).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The sorted array to inspect.
+ * @param {*} value The value to evaluate.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {number} Returns the index at which `value` should be inserted
+ * into `array`.
+ * @example
+ *
+ * var objects = [{ 'x': 4 }, { 'x': 5 }];
+ *
+ * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
+ * // => 1
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
+ * // => 1
+ */
+ function sortedLastIndexBy(array, value, iteratee) {
+ return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);
}
/**
- * Converts `string` to a property path array.
+ * This method is like `_.lastIndexOf` except that it performs a binary
+ * search on a sorted `array`.
*
- * @private
- * @param {string} string The string to convert.
- * @returns {Array} Returns the property path array.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ * @example
+ *
+ * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
+ * // => 3
*/
- var stringToPath = memoizeCapped(function(string) {
- var result = [];
- if (string.charCodeAt(0) === 46 /* . */) {
- result.push('');
+ function sortedLastIndexOf(array, value) {
+ var length = array == null ? 0 : array.length;
+ if (length) {
+ var index = baseSortedIndex(array, value, true) - 1;
+ if (eq(array[index], value)) {
+ return index;
+ }
}
- string.replace(rePropName, function(match, number, quote, subString) {
- result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
- });
- return result;
- });
+ return -1;
+ }
/**
- * Converts `value` to a string key if it's not a string or symbol.
+ * This method is like `_.uniq` except that it's designed and optimized
+ * for sorted arrays.
*
- * @private
- * @param {*} value The value to inspect.
- * @returns {string|symbol} Returns the key.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.sortedUniq([1, 1, 2]);
+ * // => [1, 2]
*/
- function toKey(value) {
- if (typeof value == 'string' || isSymbol(value)) {
- return value;
- }
- var result = (value + '');
- return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+ function sortedUniq(array) {
+ return (array && array.length)
+ ? baseSortedUniq(array)
+ : [];
}
/**
- * Converts `func` to its source code.
+ * This method is like `_.uniqBy` except that it's designed and optimized
+ * for sorted arrays.
*
- * @private
- * @param {Function} func The function to convert.
- * @returns {string} Returns the source code.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
+ * // => [1.1, 2.3]
*/
- function toSource(func) {
- if (func != null) {
- try {
- return funcToString.call(func);
- } catch (e) {}
- try {
- return (func + '');
- } catch (e) {}
- }
- return '';
+ function sortedUniqBy(array, iteratee) {
+ return (array && array.length)
+ ? baseSortedUniq(array, getIteratee(iteratee, 2))
+ : [];
}
/**
- * Updates wrapper `details` based on `bitmask` flags.
+ * Gets all but the first element of `array`.
*
- * @private
- * @returns {Array} details The details to modify.
- * @param {number} bitmask The bitmask flags. See `createWrap` for more details.
- * @returns {Array} Returns `details`.
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.tail([1, 2, 3]);
+ * // => [2, 3]
*/
- function updateWrapDetails(details, bitmask) {
- arrayEach(wrapFlags, function(pair) {
- var value = '_.' + pair[0];
- if ((bitmask & pair[1]) && !arrayIncludes(details, value)) {
- details.push(value);
- }
- });
- return details.sort();
+ function tail(array) {
+ var length = array == null ? 0 : array.length;
+ return length ? baseSlice(array, 1, length) : [];
}
/**
- * Creates a clone of `wrapper`.
+ * Creates a slice of `array` with `n` elements taken from the beginning.
*
- * @private
- * @param {Object} wrapper The wrapper to clone.
- * @returns {Object} Returns the cloned wrapper.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.take([1, 2, 3]);
+ * // => [1]
+ *
+ * _.take([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.take([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.take([1, 2, 3], 0);
+ * // => []
*/
- function wrapperClone(wrapper) {
- if (wrapper instanceof LazyWrapper) {
- return wrapper.clone();
+ function take(array, n, guard) {
+ if (!(array && array.length)) {
+ return [];
}
- var result = new LodashWrapper(wrapper.__wrapped__, wrapper.__chain__);
- result.__actions__ = copyArray(wrapper.__actions__);
- result.__index__ = wrapper.__index__;
- result.__values__ = wrapper.__values__;
- return result;
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ return baseSlice(array, 0, n < 0 ? 0 : n);
}
- /*------------------------------------------------------------------------*/
-
/**
- * Creates an array of elements split into groups the length of `size`.
- * If `array` can't be split evenly, the final chunk will be the remaining
- * elements.
+ * Creates a slice of `array` with `n` elements taken from the end.
*
* @static
* @memberOf _
* @since 3.0.0
* @category Array
- * @param {Array} array The array to process.
- * @param {number} [size=1] The length of each chunk
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
* @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the new array of chunks.
+ * @returns {Array} Returns the slice of `array`.
* @example
*
- * _.chunk(['a', 'b', 'c', 'd'], 2);
- * // => [['a', 'b'], ['c', 'd']]
+ * _.takeRight([1, 2, 3]);
+ * // => [3]
*
- * _.chunk(['a', 'b', 'c', 'd'], 3);
- * // => [['a', 'b', 'c'], ['d']]
+ * _.takeRight([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.takeRight([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.takeRight([1, 2, 3], 0);
+ * // => []
*/
- function chunk(array, size, guard) {
- if ((guard ? isIterateeCall(array, size, guard) : size === undefined)) {
- size = 1;
- } else {
- size = nativeMax(toInteger(size), 0);
- }
+ function takeRight(array, n, guard) {
var length = array == null ? 0 : array.length;
- if (!length || size < 1) {
+ if (!length) {
return [];
}
- var index = 0,
- resIndex = 0,
- result = Array(nativeCeil(length / size));
-
- while (index < length) {
- result[resIndex++] = baseSlice(array, index, (index += size));
- }
- return result;
+ n = (guard || n === undefined) ? 1 : toInteger(n);
+ n = length - n;
+ return baseSlice(array, n < 0 ? 0 : n, length);
}
/**
- * Creates an array with all falsey values removed. The values `false`, `null`,
- * `0`, `""`, `undefined`, and `NaN` are falsey.
+ * Creates a slice of `array` with elements taken from the end. Elements are
+ * taken until `predicate` returns falsey. The predicate is invoked with
+ * three arguments: (value, index, array).
*
* @static
* @memberOf _
- * @since 0.1.0
+ * @since 3.0.0
* @category Array
- * @param {Array} array The array to compact.
- * @returns {Array} Returns the new array of filtered values.
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
* @example
*
- * _.compact([0, 1, false, 2, '', 3]);
- * // => [1, 2, 3]
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': false }
+ * ];
+ *
+ * _.takeRightWhile(users, function(o) { return !o.active; });
+ * // => objects for ['fred', 'pebbles']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
+ * // => objects for ['pebbles']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.takeRightWhile(users, ['active', false]);
+ * // => objects for ['fred', 'pebbles']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.takeRightWhile(users, 'active');
+ * // => []
*/
- function compact(array) {
- var index = -1,
- length = array == null ? 0 : array.length,
- resIndex = 0,
- result = [];
-
- while (++index < length) {
- var value = array[index];
- if (value) {
- result[resIndex++] = value;
- }
- }
- return result;
+ function takeRightWhile(array, predicate) {
+ return (array && array.length)
+ ? baseWhile(array, getIteratee(predicate, 3), false, true)
+ : [];
}
/**
- * Creates a new array concatenating `array` with any additional arrays
- * and/or values.
+ * Creates a slice of `array` with elements taken from the beginning. Elements
+ * are taken until `predicate` returns falsey. The predicate is invoked with
+ * three arguments: (value, index, array).
*
* @static
* @memberOf _
- * @since 4.0.0
+ * @since 3.0.0
* @category Array
- * @param {Array} array The array to concatenate.
- * @param {...*} [values] The values to concatenate.
- * @returns {Array} Returns the new concatenated array.
+ * @param {Array} array The array to query.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the slice of `array`.
* @example
*
- * var array = [1];
- * var other = _.concat(array, 2, [3], [[4]]);
+ * var users = [
+ * { 'user': 'barney', 'active': false },
+ * { 'user': 'fred', 'active': false },
+ * { 'user': 'pebbles', 'active': true }
+ * ];
*
- * console.log(other);
- * // => [1, 2, 3, [4]]
+ * _.takeWhile(users, function(o) { return !o.active; });
+ * // => objects for ['barney', 'fred']
*
- * console.log(array);
- * // => [1]
+ * // The `_.matches` iteratee shorthand.
+ * _.takeWhile(users, { 'user': 'barney', 'active': false });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.takeWhile(users, ['active', false]);
+ * // => objects for ['barney', 'fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.takeWhile(users, 'active');
+ * // => []
*/
- function concat() {
- var length = arguments.length;
- if (!length) {
- return [];
- }
- var args = Array(length - 1),
- array = arguments[0],
- index = length;
-
- while (index--) {
- args[index - 1] = arguments[index];
- }
- return arrayPush(isArray(array) ? copyArray(array) : [array], baseFlatten(args, 1));
+ function takeWhile(array, predicate) {
+ return (array && array.length)
+ ? baseWhile(array, getIteratee(predicate, 3))
+ : [];
}
/**
- * Creates an array of `array` values not included in the other given arrays
- * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons. The order and references of result values are
- * determined by the first array.
- *
- * **Note:** Unlike `_.pullAll`, this method returns a new array.
+ * Creates an array of unique values, in order, from all given arrays using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Array
- * @param {Array} array The array to inspect.
- * @param {...Array} [values] The values to exclude.
- * @returns {Array} Returns the new array of filtered values.
- * @see _.without, _.xor
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of combined values.
* @example
*
- * _.difference([2, 1], [2, 3]);
- * // => [1]
+ * _.union([2], [1, 2]);
+ * // => [2, 1]
*/
- var difference = baseRest(function(array, values) {
- return isArrayLikeObject(array)
- ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
- : [];
+ var union = baseRest(function(arrays) {
+ return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
});
/**
- * This method is like `_.difference` except that it accepts `iteratee` which
- * is invoked for each element of `array` and `values` to generate the criterion
- * by which they're compared. The order and references of result values are
- * determined by the first array. The iteratee is invoked with one argument:
+ * This method is like `_.union` except that it accepts `iteratee` which is
+ * invoked for each element of each `arrays` to generate the criterion by
+ * which uniqueness is computed. Result values are chosen from the first
+ * array in which the value occurs. The iteratee is invoked with one argument:
* (value).
*
- * **Note:** Unlike `_.pullAllBy`, this method returns a new array.
- *
* @static
* @memberOf _
* @since 4.0.0
* @category Array
- * @param {Array} array The array to inspect.
- * @param {...Array} [values] The values to exclude.
+ * @param {...Array} [arrays] The arrays to inspect.
* @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @returns {Array} Returns the new array of combined values.
* @example
*
- * _.differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor);
- * // => [1.2]
+ * _.unionBy([2.1], [1.2, 2.3], Math.floor);
+ * // => [2.1, 1.2]
*
* // The `_.property` iteratee shorthand.
- * _.differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x');
- * // => [{ 'x': 2 }]
+ * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
*/
- var differenceBy = baseRest(function(array, values) {
- var iteratee = last(values);
+ var unionBy = baseRest(function(arrays) {
+ var iteratee = last(arrays);
if (isArrayLikeObject(iteratee)) {
iteratee = undefined;
}
- return isArrayLikeObject(array)
- ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), getIteratee(iteratee, 2))
- : [];
+ return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));
+ });
+
+ /**
+ * This method is like `_.union` except that it accepts `comparator` which
+ * is invoked to compare elements of `arrays`. Result values are chosen from
+ * the first array in which the value occurs. The comparator is invoked
+ * with two arguments: (arrVal, othVal).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Array
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of combined values.
+ * @example
+ *
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ *
+ * _.unionWith(objects, others, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ */
+ var unionWith = baseRest(function(arrays) {
+ var comparator = last(arrays);
+ comparator = typeof comparator == 'function' ? comparator : undefined;
+ return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);
});
/**
- * This method is like `_.difference` except that it accepts `comparator`
- * which is invoked to compare elements of `array` to `values`. The order and
- * references of result values are determined by the first array. The comparator
- * is invoked with two arguments: (arrVal, othVal).
- *
- * **Note:** Unlike `_.pullAllWith`, this method returns a new array.
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each element
+ * is kept. The order of result values is determined by the order they occur
+ * in the array.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */
+ function uniq(array) {
+ return (array && array.length) ? baseUniq(array) : [];
+ }
+
+ /**
+ * This method is like `_.uniq` except that it accepts `iteratee` which is
+ * invoked for each element in `array` to generate the criterion by which
+ * uniqueness is computed. The order of result values is determined by the
+ * order they occur in the array. The iteratee is invoked with one argument:
+ * (value).
*
* @static
* @memberOf _
* @since 4.0.0
* @category Array
* @param {Array} array The array to inspect.
- * @param {...Array} [values] The values to exclude.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
* @example
*
- * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
+ * // => [2.1, 1.2]
*
- * _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
- * // => [{ 'x': 2, 'y': 1 }]
+ * // The `_.property` iteratee shorthand.
+ * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 1 }, { 'x': 2 }]
*/
- var differenceWith = baseRest(function(array, values) {
- var comparator = last(values);
- if (isArrayLikeObject(comparator)) {
- comparator = undefined;
- }
- return isArrayLikeObject(array)
- ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator)
- : [];
- });
+ function uniqBy(array, iteratee) {
+ return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : [];
+ }
/**
- * Creates a slice of `array` with `n` elements dropped from the beginning.
+ * This method is like `_.uniq` except that it accepts `comparator` which
+ * is invoked to compare elements of `array`. The order of result values is
+ * determined by the order they occur in the array.The comparator is invoked
+ * with two arguments: (arrVal, othVal).
*
* @static
* @memberOf _
- * @since 0.5.0
+ * @since 4.0.0
* @category Array
- * @param {Array} array The array to query.
- * @param {number} [n=1] The number of elements to drop.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the slice of `array`.
+ * @param {Array} array The array to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
* @example
*
- * _.drop([1, 2, 3]);
- * // => [2, 3]
- *
- * _.drop([1, 2, 3], 2);
- * // => [3]
- *
- * _.drop([1, 2, 3], 5);
- * // => []
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
*
- * _.drop([1, 2, 3], 0);
- * // => [1, 2, 3]
+ * _.uniqWith(objects, _.isEqual);
+ * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
*/
- function drop(array, n, guard) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return [];
- }
- n = (guard || n === undefined) ? 1 : toInteger(n);
- return baseSlice(array, n < 0 ? 0 : n, length);
+ function uniqWith(array, comparator) {
+ comparator = typeof comparator == 'function' ? comparator : undefined;
+ return (array && array.length) ? baseUniq(array, undefined, comparator) : [];
}
/**
- * Creates a slice of `array` with `n` elements dropped from the end.
+ * This method is like `_.zip` except that it accepts an array of grouped
+ * elements and creates an array regrouping the elements to their pre-zip
+ * configuration.
*
* @static
* @memberOf _
- * @since 3.0.0
+ * @since 1.2.0
* @category Array
- * @param {Array} array The array to query.
- * @param {number} [n=1] The number of elements to drop.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the slice of `array`.
+ * @param {Array} array The array of grouped elements to process.
+ * @returns {Array} Returns the new array of regrouped elements.
* @example
*
- * _.dropRight([1, 2, 3]);
- * // => [1, 2]
- *
- * _.dropRight([1, 2, 3], 2);
- * // => [1]
- *
- * _.dropRight([1, 2, 3], 5);
- * // => []
+ * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
+ * // => [['a', 1, true], ['b', 2, false]]
*
- * _.dropRight([1, 2, 3], 0);
- * // => [1, 2, 3]
+ * _.unzip(zipped);
+ * // => [['a', 'b'], [1, 2], [true, false]]
*/
- function dropRight(array, n, guard) {
- var length = array == null ? 0 : array.length;
- if (!length) {
+ function unzip(array) {
+ if (!(array && array.length)) {
return [];
}
- n = (guard || n === undefined) ? 1 : toInteger(n);
- n = length - n;
- return baseSlice(array, 0, n < 0 ? 0 : n);
+ var length = 0;
+ array = arrayFilter(array, function(group) {
+ if (isArrayLikeObject(group)) {
+ length = nativeMax(group.length, length);
+ return true;
+ }
+ });
+ return baseTimes(length, function(index) {
+ return arrayMap(array, baseProperty(index));
+ });
}
/**
- * Creates a slice of `array` excluding elements dropped from the end.
- * Elements are dropped until `predicate` returns falsey. The predicate is
- * invoked with three arguments: (value, index, array).
+ * This method is like `_.unzip` except that it accepts `iteratee` to specify
+ * how regrouped values should be combined. The iteratee is invoked with the
+ * elements of each group: (...group).
*
* @static
* @memberOf _
- * @since 3.0.0
+ * @since 3.8.0
* @category Array
- * @param {Array} array The array to query.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the slice of `array`.
+ * @param {Array} array The array of grouped elements to process.
+ * @param {Function} [iteratee=_.identity] The function to combine
+ * regrouped values.
+ * @returns {Array} Returns the new array of regrouped elements.
* @example
*
- * var users = [
- * { 'user': 'barney', 'active': true },
- * { 'user': 'fred', 'active': false },
- * { 'user': 'pebbles', 'active': false }
- * ];
- *
- * _.dropRightWhile(users, function(o) { return !o.active; });
- * // => objects for ['barney']
- *
- * // The `_.matches` iteratee shorthand.
- * _.dropRightWhile(users, { 'user': 'pebbles', 'active': false });
- * // => objects for ['barney', 'fred']
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.dropRightWhile(users, ['active', false]);
- * // => objects for ['barney']
+ * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
+ * // => [[1, 10, 100], [2, 20, 200]]
*
- * // The `_.property` iteratee shorthand.
- * _.dropRightWhile(users, 'active');
- * // => objects for ['barney', 'fred', 'pebbles']
+ * _.unzipWith(zipped, _.add);
+ * // => [3, 30, 300]
*/
- function dropRightWhile(array, predicate) {
- return (array && array.length)
- ? baseWhile(array, getIteratee(predicate, 3), true, true)
- : [];
+ function unzipWith(array, iteratee) {
+ if (!(array && array.length)) {
+ return [];
+ }
+ var result = unzip(array);
+ if (iteratee == null) {
+ return result;
+ }
+ return arrayMap(result, function(group) {
+ return apply(iteratee, undefined, group);
+ });
}
/**
- * Creates a slice of `array` excluding elements dropped from the beginning.
- * Elements are dropped until `predicate` returns falsey. The predicate is
- * invoked with three arguments: (value, index, array).
+ * Creates an array excluding all given values using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons.
+ *
+ * **Note:** Unlike `_.pull`, this method returns a new array.
*
* @static
* @memberOf _
- * @since 3.0.0
+ * @since 0.1.0
* @category Array
- * @param {Array} array The array to query.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the slice of `array`.
+ * @param {Array} array The array to inspect.
+ * @param {...*} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.difference, _.xor
* @example
*
- * var users = [
- * { 'user': 'barney', 'active': false },
- * { 'user': 'fred', 'active': false },
- * { 'user': 'pebbles', 'active': true }
- * ];
- *
- * _.dropWhile(users, function(o) { return !o.active; });
- * // => objects for ['pebbles']
- *
- * // The `_.matches` iteratee shorthand.
- * _.dropWhile(users, { 'user': 'barney', 'active': false });
- * // => objects for ['fred', 'pebbles']
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.dropWhile(users, ['active', false]);
- * // => objects for ['pebbles']
- *
- * // The `_.property` iteratee shorthand.
- * _.dropWhile(users, 'active');
- * // => objects for ['barney', 'fred', 'pebbles']
+ * _.without([2, 1, 2, 3], 1, 2);
+ * // => [3]
*/
- function dropWhile(array, predicate) {
- return (array && array.length)
- ? baseWhile(array, getIteratee(predicate, 3), true)
+ var without = baseRest(function(array, values) {
+ return isArrayLikeObject(array)
+ ? baseDifference(array, values)
: [];
- }
+ });
/**
- * Fills elements of `array` with `value` from `start` up to, but not
- * including, `end`.
- *
- * **Note:** This method mutates `array`.
+ * Creates an array of unique values that is the
+ * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
+ * of the given arrays. The order of result values is determined by the order
+ * they occur in the arrays.
*
* @static
* @memberOf _
- * @since 3.2.0
+ * @since 2.4.0
* @category Array
- * @param {Array} array The array to fill.
- * @param {*} value The value to fill `array` with.
- * @param {number} [start=0] The start position.
- * @param {number} [end=array.length] The end position.
- * @returns {Array} Returns `array`.
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of filtered values.
+ * @see _.difference, _.without
* @example
*
- * var array = [1, 2, 3];
- *
- * _.fill(array, 'a');
- * console.log(array);
- * // => ['a', 'a', 'a']
- *
- * _.fill(Array(3), 2);
- * // => [2, 2, 2]
- *
- * _.fill([4, 6, 8, 10], '*', 1, 3);
- * // => [4, '*', '*', 10]
+ * _.xor([2, 1], [2, 3]);
+ * // => [1, 3]
*/
- function fill(array, value, start, end) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return [];
- }
- if (start && typeof start != 'number' && isIterateeCall(array, value, start)) {
- start = 0;
- end = length;
- }
- return baseFill(array, value, start, end);
- }
+ var xor = baseRest(function(arrays) {
+ return baseXor(arrayFilter(arrays, isArrayLikeObject));
+ });
/**
- * This method is like `_.find` except that it returns the index of the first
- * element `predicate` returns truthy for instead of the element itself.
+ * This method is like `_.xor` except that it accepts `iteratee` which is
+ * invoked for each element of each `arrays` to generate the criterion by
+ * which by which they're compared. The order of result values is determined
+ * by the order they occur in the arrays. The iteratee is invoked with one
+ * argument: (value).
*
* @static
* @memberOf _
- * @since 1.1.0
+ * @since 4.0.0
* @category Array
- * @param {Array} array The array to inspect.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @param {number} [fromIndex=0] The index to search from.
- * @returns {number} Returns the index of the found element, else `-1`.
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
* @example
*
- * var users = [
- * { 'user': 'barney', 'active': false },
- * { 'user': 'fred', 'active': false },
- * { 'user': 'pebbles', 'active': true }
- * ];
- *
- * _.findIndex(users, function(o) { return o.user == 'barney'; });
- * // => 0
- *
- * // The `_.matches` iteratee shorthand.
- * _.findIndex(users, { 'user': 'fred', 'active': false });
- * // => 1
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.findIndex(users, ['active', false]);
- * // => 0
+ * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
+ * // => [1.2, 3.4]
*
* // The `_.property` iteratee shorthand.
- * _.findIndex(users, 'active');
- * // => 2
+ * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
+ * // => [{ 'x': 2 }]
*/
- function findIndex(array, predicate, fromIndex) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return -1;
- }
- var index = fromIndex == null ? 0 : toInteger(fromIndex);
- if (index < 0) {
- index = nativeMax(length + index, 0);
+ var xorBy = baseRest(function(arrays) {
+ var iteratee = last(arrays);
+ if (isArrayLikeObject(iteratee)) {
+ iteratee = undefined;
}
- return baseFindIndex(array, getIteratee(predicate, 3), index);
- }
+ return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));
+ });
/**
- * This method is like `_.findIndex` except that it iterates over elements
- * of `collection` from right to left.
+ * This method is like `_.xor` except that it accepts `comparator` which is
+ * invoked to compare elements of `arrays`. The order of result values is
+ * determined by the order they occur in the arrays. The comparator is invoked
+ * with two arguments: (arrVal, othVal).
*
* @static
* @memberOf _
- * @since 2.0.0
+ * @since 4.0.0
* @category Array
- * @param {Array} array The array to inspect.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @param {number} [fromIndex=array.length-1] The index to search from.
- * @returns {number} Returns the index of the found element, else `-1`.
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new array of filtered values.
* @example
*
- * var users = [
- * { 'user': 'barney', 'active': true },
- * { 'user': 'fred', 'active': false },
- * { 'user': 'pebbles', 'active': false }
- * ];
- *
- * _.findLastIndex(users, function(o) { return o.user == 'pebbles'; });
- * // => 2
- *
- * // The `_.matches` iteratee shorthand.
- * _.findLastIndex(users, { 'user': 'barney', 'active': true });
- * // => 0
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.findLastIndex(users, ['active', false]);
- * // => 2
+ * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
+ * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
*
- * // The `_.property` iteratee shorthand.
- * _.findLastIndex(users, 'active');
- * // => 0
+ * _.xorWith(objects, others, _.isEqual);
+ * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
*/
- function findLastIndex(array, predicate, fromIndex) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return -1;
- }
- var index = length - 1;
- if (fromIndex !== undefined) {
- index = toInteger(fromIndex);
- index = fromIndex < 0
- ? nativeMax(length + index, 0)
- : nativeMin(index, length - 1);
- }
- return baseFindIndex(array, getIteratee(predicate, 3), index, true);
- }
+ var xorWith = baseRest(function(arrays) {
+ var comparator = last(arrays);
+ comparator = typeof comparator == 'function' ? comparator : undefined;
+ return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
+ });
/**
- * Flattens `array` a single level deep.
+ * Creates an array of grouped elements, the first of which contains the
+ * first elements of the given arrays, the second of which contains the
+ * second elements of the given arrays, and so on.
*
* @static
* @memberOf _
* @since 0.1.0
* @category Array
- * @param {Array} array The array to flatten.
- * @returns {Array} Returns the new flattened array.
+ * @param {...Array} [arrays] The arrays to process.
+ * @returns {Array} Returns the new array of grouped elements.
* @example
*
- * _.flatten([1, [2, [3, [4]], 5]]);
- * // => [1, 2, [3, [4]], 5]
+ * _.zip(['a', 'b'], [1, 2], [true, false]);
+ * // => [['a', 1, true], ['b', 2, false]]
*/
- function flatten(array) {
- var length = array == null ? 0 : array.length;
- return length ? baseFlatten(array, 1) : [];
- }
+ var zip = baseRest(unzip);
/**
- * Recursively flattens `array`.
+ * This method is like `_.fromPairs` except that it accepts two arrays,
+ * one of property identifiers and one of corresponding values.
*
* @static
* @memberOf _
- * @since 3.0.0
+ * @since 0.4.0
* @category Array
- * @param {Array} array The array to flatten.
- * @returns {Array} Returns the new flattened array.
+ * @param {Array} [props=[]] The property identifiers.
+ * @param {Array} [values=[]] The property values.
+ * @returns {Object} Returns the new object.
* @example
*
- * _.flattenDeep([1, [2, [3, [4]], 5]]);
- * // => [1, 2, 3, 4, 5]
+ * _.zipObject(['a', 'b'], [1, 2]);
+ * // => { 'a': 1, 'b': 2 }
*/
- function flattenDeep(array) {
- var length = array == null ? 0 : array.length;
- return length ? baseFlatten(array, INFINITY) : [];
+ function zipObject(props, values) {
+ return baseZipObject(props || [], values || [], assignValue);
}
/**
- * Recursively flatten `array` up to `depth` times.
+ * This method is like `_.zipObject` except that it supports property paths.
*
* @static
* @memberOf _
- * @since 4.4.0
+ * @since 4.1.0
* @category Array
- * @param {Array} array The array to flatten.
- * @param {number} [depth=1] The maximum recursion depth.
- * @returns {Array} Returns the new flattened array.
+ * @param {Array} [props=[]] The property identifiers.
+ * @param {Array} [values=[]] The property values.
+ * @returns {Object} Returns the new object.
* @example
*
- * var array = [1, [2, [3, [4]], 5]];
- *
- * _.flattenDepth(array, 1);
- * // => [1, 2, [3, [4]], 5]
- *
- * _.flattenDepth(array, 2);
- * // => [1, 2, 3, [4], 5]
+ * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
+ * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
*/
- function flattenDepth(array, depth) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return [];
- }
- depth = depth === undefined ? 1 : toInteger(depth);
- return baseFlatten(array, depth);
+ function zipObjectDeep(props, values) {
+ return baseZipObject(props || [], values || [], baseSet);
}
/**
- * The inverse of `_.toPairs`; this method returns an object composed
- * from key-value `pairs`.
+ * This method is like `_.zip` except that it accepts `iteratee` to specify
+ * how grouped values should be combined. The iteratee is invoked with the
+ * elements of each group: (...group).
*
* @static
* @memberOf _
- * @since 4.0.0
+ * @since 3.8.0
* @category Array
- * @param {Array} pairs The key-value pairs.
- * @returns {Object} Returns the new object.
+ * @param {...Array} [arrays] The arrays to process.
+ * @param {Function} [iteratee=_.identity] The function to combine
+ * grouped values.
+ * @returns {Array} Returns the new array of grouped elements.
* @example
*
- * _.fromPairs([['a', 1], ['b', 2]]);
- * // => { 'a': 1, 'b': 2 }
+ * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
+ * return a + b + c;
+ * });
+ * // => [111, 222]
*/
- function fromPairs(pairs) {
- var index = -1,
- length = pairs == null ? 0 : pairs.length,
- result = {};
+ var zipWith = baseRest(function(arrays) {
+ var length = arrays.length,
+ iteratee = length > 1 ? arrays[length - 1] : undefined;
- while (++index < length) {
- var pair = pairs[index];
- result[pair[0]] = pair[1];
- }
- return result;
- }
+ iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;
+ return unzipWith(arrays, iteratee);
+ });
+
+ /*------------------------------------------------------------------------*/
/**
- * Gets the first element of `array`.
+ * Creates a `lodash` wrapper instance that wraps `value` with explicit method
+ * chain sequences enabled. The result of such sequences must be unwrapped
+ * with `_#value`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @alias first
- * @category Array
- * @param {Array} array The array to query.
- * @returns {*} Returns the first element of `array`.
+ * @since 1.3.0
+ * @category Seq
+ * @param {*} value The value to wrap.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
- * _.head([1, 2, 3]);
- * // => 1
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'pebbles', 'age': 1 }
+ * ];
*
- * _.head([]);
- * // => undefined
+ * var youngest = _
+ * .chain(users)
+ * .sortBy('age')
+ * .map(function(o) {
+ * return o.user + ' is ' + o.age;
+ * })
+ * .head()
+ * .value();
+ * // => 'pebbles is 1'
*/
- function head(array) {
- return (array && array.length) ? array[0] : undefined;
+ function chain(value) {
+ var result = lodash(value);
+ result.__chain__ = true;
+ return result;
}
/**
- * Gets the index at which the first occurrence of `value` is found in `array`
- * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons. If `fromIndex` is negative, it's used as the
- * offset from the end of `array`.
+ * This method invokes `interceptor` and returns `value`. The interceptor
+ * is invoked with one argument; (value). The purpose of this method is to
+ * "tap into" a method chain sequence in order to modify intermediate results.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @param {number} [fromIndex=0] The index to search from.
- * @returns {number} Returns the index of the matched value, else `-1`.
+ * @category Seq
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns `value`.
* @example
*
- * _.indexOf([1, 2, 1, 2], 2);
- * // => 1
- *
- * // Search from the `fromIndex`.
- * _.indexOf([1, 2, 1, 2], 2, 2);
- * // => 3
+ * _([1, 2, 3])
+ * .tap(function(array) {
+ * // Mutate input array.
+ * array.pop();
+ * })
+ * .reverse()
+ * .value();
+ * // => [2, 1]
*/
- function indexOf(array, value, fromIndex) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return -1;
- }
- var index = fromIndex == null ? 0 : toInteger(fromIndex);
- if (index < 0) {
- index = nativeMax(length + index, 0);
- }
- return baseIndexOf(array, value, index);
+ function tap(value, interceptor) {
+ interceptor(value);
+ return value;
}
/**
- * Gets all but the last element of `array`.
+ * This method is like `_.tap` except that it returns the result of `interceptor`.
+ * The purpose of this method is to "pass thru" values replacing intermediate
+ * results in a method chain sequence.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The array to query.
- * @returns {Array} Returns the slice of `array`.
+ * @since 3.0.0
+ * @category Seq
+ * @param {*} value The value to provide to `interceptor`.
+ * @param {Function} interceptor The function to invoke.
+ * @returns {*} Returns the result of `interceptor`.
* @example
*
- * _.initial([1, 2, 3]);
- * // => [1, 2]
+ * _(' abc ')
+ * .chain()
+ * .trim()
+ * .thru(function(value) {
+ * return [value];
+ * })
+ * .value();
+ * // => ['abc']
*/
- function initial(array) {
- var length = array == null ? 0 : array.length;
- return length ? baseSlice(array, 0, -1) : [];
+ function thru(value, interceptor) {
+ return interceptor(value);
}
/**
- * Creates an array of unique values that are included in all given arrays
- * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons. The order and references of result values are
- * determined by the first array.
+ * This method is the wrapper version of `_.at`.
*
- * @static
+ * @name at
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @returns {Array} Returns the new array of intersecting values.
+ * @since 1.0.0
+ * @category Seq
+ * @param {...(string|string[])} [paths] The property paths to pick.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
- * _.intersection([2, 1], [2, 3]);
- * // => [2]
+ * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
+ *
+ * _(object).at(['a[0].b.c', 'a[1]']).value();
+ * // => [3, 4]
*/
- var intersection = baseRest(function(arrays) {
- var mapped = arrayMap(arrays, castArrayLikeObject);
- return (mapped.length && mapped[0] === arrays[0])
- ? baseIntersection(mapped)
- : [];
+ var wrapperAt = flatRest(function(paths) {
+ var length = paths.length,
+ start = length ? paths[0] : 0,
+ value = this.__wrapped__,
+ interceptor = function(object) { return baseAt(object, paths); };
+
+ if (length > 1 || this.__actions__.length ||
+ !(value instanceof LazyWrapper) || !isIndex(start)) {
+ return this.thru(interceptor);
+ }
+ value = value.slice(start, +start + (length ? 1 : 0));
+ value.__actions__.push({
+ 'func': thru,
+ 'args': [interceptor],
+ 'thisArg': undefined
+ });
+ return new LodashWrapper(value, this.__chain__).thru(function(array) {
+ if (length && !array.length) {
+ array.push(undefined);
+ }
+ return array;
+ });
});
/**
- * This method is like `_.intersection` except that it accepts `iteratee`
- * which is invoked for each element of each `arrays` to generate the criterion
- * by which they're compared. The order and references of result values are
- * determined by the first array. The iteratee is invoked with one argument:
- * (value).
+ * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
*
- * @static
+ * @name chain
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {Array} Returns the new array of intersecting values.
+ * @since 0.1.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
- * _.intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor);
- * // => [2.1]
+ * var users = [
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 }
+ * ];
*
- * // The `_.property` iteratee shorthand.
- * _.intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
- * // => [{ 'x': 1 }]
+ * // A sequence without explicit chaining.
+ * _(users).head();
+ * // => { 'user': 'barney', 'age': 36 }
+ *
+ * // A sequence with explicit chaining.
+ * _(users)
+ * .chain()
+ * .head()
+ * .pick('user')
+ * .value();
+ * // => { 'user': 'barney' }
*/
- var intersectionBy = baseRest(function(arrays) {
- var iteratee = last(arrays),
- mapped = arrayMap(arrays, castArrayLikeObject);
-
- if (iteratee === last(mapped)) {
- iteratee = undefined;
- } else {
- mapped.pop();
- }
- return (mapped.length && mapped[0] === arrays[0])
- ? baseIntersection(mapped, getIteratee(iteratee, 2))
- : [];
- });
+ function wrapperChain() {
+ return chain(this);
+ }
/**
- * This method is like `_.intersection` except that it accepts `comparator`
- * which is invoked to compare elements of `arrays`. The order and references
- * of result values are determined by the first array. The comparator is
- * invoked with two arguments: (arrVal, othVal).
+ * Executes the chain sequence and returns the wrapped result.
*
- * @static
+ * @name commit
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of intersecting values.
+ * @since 3.2.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
- * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
- * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ * var array = [1, 2];
+ * var wrapped = _(array).push(3);
*
- * _.intersectionWith(objects, others, _.isEqual);
- * // => [{ 'x': 1, 'y': 2 }]
+ * console.log(array);
+ * // => [1, 2]
+ *
+ * wrapped = wrapped.commit();
+ * console.log(array);
+ * // => [1, 2, 3]
+ *
+ * wrapped.last();
+ * // => 3
+ *
+ * console.log(array);
+ * // => [1, 2, 3]
*/
- var intersectionWith = baseRest(function(arrays) {
- var comparator = last(arrays),
- mapped = arrayMap(arrays, castArrayLikeObject);
-
- comparator = typeof comparator == 'function' ? comparator : undefined;
- if (comparator) {
- mapped.pop();
- }
- return (mapped.length && mapped[0] === arrays[0])
- ? baseIntersection(mapped, undefined, comparator)
- : [];
- });
+ function wrapperCommit() {
+ return new LodashWrapper(this.value(), this.__chain__);
+ }
/**
- * Converts all elements in `array` into a string separated by `separator`.
+ * Gets the next value on a wrapped object following the
+ * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
*
- * @static
+ * @name next
* @memberOf _
* @since 4.0.0
- * @category Array
- * @param {Array} array The array to convert.
- * @param {string} [separator=','] The element separator.
- * @returns {string} Returns the joined string.
+ * @category Seq
+ * @returns {Object} Returns the next iterator value.
* @example
*
- * _.join(['a', 'b', 'c'], '~');
- * // => 'a~b~c'
+ * var wrapped = _([1, 2]);
+ *
+ * wrapped.next();
+ * // => { 'done': false, 'value': 1 }
+ *
+ * wrapped.next();
+ * // => { 'done': false, 'value': 2 }
+ *
+ * wrapped.next();
+ * // => { 'done': true, 'value': undefined }
*/
- function join(array, separator) {
- return array == null ? '' : nativeJoin.call(array, separator);
+ function wrapperNext() {
+ if (this.__values__ === undefined) {
+ this.__values__ = toArray(this.value());
+ }
+ var done = this.__index__ >= this.__values__.length,
+ value = done ? undefined : this.__values__[this.__index__++];
+
+ return { 'done': done, 'value': value };
}
/**
- * Gets the last element of `array`.
+ * Enables the wrapper to be iterable.
*
- * @static
+ * @name Symbol.iterator
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The array to query.
- * @returns {*} Returns the last element of `array`.
+ * @since 4.0.0
+ * @category Seq
+ * @returns {Object} Returns the wrapper object.
* @example
*
- * _.last([1, 2, 3]);
- * // => 3
+ * var wrapped = _([1, 2]);
+ *
+ * wrapped[Symbol.iterator]() === wrapped;
+ * // => true
+ *
+ * Array.from(wrapped);
+ * // => [1, 2]
*/
- function last(array) {
- var length = array == null ? 0 : array.length;
- return length ? array[length - 1] : undefined;
+ function wrapperToIterator() {
+ return this;
}
/**
- * This method is like `_.indexOf` except that it iterates over elements of
- * `array` from right to left.
+ * Creates a clone of the chain sequence planting `value` as the wrapped value.
*
- * @static
+ * @name plant
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @param {number} [fromIndex=array.length-1] The index to search from.
- * @returns {number} Returns the index of the matched value, else `-1`.
+ * @since 3.2.0
+ * @category Seq
+ * @param {*} value The value to plant.
+ * @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
- * _.lastIndexOf([1, 2, 1, 2], 2);
- * // => 3
+ * function square(n) {
+ * return n * n;
+ * }
*
- * // Search from the `fromIndex`.
- * _.lastIndexOf([1, 2, 1, 2], 2, 2);
- * // => 1
+ * var wrapped = _([1, 2]).map(square);
+ * var other = wrapped.plant([3, 4]);
+ *
+ * other.value();
+ * // => [9, 16]
+ *
+ * wrapped.value();
+ * // => [1, 4]
*/
- function lastIndexOf(array, value, fromIndex) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return -1;
- }
- var index = length;
- if (fromIndex !== undefined) {
- index = toInteger(fromIndex);
- index = index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
+ function wrapperPlant(value) {
+ var result,
+ parent = this;
+
+ while (parent instanceof baseLodash) {
+ var clone = wrapperClone(parent);
+ clone.__index__ = 0;
+ clone.__values__ = undefined;
+ if (result) {
+ previous.__wrapped__ = clone;
+ } else {
+ result = clone;
+ }
+ var previous = clone;
+ parent = parent.__wrapped__;
}
- return value === value
- ? strictLastIndexOf(array, value, index)
- : baseFindIndex(array, baseIsNaN, index, true);
+ previous.__wrapped__ = value;
+ return result;
}
/**
- * Gets the element at index `n` of `array`. If `n` is negative, the nth
- * element from the end is returned.
+ * This method is the wrapper version of `_.reverse`.
*
- * @static
+ * **Note:** This method mutates the wrapped array.
+ *
+ * @name reverse
* @memberOf _
- * @since 4.11.0
- * @category Array
- * @param {Array} array The array to query.
- * @param {number} [n=0] The index of the element to return.
- * @returns {*} Returns the nth element of `array`.
+ * @since 0.1.0
+ * @category Seq
+ * @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
- * var array = ['a', 'b', 'c', 'd'];
+ * var array = [1, 2, 3];
*
- * _.nth(array, 1);
- * // => 'b'
+ * _(array).reverse().value()
+ * // => [3, 2, 1]
*
- * _.nth(array, -2);
- * // => 'c';
+ * console.log(array);
+ * // => [3, 2, 1]
*/
- function nth(array, n) {
- return (array && array.length) ? baseNth(array, toInteger(n)) : undefined;
+ function wrapperReverse() {
+ var value = this.__wrapped__;
+ if (value instanceof LazyWrapper) {
+ var wrapped = value;
+ if (this.__actions__.length) {
+ wrapped = new LazyWrapper(this);
+ }
+ wrapped = wrapped.reverse();
+ wrapped.__actions__.push({
+ 'func': thru,
+ 'args': [reverse],
+ 'thisArg': undefined
+ });
+ return new LodashWrapper(wrapped, this.__chain__);
+ }
+ return this.thru(reverse);
}
/**
- * Removes all given values from `array` using
- * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons.
- *
- * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
- * to remove elements from an array by predicate.
+ * Executes the chain sequence to resolve the unwrapped value.
*
- * @static
+ * @name value
* @memberOf _
- * @since 2.0.0
- * @category Array
- * @param {Array} array The array to modify.
- * @param {...*} [values] The values to remove.
- * @returns {Array} Returns `array`.
+ * @since 0.1.0
+ * @alias toJSON, valueOf
+ * @category Seq
+ * @returns {*} Returns the resolved unwrapped value.
* @example
*
- * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
- *
- * _.pull(array, 'a', 'c');
- * console.log(array);
- * // => ['b', 'b']
+ * _([1, 2, 3]).value();
+ * // => [1, 2, 3]
*/
- var pull = baseRest(pullAll);
+ function wrapperValue() {
+ return baseWrapperValue(this.__wrapped__, this.__actions__);
+ }
+
+ /*------------------------------------------------------------------------*/
/**
- * This method is like `_.pull` except that it accepts an array of values to remove.
- *
- * **Note:** Unlike `_.difference`, this method mutates `array`.
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The corresponding value of
+ * each key is the number of times the key was returned by `iteratee`. The
+ * iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The array to modify.
- * @param {Array} values The values to remove.
- * @returns {Array} Returns `array`.
+ * @since 0.5.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
* @example
*
- * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
+ * _.countBy([6.1, 4.2, 6.3], Math.floor);
+ * // => { '4': 1, '6': 2 }
*
- * _.pullAll(array, ['a', 'c']);
- * console.log(array);
- * // => ['b', 'b']
+ * // The `_.property` iteratee shorthand.
+ * _.countBy(['one', 'two', 'three'], 'length');
+ * // => { '3': 2, '5': 1 }
*/
- function pullAll(array, values) {
- return (array && array.length && values && values.length)
- ? basePullAll(array, values)
- : array;
- }
+ var countBy = createAggregator(function(result, value, key) {
+ if (hasOwnProperty.call(result, key)) {
+ ++result[key];
+ } else {
+ baseAssignValue(result, key, 1);
+ }
+ });
/**
- * This method is like `_.pullAll` except that it accepts `iteratee` which is
- * invoked for each element of `array` and `values` to generate the criterion
- * by which they're compared. The iteratee is invoked with one argument: (value).
+ * Checks if `predicate` returns truthy for **all** elements of `collection`.
+ * Iteration is stopped once `predicate` returns falsey. The predicate is
+ * invoked with three arguments: (value, index|key, collection).
*
- * **Note:** Unlike `_.differenceBy`, this method mutates `array`.
+ * **Note:** This method returns `true` for
+ * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
+ * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
+ * elements of empty collections.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The array to modify.
- * @param {Array} values The values to remove.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {Array} Returns `array`.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {boolean} Returns `true` if all elements pass the predicate check,
+ * else `false`.
* @example
*
- * var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
+ * _.every([true, 1, null, 'yes'], Boolean);
+ * // => false
*
- * _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
- * console.log(array);
- * // => [{ 'x': 2 }]
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.every(users, { 'user': 'barney', 'active': false });
+ * // => false
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.every(users, ['active', false]);
+ * // => true
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.every(users, 'active');
+ * // => false
*/
- function pullAllBy(array, values, iteratee) {
- return (array && array.length && values && values.length)
- ? basePullAll(array, values, getIteratee(iteratee, 2))
- : array;
+ function every(collection, predicate, guard) {
+ var func = isArray(collection) ? arrayEvery : baseEvery;
+ if (guard && isIterateeCall(collection, predicate, guard)) {
+ predicate = undefined;
+ }
+ return func(collection, getIteratee(predicate, 3));
}
/**
- * This method is like `_.pullAll` except that it accepts `comparator` which
- * is invoked to compare elements of `array` to `values`. The comparator is
- * invoked with two arguments: (arrVal, othVal).
+ * Iterates over elements of `collection`, returning an array of all elements
+ * `predicate` returns truthy for. The predicate is invoked with three
+ * arguments: (value, index|key, collection).
*
- * **Note:** Unlike `_.differenceWith`, this method mutates `array`.
+ * **Note:** Unlike `_.remove`, this method returns a new array.
*
* @static
* @memberOf _
- * @since 4.6.0
- * @category Array
- * @param {Array} array The array to modify.
- * @param {Array} values The values to remove.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns `array`.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new filtered array.
+ * @see _.reject
* @example
*
- * var array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }];
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false }
+ * ];
*
- * _.pullAllWith(array, [{ 'x': 3, 'y': 4 }], _.isEqual);
- * console.log(array);
- * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
+ * _.filter(users, function(o) { return !o.active; });
+ * // => objects for ['fred']
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.filter(users, { 'age': 36, 'active': true });
+ * // => objects for ['barney']
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.filter(users, ['active', false]);
+ * // => objects for ['fred']
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.filter(users, 'active');
+ * // => objects for ['barney']
*/
- function pullAllWith(array, values, comparator) {
- return (array && array.length && values && values.length)
- ? basePullAll(array, values, undefined, comparator)
- : array;
+ function filter(collection, predicate) {
+ var func = isArray(collection) ? arrayFilter : baseFilter;
+ return func(collection, getIteratee(predicate, 3));
}
/**
- * Removes elements from `array` corresponding to `indexes` and returns an
- * array of removed elements.
- *
- * **Note:** Unlike `_.at`, this method mutates `array`.
+ * Iterates over elements of `collection`, returning the first element
+ * `predicate` returns truthy for. The predicate is invoked with three
+ * arguments: (value, index|key, collection).
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Array
- * @param {Array} array The array to modify.
- * @param {...(number|number[])} [indexes] The indexes of elements to remove.
- * @returns {Array} Returns the new array of removed elements.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @returns {*} Returns the matched element, else `undefined`.
* @example
*
- * var array = ['a', 'b', 'c', 'd'];
- * var pulled = _.pullAt(array, [1, 3]);
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': true },
+ * { 'user': 'fred', 'age': 40, 'active': false },
+ * { 'user': 'pebbles', 'age': 1, 'active': true }
+ * ];
*
- * console.log(array);
- * // => ['a', 'c']
+ * _.find(users, function(o) { return o.age < 40; });
+ * // => object for 'barney'
*
- * console.log(pulled);
- * // => ['b', 'd']
+ * // The `_.matches` iteratee shorthand.
+ * _.find(users, { 'age': 1, 'active': true });
+ * // => object for 'pebbles'
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.find(users, ['active', false]);
+ * // => object for 'fred'
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.find(users, 'active');
+ * // => object for 'barney'
*/
- var pullAt = flatRest(function(array, indexes) {
- var length = array == null ? 0 : array.length,
- result = baseAt(array, indexes);
-
- basePullAt(array, arrayMap(indexes, function(index) {
- return isIndex(index, length) ? +index : index;
- }).sort(compareAscending));
-
- return result;
- });
+ var find = createFind(findIndex);
/**
- * Removes all elements from `array` that `predicate` returns truthy for
- * and returns an array of the removed elements. The predicate is invoked
- * with three arguments: (value, index, array).
- *
- * **Note:** Unlike `_.filter`, this method mutates `array`. Use `_.pull`
- * to pull elements from an array by value.
+ * This method is like `_.find` except that it iterates over elements of
+ * `collection` from right to left.
*
* @static
* @memberOf _
* @since 2.0.0
- * @category Array
- * @param {Array} array The array to modify.
+ * @category Collection
+ * @param {Array|Object} collection The collection to inspect.
* @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the new array of removed elements.
+ * @param {number} [fromIndex=collection.length-1] The index to search from.
+ * @returns {*} Returns the matched element, else `undefined`.
* @example
*
- * var array = [1, 2, 3, 4];
- * var evens = _.remove(array, function(n) {
- * return n % 2 == 0;
+ * _.findLast([1, 2, 3, 4], function(n) {
+ * return n % 2 == 1;
* });
- *
- * console.log(array);
- * // => [1, 3]
- *
- * console.log(evens);
- * // => [2, 4]
+ * // => 3
*/
- function remove(array, predicate) {
- var result = [];
- if (!(array && array.length)) {
- return result;
- }
- var index = -1,
- indexes = [],
- length = array.length;
-
- predicate = getIteratee(predicate, 3);
- while (++index < length) {
- var value = array[index];
- if (predicate(value, index, array)) {
- result.push(value);
- indexes.push(index);
- }
- }
- basePullAt(array, indexes);
- return result;
- }
+ var findLast = createFind(findLastIndex);
/**
- * Reverses `array` so that the first element becomes the last, the second
- * element becomes the second to last, and so on.
- *
- * **Note:** This method mutates `array` and is based on
- * [`Array#reverse`](https://mdn.io/Array/reverse).
+ * Creates a flattened array of values by running each element in `collection`
+ * thru `iteratee` and flattening the mapped results. The iteratee is invoked
+ * with three arguments: (value, index|key, collection).
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Array
- * @param {Array} array The array to modify.
- * @returns {Array} Returns `array`.
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new flattened array.
* @example
*
- * var array = [1, 2, 3];
- *
- * _.reverse(array);
- * // => [3, 2, 1]
+ * function duplicate(n) {
+ * return [n, n];
+ * }
*
- * console.log(array);
- * // => [3, 2, 1]
+ * _.flatMap([1, 2], duplicate);
+ * // => [1, 1, 2, 2]
*/
- function reverse(array) {
- return array == null ? array : nativeReverse.call(array);
+ function flatMap(collection, iteratee) {
+ return baseFlatten(map(collection, iteratee), 1);
}
/**
- * Creates a slice of `array` from `start` up to, but not including, `end`.
- *
- * **Note:** This method is used instead of
- * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are
- * returned.
+ * This method is like `_.flatMap` except that it recursively flattens the
+ * mapped results.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Array
- * @param {Array} array The array to slice.
- * @param {number} [start=0] The start position.
- * @param {number} [end=array.length] The end position.
- * @returns {Array} Returns the slice of `array`.
+ * @since 4.7.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new flattened array.
+ * @example
+ *
+ * function duplicate(n) {
+ * return [[[n, n]]];
+ * }
+ *
+ * _.flatMapDeep([1, 2], duplicate);
+ * // => [1, 1, 2, 2]
*/
- function slice(array, start, end) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return [];
- }
- if (end && typeof end != 'number' && isIterateeCall(array, start, end)) {
- start = 0;
- end = length;
- }
- else {
- start = start == null ? 0 : toInteger(start);
- end = end === undefined ? length : toInteger(end);
- }
- return baseSlice(array, start, end);
+ function flatMapDeep(collection, iteratee) {
+ return baseFlatten(map(collection, iteratee), INFINITY);
}
/**
- * Uses a binary search to determine the lowest index at which `value`
- * should be inserted into `array` in order to maintain its sort order.
+ * This method is like `_.flatMap` except that it recursively flattens the
+ * mapped results up to `depth` times.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The sorted array to inspect.
- * @param {*} value The value to evaluate.
- * @returns {number} Returns the index at which `value` should be inserted
- * into `array`.
+ * @since 4.7.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {number} [depth=1] The maximum recursion depth.
+ * @returns {Array} Returns the new flattened array.
* @example
*
- * _.sortedIndex([30, 50], 40);
- * // => 1
+ * function duplicate(n) {
+ * return [[[n, n]]];
+ * }
+ *
+ * _.flatMapDepth([1, 2], duplicate, 2);
+ * // => [[1, 1], [2, 2]]
*/
- function sortedIndex(array, value) {
- return baseSortedIndex(array, value);
+ function flatMapDepth(collection, iteratee, depth) {
+ depth = depth === undefined ? 1 : toInteger(depth);
+ return baseFlatten(map(collection, iteratee), depth);
}
-
- /**
- * This method is like `_.sortedIndex` except that it accepts `iteratee`
- * which is invoked for `value` and each element of `array` to compute their
- * sort ranking. The iteratee is invoked with one argument: (value).
+
+ /**
+ * Iterates over elements of `collection` and invokes `iteratee` for each element.
+ * The iteratee is invoked with three arguments: (value, index|key, collection).
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
+ *
+ * **Note:** As with other "Collections" methods, objects with a "length"
+ * property are iterated like arrays. To avoid this behavior use `_.forIn`
+ * or `_.forOwn` for object iteration.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The sorted array to inspect.
- * @param {*} value The value to evaluate.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {number} Returns the index at which `value` should be inserted
- * into `array`.
+ * @since 0.1.0
+ * @alias each
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ * @see _.forEachRight
* @example
*
- * var objects = [{ 'x': 4 }, { 'x': 5 }];
- *
- * _.sortedIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
- * // => 0
+ * _.forEach([1, 2], function(value) {
+ * console.log(value);
+ * });
+ * // => Logs `1` then `2`.
*
- * // The `_.property` iteratee shorthand.
- * _.sortedIndexBy(objects, { 'x': 4 }, 'x');
- * // => 0
+ * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'a' then 'b' (iteration order is not guaranteed).
*/
- function sortedIndexBy(array, value, iteratee) {
- return baseSortedIndexBy(array, value, getIteratee(iteratee, 2));
+ function forEach(collection, iteratee) {
+ var func = isArray(collection) ? arrayEach : baseEach;
+ return func(collection, getIteratee(iteratee, 3));
}
/**
- * This method is like `_.indexOf` except that it performs a binary
- * search on a sorted `array`.
+ * This method is like `_.forEach` except that it iterates over elements of
+ * `collection` from right to left.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @returns {number} Returns the index of the matched value, else `-1`.
+ * @since 2.0.0
+ * @alias eachRight
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array|Object} Returns `collection`.
+ * @see _.forEach
* @example
*
- * _.sortedIndexOf([4, 5, 5, 5, 6], 5);
- * // => 1
+ * _.forEachRight([1, 2], function(value) {
+ * console.log(value);
+ * });
+ * // => Logs `2` then `1`.
*/
- function sortedIndexOf(array, value) {
- var length = array == null ? 0 : array.length;
- if (length) {
- var index = baseSortedIndex(array, value);
- if (index < length && eq(array[index], value)) {
- return index;
- }
- }
- return -1;
+ function forEachRight(collection, iteratee) {
+ var func = isArray(collection) ? arrayEachRight : baseEachRight;
+ return func(collection, getIteratee(iteratee, 3));
}
/**
- * This method is like `_.sortedIndex` except that it returns the highest
- * index at which `value` should be inserted into `array` in order to
- * maintain its sort order.
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The order of grouped values
+ * is determined by the order they occur in `collection`. The corresponding
+ * value of each key is an array of elements responsible for generating the
+ * key. The iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Array
- * @param {Array} array The sorted array to inspect.
- * @param {*} value The value to evaluate.
- * @returns {number} Returns the index at which `value` should be inserted
- * into `array`.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
* @example
*
- * _.sortedLastIndex([4, 5, 5, 5, 6], 5);
- * // => 4
+ * _.groupBy([6.1, 4.2, 6.3], Math.floor);
+ * // => { '4': [4.2], '6': [6.1, 6.3] }
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.groupBy(['one', 'two', 'three'], 'length');
+ * // => { '3': ['one', 'two'], '5': ['three'] }
*/
- function sortedLastIndex(array, value) {
- return baseSortedIndex(array, value, true);
- }
+ var groupBy = createAggregator(function(result, value, key) {
+ if (hasOwnProperty.call(result, key)) {
+ result[key].push(value);
+ } else {
+ baseAssignValue(result, key, [value]);
+ }
+ });
/**
- * This method is like `_.sortedLastIndex` except that it accepts `iteratee`
- * which is invoked for `value` and each element of `array` to compute their
- * sort ranking. The iteratee is invoked with one argument: (value).
+ * Checks if `value` is in `collection`. If `collection` is a string, it's
+ * checked for a substring of `value`, otherwise
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * is used for equality comparisons. If `fromIndex` is negative, it's used as
+ * the offset from the end of `collection`.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The sorted array to inspect.
- * @param {*} value The value to evaluate.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {number} Returns the index at which `value` should be inserted
- * into `array`.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} [fromIndex=0] The index to search from.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
+ * @returns {boolean} Returns `true` if `value` is found, else `false`.
* @example
*
- * var objects = [{ 'x': 4 }, { 'x': 5 }];
+ * _.includes([1, 2, 3], 1);
+ * // => true
*
- * _.sortedLastIndexBy(objects, { 'x': 4 }, function(o) { return o.x; });
- * // => 1
+ * _.includes([1, 2, 3], 1, 2);
+ * // => false
*
- * // The `_.property` iteratee shorthand.
- * _.sortedLastIndexBy(objects, { 'x': 4 }, 'x');
- * // => 1
+ * _.includes({ 'a': 1, 'b': 2 }, 1);
+ * // => true
+ *
+ * _.includes('abcd', 'bc');
+ * // => true
*/
- function sortedLastIndexBy(array, value, iteratee) {
- return baseSortedIndexBy(array, value, getIteratee(iteratee, 2), true);
+ function includes(collection, value, fromIndex, guard) {
+ collection = isArrayLike(collection) ? collection : values(collection);
+ fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;
+
+ var length = collection.length;
+ if (fromIndex < 0) {
+ fromIndex = nativeMax(length + fromIndex, 0);
+ }
+ return isString(collection)
+ ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
+ : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
}
/**
- * This method is like `_.lastIndexOf` except that it performs a binary
- * search on a sorted `array`.
+ * Invokes the method at `path` of each element in `collection`, returning
+ * an array of the results of each invoked method. Any additional arguments
+ * are provided to each invoked method. If `path` is a function, it's invoked
+ * for, and `this` bound to, each element in `collection`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {*} value The value to search for.
- * @returns {number} Returns the index of the matched value, else `-1`.
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Array|Function|string} path The path of the method to invoke or
+ * the function invoked per iteration.
+ * @param {...*} [args] The arguments to invoke each method with.
+ * @returns {Array} Returns the array of results.
* @example
*
- * _.sortedLastIndexOf([4, 5, 5, 5, 6], 5);
- * // => 3
+ * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
+ * // => [[1, 5, 7], [1, 2, 3]]
+ *
+ * _.invokeMap([123, 456], String.prototype.split, '');
+ * // => [['1', '2', '3'], ['4', '5', '6']]
*/
- function sortedLastIndexOf(array, value) {
- var length = array == null ? 0 : array.length;
- if (length) {
- var index = baseSortedIndex(array, value, true) - 1;
- if (eq(array[index], value)) {
- return index;
- }
- }
- return -1;
- }
+ var invokeMap = baseRest(function(collection, path, args) {
+ var index = -1,
+ isFunc = typeof path == 'function',
+ result = isArrayLike(collection) ? Array(collection.length) : [];
+
+ baseEach(collection, function(value) {
+ result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);
+ });
+ return result;
+ });
/**
- * This method is like `_.uniq` except that it's designed and optimized
- * for sorted arrays.
+ * Creates an object composed of keys generated from the results of running
+ * each element of `collection` thru `iteratee`. The corresponding value of
+ * each key is the last element responsible for generating the key. The
+ * iteratee is invoked with one argument: (value).
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @returns {Array} Returns the new duplicate free array.
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
+ * @returns {Object} Returns the composed aggregate object.
* @example
*
- * _.sortedUniq([1, 1, 2]);
- * // => [1, 2]
+ * var array = [
+ * { 'dir': 'left', 'code': 97 },
+ * { 'dir': 'right', 'code': 100 }
+ * ];
+ *
+ * _.keyBy(array, function(o) {
+ * return String.fromCharCode(o.code);
+ * });
+ * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ *
+ * _.keyBy(array, 'dir');
+ * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
*/
- function sortedUniq(array) {
- return (array && array.length)
- ? baseSortedUniq(array)
- : [];
- }
+ var keyBy = createAggregator(function(result, value, key) {
+ baseAssignValue(result, key, value);
+ });
/**
- * This method is like `_.uniqBy` except that it's designed and optimized
- * for sorted arrays.
+ * Creates an array of values by running each element in `collection` thru
+ * `iteratee`. The iteratee is invoked with three arguments:
+ * (value, index|key, collection).
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like
+ * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
+ *
+ * The guarded methods are:
+ * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
+ * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
+ * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
+ * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {Function} [iteratee] The iteratee invoked per element.
- * @returns {Array} Returns the new duplicate free array.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the new mapped array.
* @example
*
- * _.sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor);
- * // => [1.1, 2.3]
+ * function square(n) {
+ * return n * n;
+ * }
+ *
+ * _.map([4, 8], square);
+ * // => [16, 64]
+ *
+ * _.map({ 'a': 4, 'b': 8 }, square);
+ * // => [16, 64] (iteration order is not guaranteed)
+ *
+ * var users = [
+ * { 'user': 'barney' },
+ * { 'user': 'fred' }
+ * ];
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.map(users, 'user');
+ * // => ['barney', 'fred']
*/
- function sortedUniqBy(array, iteratee) {
- return (array && array.length)
- ? baseSortedUniq(array, getIteratee(iteratee, 2))
- : [];
+ function map(collection, iteratee) {
+ var func = isArray(collection) ? arrayMap : baseMap;
+ return func(collection, getIteratee(iteratee, 3));
}
/**
- * Gets all but the first element of `array`.
+ * This method is like `_.sortBy` except that it allows specifying the sort
+ * orders of the iteratees to sort by. If `orders` is unspecified, all values
+ * are sorted in ascending order. Otherwise, specify an order of "desc" for
+ * descending or "asc" for ascending sort order of corresponding values.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Array
- * @param {Array} array The array to query.
- * @returns {Array} Returns the slice of `array`.
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
+ * The iteratees to sort by.
+ * @param {string[]} [orders] The sort orders of `iteratees`.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
+ * @returns {Array} Returns the new sorted array.
* @example
*
- * _.tail([1, 2, 3]);
- * // => [2, 3]
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 34 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'barney', 'age': 36 }
+ * ];
+ *
+ * // Sort by `user` in ascending order and by `age` in descending order.
+ * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
*/
- function tail(array) {
- var length = array == null ? 0 : array.length;
- return length ? baseSlice(array, 1, length) : [];
+ function orderBy(collection, iteratees, orders, guard) {
+ if (collection == null) {
+ return [];
+ }
+ if (!isArray(iteratees)) {
+ iteratees = iteratees == null ? [] : [iteratees];
+ }
+ orders = guard ? undefined : orders;
+ if (!isArray(orders)) {
+ orders = orders == null ? [] : [orders];
+ }
+ return baseOrderBy(collection, iteratees, orders);
}
/**
- * Creates a slice of `array` with `n` elements taken from the beginning.
+ * Creates an array of elements split into two groups, the first of which
+ * contains elements `predicate` returns truthy for, the second of which
+ * contains elements `predicate` returns falsey for. The predicate is
+ * invoked with one argument: (value).
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The array to query.
- * @param {number} [n=1] The number of elements to take.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the slice of `array`.
+ * @since 3.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {Array} Returns the array of grouped elements.
* @example
*
- * _.take([1, 2, 3]);
- * // => [1]
+ * var users = [
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true },
+ * { 'user': 'pebbles', 'age': 1, 'active': false }
+ * ];
*
- * _.take([1, 2, 3], 2);
- * // => [1, 2]
+ * _.partition(users, function(o) { return o.active; });
+ * // => objects for [['fred'], ['barney', 'pebbles']]
*
- * _.take([1, 2, 3], 5);
- * // => [1, 2, 3]
+ * // The `_.matches` iteratee shorthand.
+ * _.partition(users, { 'age': 1, 'active': false });
+ * // => objects for [['pebbles'], ['barney', 'fred']]
*
- * _.take([1, 2, 3], 0);
- * // => []
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.partition(users, ['active', false]);
+ * // => objects for [['barney', 'pebbles'], ['fred']]
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.partition(users, 'active');
+ * // => objects for [['fred'], ['barney', 'pebbles']]
*/
- function take(array, n, guard) {
- if (!(array && array.length)) {
- return [];
- }
- n = (guard || n === undefined) ? 1 : toInteger(n);
- return baseSlice(array, 0, n < 0 ? 0 : n);
- }
+ var partition = createAggregator(function(result, value, key) {
+ result[key ? 0 : 1].push(value);
+ }, function() { return [[], []]; });
/**
- * Creates a slice of `array` with `n` elements taken from the end.
+ * Reduces `collection` to a value which is the accumulated result of running
+ * each element in `collection` thru `iteratee`, where each successive
+ * invocation is supplied the return value of the previous. If `accumulator`
+ * is not given, the first element of `collection` is used as the initial
+ * value. The iteratee is invoked with four arguments:
+ * (accumulator, value, index|key, collection).
+ *
+ * Many lodash methods are guarded to work as iteratees for methods like
+ * `_.reduce`, `_.reduceRight`, and `_.transform`.
+ *
+ * The guarded methods are:
+ * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
+ * and `sortBy`
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Array
- * @param {Array} array The array to query.
- * @param {number} [n=1] The number of elements to take.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the slice of `array`.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @returns {*} Returns the accumulated value.
+ * @see _.reduceRight
* @example
*
- * _.takeRight([1, 2, 3]);
- * // => [3]
- *
- * _.takeRight([1, 2, 3], 2);
- * // => [2, 3]
- *
- * _.takeRight([1, 2, 3], 5);
- * // => [1, 2, 3]
+ * _.reduce([1, 2], function(sum, n) {
+ * return sum + n;
+ * }, 0);
+ * // => 3
*
- * _.takeRight([1, 2, 3], 0);
- * // => []
+ * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
+ * (result[value] || (result[value] = [])).push(key);
+ * return result;
+ * }, {});
+ * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
*/
- function takeRight(array, n, guard) {
- var length = array == null ? 0 : array.length;
- if (!length) {
- return [];
- }
- n = (guard || n === undefined) ? 1 : toInteger(n);
- n = length - n;
- return baseSlice(array, n < 0 ? 0 : n, length);
+ function reduce(collection, iteratee, accumulator) {
+ var func = isArray(collection) ? arrayReduce : baseReduce,
+ initAccum = arguments.length < 3;
+
+ return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
}
/**
- * Creates a slice of `array` with elements taken from the end. Elements are
- * taken until `predicate` returns falsey. The predicate is invoked with
- * three arguments: (value, index, array).
+ * This method is like `_.reduce` except that it iterates over elements of
+ * `collection` from right to left.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Array
- * @param {Array} array The array to query.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the slice of `array`.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The initial value.
+ * @returns {*} Returns the accumulated value.
+ * @see _.reduce
* @example
*
- * var users = [
- * { 'user': 'barney', 'active': true },
- * { 'user': 'fred', 'active': false },
- * { 'user': 'pebbles', 'active': false }
- * ];
- *
- * _.takeRightWhile(users, function(o) { return !o.active; });
- * // => objects for ['fred', 'pebbles']
- *
- * // The `_.matches` iteratee shorthand.
- * _.takeRightWhile(users, { 'user': 'pebbles', 'active': false });
- * // => objects for ['pebbles']
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.takeRightWhile(users, ['active', false]);
- * // => objects for ['fred', 'pebbles']
+ * var array = [[0, 1], [2, 3], [4, 5]];
*
- * // The `_.property` iteratee shorthand.
- * _.takeRightWhile(users, 'active');
- * // => []
+ * _.reduceRight(array, function(flattened, other) {
+ * return flattened.concat(other);
+ * }, []);
+ * // => [4, 5, 2, 3, 0, 1]
*/
- function takeRightWhile(array, predicate) {
- return (array && array.length)
- ? baseWhile(array, getIteratee(predicate, 3), false, true)
- : [];
+ function reduceRight(collection, iteratee, accumulator) {
+ var func = isArray(collection) ? arrayReduceRight : baseReduce,
+ initAccum = arguments.length < 3;
+
+ return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);
}
/**
- * Creates a slice of `array` with elements taken from the beginning. Elements
- * are taken until `predicate` returns falsey. The predicate is invoked with
- * three arguments: (value, index, array).
+ * The opposite of `_.filter`; this method returns the elements of `collection`
+ * that `predicate` does **not** return truthy for.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Array
- * @param {Array} array The array to query.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
* @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the slice of `array`.
+ * @returns {Array} Returns the new filtered array.
+ * @see _.filter
* @example
*
* var users = [
- * { 'user': 'barney', 'active': false },
- * { 'user': 'fred', 'active': false },
- * { 'user': 'pebbles', 'active': true }
+ * { 'user': 'barney', 'age': 36, 'active': false },
+ * { 'user': 'fred', 'age': 40, 'active': true }
* ];
*
- * _.takeWhile(users, function(o) { return !o.active; });
- * // => objects for ['barney', 'fred']
+ * _.reject(users, function(o) { return !o.active; });
+ * // => objects for ['fred']
*
* // The `_.matches` iteratee shorthand.
- * _.takeWhile(users, { 'user': 'barney', 'active': false });
+ * _.reject(users, { 'age': 40, 'active': true });
* // => objects for ['barney']
*
* // The `_.matchesProperty` iteratee shorthand.
- * _.takeWhile(users, ['active', false]);
- * // => objects for ['barney', 'fred']
+ * _.reject(users, ['active', false]);
+ * // => objects for ['fred']
*
* // The `_.property` iteratee shorthand.
- * _.takeWhile(users, 'active');
- * // => []
+ * _.reject(users, 'active');
+ * // => objects for ['barney']
*/
- function takeWhile(array, predicate) {
- return (array && array.length)
- ? baseWhile(array, getIteratee(predicate, 3))
- : [];
+ function reject(collection, predicate) {
+ var func = isArray(collection) ? arrayFilter : baseFilter;
+ return func(collection, negate(getIteratee(predicate, 3)));
}
/**
- * Creates an array of unique values, in order, from all given arrays using
- * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons.
+ * Gets a random element from `collection`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @returns {Array} Returns the new array of combined values.
+ * @since 2.0.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to sample.
+ * @returns {*} Returns the random element.
* @example
*
- * _.union([2], [1, 2]);
- * // => [2, 1]
+ * _.sample([1, 2, 3, 4]);
+ * // => 2
*/
- var union = baseRest(function(arrays) {
- return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true));
- });
+ function sample(collection) {
+ var func = isArray(collection) ? arraySample : baseSample;
+ return func(collection);
+ }
/**
- * This method is like `_.union` except that it accepts `iteratee` which is
- * invoked for each element of each `arrays` to generate the criterion by
- * which uniqueness is computed. Result values are chosen from the first
- * array in which the value occurs. The iteratee is invoked with one argument:
- * (value).
+ * Gets `n` random elements at unique keys from `collection` up to the
+ * size of `collection`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {Array} Returns the new array of combined values.
+ * @category Collection
+ * @param {Array|Object} collection The collection to sample.
+ * @param {number} [n=1] The number of elements to sample.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Array} Returns the random elements.
* @example
*
- * _.unionBy([2.1], [1.2, 2.3], Math.floor);
- * // => [2.1, 1.2]
+ * _.sampleSize([1, 2, 3], 2);
+ * // => [3, 1]
*
- * // The `_.property` iteratee shorthand.
- * _.unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
- * // => [{ 'x': 1 }, { 'x': 2 }]
+ * _.sampleSize([1, 2, 3], 4);
+ * // => [2, 3, 1]
*/
- var unionBy = baseRest(function(arrays) {
- var iteratee = last(arrays);
- if (isArrayLikeObject(iteratee)) {
- iteratee = undefined;
+ function sampleSize(collection, n, guard) {
+ if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {
+ n = 1;
+ } else {
+ n = toInteger(n);
}
- return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), getIteratee(iteratee, 2));
- });
+ var func = isArray(collection) ? arraySampleSize : baseSampleSize;
+ return func(collection, n);
+ }
/**
- * This method is like `_.union` except that it accepts `comparator` which
- * is invoked to compare elements of `arrays`. Result values are chosen from
- * the first array in which the value occurs. The comparator is invoked
- * with two arguments: (arrVal, othVal).
+ * Creates an array of shuffled values, using a version of the
+ * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of combined values.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to shuffle.
+ * @returns {Array} Returns the new shuffled array.
* @example
*
- * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
- * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
- *
- * _.unionWith(objects, others, _.isEqual);
- * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ * _.shuffle([1, 2, 3, 4]);
+ * // => [4, 1, 3, 2]
*/
- var unionWith = baseRest(function(arrays) {
- var comparator = last(arrays);
- comparator = typeof comparator == 'function' ? comparator : undefined;
- return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator);
- });
+ function shuffle(collection) {
+ var func = isArray(collection) ? arrayShuffle : baseShuffle;
+ return func(collection);
+ }
/**
- * Creates a duplicate-free version of an array, using
- * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons, in which only the first occurrence of each element
- * is kept. The order of result values is determined by the order they occur
- * in the array.
+ * Gets the size of `collection` by returning its length for array-like
+ * values or the number of own enumerable string keyed properties for objects.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @returns {Array} Returns the new duplicate free array.
+ * @category Collection
+ * @param {Array|Object|string} collection The collection to inspect.
+ * @returns {number} Returns the collection size.
* @example
*
- * _.uniq([2, 1, 2]);
- * // => [2, 1]
+ * _.size([1, 2, 3]);
+ * // => 3
+ *
+ * _.size({ 'a': 1, 'b': 2 });
+ * // => 2
+ *
+ * _.size('pebbles');
+ * // => 7
*/
- function uniq(array) {
- return (array && array.length) ? baseUniq(array) : [];
+ function size(collection) {
+ if (collection == null) {
+ return 0;
+ }
+ if (isArrayLike(collection)) {
+ return isString(collection) ? stringSize(collection) : collection.length;
+ }
+ var tag = getTag(collection);
+ if (tag == mapTag || tag == setTag) {
+ return collection.size;
+ }
+ return baseKeys(collection).length;
}
/**
- * This method is like `_.uniq` except that it accepts `iteratee` which is
- * invoked for each element in `array` to generate the criterion by which
- * uniqueness is computed. The order of result values is determined by the
- * order they occur in the array. The iteratee is invoked with one argument:
- * (value).
+ * Checks if `predicate` returns truthy for **any** element of `collection`.
+ * Iteration is stopped once `predicate` returns truthy. The predicate is
+ * invoked with three arguments: (value, index|key, collection).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {Array} Returns the new duplicate free array.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {boolean} Returns `true` if any element passes the predicate check,
+ * else `false`.
* @example
*
- * _.uniqBy([2.1, 1.2, 2.3], Math.floor);
- * // => [2.1, 1.2]
+ * _.some([null, 0, 'yes', false], Boolean);
+ * // => true
+ *
+ * var users = [
+ * { 'user': 'barney', 'active': true },
+ * { 'user': 'fred', 'active': false }
+ * ];
+ *
+ * // The `_.matches` iteratee shorthand.
+ * _.some(users, { 'user': 'barney', 'active': false });
+ * // => false
+ *
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.some(users, ['active', false]);
+ * // => true
*
* // The `_.property` iteratee shorthand.
- * _.uniqBy([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
- * // => [{ 'x': 1 }, { 'x': 2 }]
+ * _.some(users, 'active');
+ * // => true
*/
- function uniqBy(array, iteratee) {
- return (array && array.length) ? baseUniq(array, getIteratee(iteratee, 2)) : [];
+ function some(collection, predicate, guard) {
+ var func = isArray(collection) ? arraySome : baseSome;
+ if (guard && isIterateeCall(collection, predicate, guard)) {
+ predicate = undefined;
+ }
+ return func(collection, getIteratee(predicate, 3));
}
/**
- * This method is like `_.uniq` except that it accepts `comparator` which
- * is invoked to compare elements of `array`. The order of result values is
- * determined by the order they occur in the array.The comparator is invoked
- * with two arguments: (arrVal, othVal).
+ * Creates an array of elements, sorted in ascending order by the results of
+ * running each element in a collection thru each iteratee. This method
+ * performs a stable sort, that is, it preserves the original sort order of
+ * equal elements. The iteratees are invoked with one argument: (value).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new duplicate free array.
+ * @since 0.1.0
+ * @category Collection
+ * @param {Array|Object} collection The collection to iterate over.
+ * @param {...(Function|Function[])} [iteratees=[_.identity]]
+ * The iteratees to sort by.
+ * @returns {Array} Returns the new sorted array.
* @example
*
- * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ * var users = [
+ * { 'user': 'fred', 'age': 48 },
+ * { 'user': 'barney', 'age': 36 },
+ * { 'user': 'fred', 'age': 40 },
+ * { 'user': 'barney', 'age': 34 }
+ * ];
*
- * _.uniqWith(objects, _.isEqual);
- * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
+ * _.sortBy(users, [function(o) { return o.user; }]);
+ * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
+ *
+ * _.sortBy(users, ['user', 'age']);
+ * // => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]
*/
- function uniqWith(array, comparator) {
- comparator = typeof comparator == 'function' ? comparator : undefined;
- return (array && array.length) ? baseUniq(array, undefined, comparator) : [];
- }
+ var sortBy = baseRest(function(collection, iteratees) {
+ if (collection == null) {
+ return [];
+ }
+ var length = iteratees.length;
+ if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
+ iteratees = [];
+ } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
+ iteratees = [iteratees[0]];
+ }
+ return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
+ });
+
+ /*------------------------------------------------------------------------*/
/**
- * This method is like `_.zip` except that it accepts an array of grouped
- * elements and creates an array regrouping the elements to their pre-zip
- * configuration.
+ * Gets the timestamp of the number of milliseconds that have elapsed since
+ * the Unix epoch (1 January 1970 00:00:00 UTC).
*
* @static
* @memberOf _
- * @since 1.2.0
- * @category Array
- * @param {Array} array The array of grouped elements to process.
- * @returns {Array} Returns the new array of regrouped elements.
+ * @since 2.4.0
+ * @category Date
+ * @returns {number} Returns the timestamp.
* @example
*
- * var zipped = _.zip(['a', 'b'], [1, 2], [true, false]);
- * // => [['a', 1, true], ['b', 2, false]]
- *
- * _.unzip(zipped);
- * // => [['a', 'b'], [1, 2], [true, false]]
+ * _.defer(function(stamp) {
+ * console.log(_.now() - stamp);
+ * }, _.now());
+ * // => Logs the number of milliseconds it took for the deferred invocation.
*/
- function unzip(array) {
- if (!(array && array.length)) {
- return [];
- }
- var length = 0;
- array = arrayFilter(array, function(group) {
- if (isArrayLikeObject(group)) {
- length = nativeMax(group.length, length);
- return true;
- }
- });
- return baseTimes(length, function(index) {
- return arrayMap(array, baseProperty(index));
- });
- }
+ var now = ctxNow || function() {
+ return root.Date.now();
+ };
+
+ /*------------------------------------------------------------------------*/
/**
- * This method is like `_.unzip` except that it accepts `iteratee` to specify
- * how regrouped values should be combined. The iteratee is invoked with the
- * elements of each group: (...group).
+ * The opposite of `_.before`; this method creates a function that invokes
+ * `func` once it's called `n` or more times.
*
* @static
* @memberOf _
- * @since 3.8.0
- * @category Array
- * @param {Array} array The array of grouped elements to process.
- * @param {Function} [iteratee=_.identity] The function to combine
- * regrouped values.
- * @returns {Array} Returns the new array of regrouped elements.
+ * @since 0.1.0
+ * @category Function
+ * @param {number} n The number of calls before `func` is invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
* @example
*
- * var zipped = _.zip([1, 2], [10, 20], [100, 200]);
- * // => [[1, 10, 100], [2, 20, 200]]
+ * var saves = ['profile', 'settings'];
*
- * _.unzipWith(zipped, _.add);
- * // => [3, 30, 300]
+ * var done = _.after(saves.length, function() {
+ * console.log('done saving!');
+ * });
+ *
+ * _.forEach(saves, function(type) {
+ * asyncSave({ 'type': type, 'complete': done });
+ * });
+ * // => Logs 'done saving!' after the two async saves have completed.
*/
- function unzipWith(array, iteratee) {
- if (!(array && array.length)) {
- return [];
- }
- var result = unzip(array);
- if (iteratee == null) {
- return result;
+ function after(n, func) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
}
- return arrayMap(result, function(group) {
- return apply(iteratee, undefined, group);
- });
+ n = toInteger(n);
+ return function() {
+ if (--n < 1) {
+ return func.apply(this, arguments);
+ }
+ };
}
/**
- * Creates an array excluding all given values using
- * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * for equality comparisons.
- *
- * **Note:** Unlike `_.pull`, this method returns a new array.
+ * Creates a function that invokes `func`, with up to `n` arguments,
+ * ignoring any additional arguments.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {Array} array The array to inspect.
- * @param {...*} [values] The values to exclude.
- * @returns {Array} Returns the new array of filtered values.
- * @see _.difference, _.xor
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} func The function to cap arguments for.
+ * @param {number} [n=func.length] The arity cap.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the new capped function.
* @example
*
- * _.without([2, 1, 2, 3], 1, 2);
- * // => [3]
+ * _.map(['6', '8', '10'], _.ary(parseInt, 1));
+ * // => [6, 8, 10]
*/
- var without = baseRest(function(array, values) {
- return isArrayLikeObject(array)
- ? baseDifference(array, values)
- : [];
- });
+ function ary(func, n, guard) {
+ n = guard ? undefined : n;
+ n = (func && n == null) ? func.length : n;
+ return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);
+ }
/**
- * Creates an array of unique values that is the
- * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference)
- * of the given arrays. The order of result values is determined by the order
- * they occur in the arrays.
+ * Creates a function that invokes `func`, with the `this` binding and arguments
+ * of the created function, while it's called less than `n` times. Subsequent
+ * calls to the created function return the result of the last `func` invocation.
*
* @static
* @memberOf _
- * @since 2.4.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @returns {Array} Returns the new array of filtered values.
- * @see _.difference, _.without
+ * @since 3.0.0
+ * @category Function
+ * @param {number} n The number of calls at which `func` is no longer invoked.
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
* @example
*
- * _.xor([2, 1], [2, 3]);
- * // => [1, 3]
+ * jQuery(element).on('click', _.before(5, addContactToList));
+ * // => Allows adding up to 4 contacts to the list.
*/
- var xor = baseRest(function(arrays) {
- return baseXor(arrayFilter(arrays, isArrayLikeObject));
- });
+ function before(n, func) {
+ var result;
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ n = toInteger(n);
+ return function() {
+ if (--n > 0) {
+ result = func.apply(this, arguments);
+ }
+ if (n <= 1) {
+ func = undefined;
+ }
+ return result;
+ };
+ }
/**
- * This method is like `_.xor` except that it accepts `iteratee` which is
- * invoked for each element of each `arrays` to generate the criterion by
- * which by which they're compared. The order of result values is determined
- * by the order they occur in the arrays. The iteratee is invoked with one
- * argument: (value).
+ * Creates a function that invokes `func` with the `this` binding of `thisArg`
+ * and `partials` prepended to the arguments it receives.
+ *
+ * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
+ * property of bound functions.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to bind.
+ * @param {*} thisArg The `this` binding of `func`.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
* @example
*
- * _.xorBy([2.1, 1.2], [2.3, 3.4], Math.floor);
- * // => [1.2, 3.4]
+ * function greet(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * }
*
- * // The `_.property` iteratee shorthand.
- * _.xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x');
- * // => [{ 'x': 2 }]
+ * var object = { 'user': 'fred' };
+ *
+ * var bound = _.bind(greet, object, 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * // Bound with placeholders.
+ * var bound = _.bind(greet, object, _, '!');
+ * bound('hi');
+ * // => 'hi fred!'
*/
- var xorBy = baseRest(function(arrays) {
- var iteratee = last(arrays);
- if (isArrayLikeObject(iteratee)) {
- iteratee = undefined;
+ var bind = baseRest(function(func, thisArg, partials) {
+ var bitmask = WRAP_BIND_FLAG;
+ if (partials.length) {
+ var holders = replaceHolders(partials, getHolder(bind));
+ bitmask |= WRAP_PARTIAL_FLAG;
}
- return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee, 2));
+ return createWrap(func, bitmask, thisArg, partials, holders);
});
/**
- * This method is like `_.xor` except that it accepts `comparator` which is
- * invoked to compare elements of `arrays`. The order of result values is
- * determined by the order they occur in the arrays. The comparator is invoked
- * with two arguments: (arrVal, othVal).
+ * Creates a function that invokes the method at `object[key]` with `partials`
+ * prepended to the arguments it receives.
+ *
+ * This method differs from `_.bind` by allowing bound functions to reference
+ * methods that may be redefined or don't yet exist. See
+ * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
+ * for more details.
+ *
+ * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Array
- * @param {...Array} [arrays] The arrays to inspect.
- * @param {Function} [comparator] The comparator invoked per element.
- * @returns {Array} Returns the new array of filtered values.
+ * @since 0.10.0
+ * @category Function
+ * @param {Object} object The object to invoke the method on.
+ * @param {string} key The key of the method.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new bound function.
* @example
*
- * var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
- * var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
+ * var object = {
+ * 'user': 'fred',
+ * 'greet': function(greeting, punctuation) {
+ * return greeting + ' ' + this.user + punctuation;
+ * }
+ * };
*
- * _.xorWith(objects, others, _.isEqual);
- * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
+ * var bound = _.bindKey(object, 'greet', 'hi');
+ * bound('!');
+ * // => 'hi fred!'
+ *
+ * object.greet = function(greeting, punctuation) {
+ * return greeting + 'ya ' + this.user + punctuation;
+ * };
+ *
+ * bound('!');
+ * // => 'hiya fred!'
+ *
+ * // Bound with placeholders.
+ * var bound = _.bindKey(object, 'greet', _, '!');
+ * bound('hi');
+ * // => 'hiya fred!'
*/
- var xorWith = baseRest(function(arrays) {
- var comparator = last(arrays);
- comparator = typeof comparator == 'function' ? comparator : undefined;
- return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
+ var bindKey = baseRest(function(object, key, partials) {
+ var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;
+ if (partials.length) {
+ var holders = replaceHolders(partials, getHolder(bindKey));
+ bitmask |= WRAP_PARTIAL_FLAG;
+ }
+ return createWrap(key, bitmask, object, partials, holders);
});
/**
- * Creates an array of grouped elements, the first of which contains the
- * first elements of the given arrays, the second of which contains the
- * second elements of the given arrays, and so on.
+ * Creates a function that accepts arguments of `func` and either invokes
+ * `func` returning its result, if at least `arity` number of arguments have
+ * been provided, or returns a function that accepts the remaining `func`
+ * arguments, and so on. The arity of `func` may be specified if `func.length`
+ * is not sufficient.
+ *
+ * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
+ * may be used as a placeholder for provided arguments.
+ *
+ * **Note:** This method doesn't set the "length" property of curried functions.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Array
- * @param {...Array} [arrays] The arrays to process.
- * @returns {Array} Returns the new array of grouped elements.
+ * @since 2.0.0
+ * @category Function
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the new curried function.
* @example
*
- * _.zip(['a', 'b'], [1, 2], [true, false]);
- * // => [['a', 1, true], ['b', 2, false]]
+ * var abc = function(a, b, c) {
+ * return [a, b, c];
+ * };
+ *
+ * var curried = _.curry(abc);
+ *
+ * curried(1)(2)(3);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2)(3);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2, 3);
+ * // => [1, 2, 3]
+ *
+ * // Curried with placeholders.
+ * curried(1)(_, 3)(2);
+ * // => [1, 2, 3]
*/
- var zip = baseRest(unzip);
+ function curry(func, arity, guard) {
+ arity = guard ? undefined : arity;
+ var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
+ result.placeholder = curry.placeholder;
+ return result;
+ }
/**
- * This method is like `_.fromPairs` except that it accepts two arrays,
- * one of property identifiers and one of corresponding values.
+ * This method is like `_.curry` except that arguments are applied to `func`
+ * in the manner of `_.partialRight` instead of `_.partial`.
+ *
+ * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for provided arguments.
+ *
+ * **Note:** This method doesn't set the "length" property of curried functions.
*
* @static
* @memberOf _
- * @since 0.4.0
- * @category Array
- * @param {Array} [props=[]] The property identifiers.
- * @param {Array} [values=[]] The property values.
- * @returns {Object} Returns the new object.
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} func The function to curry.
+ * @param {number} [arity=func.length] The arity of `func`.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the new curried function.
* @example
*
- * _.zipObject(['a', 'b'], [1, 2]);
- * // => { 'a': 1, 'b': 2 }
+ * var abc = function(a, b, c) {
+ * return [a, b, c];
+ * };
+ *
+ * var curried = _.curryRight(abc);
+ *
+ * curried(3)(2)(1);
+ * // => [1, 2, 3]
+ *
+ * curried(2, 3)(1);
+ * // => [1, 2, 3]
+ *
+ * curried(1, 2, 3);
+ * // => [1, 2, 3]
+ *
+ * // Curried with placeholders.
+ * curried(3)(1, _)(2);
+ * // => [1, 2, 3]
*/
- function zipObject(props, values) {
- return baseZipObject(props || [], values || [], assignValue);
+ function curryRight(func, arity, guard) {
+ arity = guard ? undefined : arity;
+ var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
+ result.placeholder = curryRight.placeholder;
+ return result;
}
/**
- * This method is like `_.zipObject` except that it supports property paths.
+ * Creates a debounced function that delays invoking `func` until after `wait`
+ * milliseconds have elapsed since the last time the debounced function was
+ * invoked. The debounced function comes with a `cancel` method to cancel
+ * delayed `func` invocations and a `flush` method to immediately invoke them.
+ * Provide `options` to indicate whether `func` should be invoked on the
+ * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
+ * with the last arguments provided to the debounced function. Subsequent
+ * calls to the debounced function return the result of the last `func`
+ * invocation.
+ *
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the debounced function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.debounce` and `_.throttle`.
*
* @static
* @memberOf _
- * @since 4.1.0
- * @category Array
- * @param {Array} [props=[]] The property identifiers.
- * @param {Array} [values=[]] The property values.
- * @returns {Object} Returns the new object.
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to debounce.
+ * @param {number} [wait=0] The number of milliseconds to delay.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=false]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {number} [options.maxWait]
+ * The maximum time `func` is allowed to be delayed before it's invoked.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new debounced function.
* @example
*
- * _.zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]);
- * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } }
+ * // Avoid costly calculations while the window size is in flux.
+ * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ *
+ * // Invoke `sendMail` when clicked, debouncing subsequent calls.
+ * jQuery(element).on('click', _.debounce(sendMail, 300, {
+ * 'leading': true,
+ * 'trailing': false
+ * }));
+ *
+ * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
+ * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
+ * var source = new EventSource('/stream');
+ * jQuery(source).on('message', debounced);
+ *
+ * // Cancel the trailing debounced invocation.
+ * jQuery(window).on('popstate', debounced.cancel);
*/
- function zipObjectDeep(props, values) {
- return baseZipObject(props || [], values || [], baseSet);
- }
+ function debounce(func, wait, options) {
+ var lastArgs,
+ lastThis,
+ maxWait,
+ result,
+ timerId,
+ lastCallTime,
+ lastInvokeTime = 0,
+ leading = false,
+ maxing = false,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ wait = toNumber(wait) || 0;
+ if (isObject(options)) {
+ leading = !!options.leading;
+ maxing = 'maxWait' in options;
+ maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+
+ function invokeFunc(time) {
+ var args = lastArgs,
+ thisArg = lastThis;
+
+ lastArgs = lastThis = undefined;
+ lastInvokeTime = time;
+ result = func.apply(thisArg, args);
+ return result;
+ }
+
+ function leadingEdge(time) {
+ // Reset any `maxWait` timer.
+ lastInvokeTime = time;
+ // Start the timer for the trailing edge.
+ timerId = setTimeout(timerExpired, wait);
+ // Invoke the leading edge.
+ return leading ? invokeFunc(time) : result;
+ }
+
+ function remainingWait(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime,
+ result = wait - timeSinceLastCall;
+
+ return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result;
+ }
+
+ function shouldInvoke(time) {
+ var timeSinceLastCall = time - lastCallTime,
+ timeSinceLastInvoke = time - lastInvokeTime;
+
+ // Either this is the first call, activity has stopped and we're at the
+ // trailing edge, the system time has gone backwards and we're treating
+ // it as the trailing edge, or we've hit the `maxWait` limit.
+ return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
+ (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
+ }
+
+ function timerExpired() {
+ var time = now();
+ if (shouldInvoke(time)) {
+ return trailingEdge(time);
+ }
+ // Restart the timer.
+ timerId = setTimeout(timerExpired, remainingWait(time));
+ }
+
+ function trailingEdge(time) {
+ timerId = undefined;
+
+ // Only invoke if we have `lastArgs` which means `func` has been
+ // debounced at least once.
+ if (trailing && lastArgs) {
+ return invokeFunc(time);
+ }
+ lastArgs = lastThis = undefined;
+ return result;
+ }
+
+ function cancel() {
+ if (timerId !== undefined) {
+ clearTimeout(timerId);
+ }
+ lastInvokeTime = 0;
+ lastArgs = lastCallTime = lastThis = timerId = undefined;
+ }
- /**
- * This method is like `_.zip` except that it accepts `iteratee` to specify
- * how grouped values should be combined. The iteratee is invoked with the
- * elements of each group: (...group).
- *
- * @static
- * @memberOf _
- * @since 3.8.0
- * @category Array
- * @param {...Array} [arrays] The arrays to process.
- * @param {Function} [iteratee=_.identity] The function to combine
- * grouped values.
- * @returns {Array} Returns the new array of grouped elements.
- * @example
- *
- * _.zipWith([1, 2], [10, 20], [100, 200], function(a, b, c) {
- * return a + b + c;
- * });
- * // => [111, 222]
- */
- var zipWith = baseRest(function(arrays) {
- var length = arrays.length,
- iteratee = length > 1 ? arrays[length - 1] : undefined;
+ function flush() {
+ return timerId === undefined ? result : trailingEdge(now());
+ }
- iteratee = typeof iteratee == 'function' ? (arrays.pop(), iteratee) : undefined;
- return unzipWith(arrays, iteratee);
- });
+ function debounced() {
+ var time = now(),
+ isInvoking = shouldInvoke(time);
- /*------------------------------------------------------------------------*/
+ lastArgs = arguments;
+ lastThis = this;
+ lastCallTime = time;
- /**
- * Creates a `lodash` wrapper instance that wraps `value` with explicit method
- * chain sequences enabled. The result of such sequences must be unwrapped
- * with `_#value`.
- *
- * @static
- * @memberOf _
- * @since 1.3.0
- * @category Seq
- * @param {*} value The value to wrap.
- * @returns {Object} Returns the new `lodash` wrapper instance.
- * @example
- *
- * var users = [
- * { 'user': 'barney', 'age': 36 },
- * { 'user': 'fred', 'age': 40 },
- * { 'user': 'pebbles', 'age': 1 }
- * ];
- *
- * var youngest = _
- * .chain(users)
- * .sortBy('age')
- * .map(function(o) {
- * return o.user + ' is ' + o.age;
- * })
- * .head()
- * .value();
- * // => 'pebbles is 1'
- */
- function chain(value) {
- var result = lodash(value);
- result.__chain__ = true;
- return result;
+ if (isInvoking) {
+ if (timerId === undefined) {
+ return leadingEdge(lastCallTime);
+ }
+ if (maxing) {
+ // Handle invocations in a tight loop.
+ timerId = setTimeout(timerExpired, wait);
+ return invokeFunc(lastCallTime);
+ }
+ }
+ if (timerId === undefined) {
+ timerId = setTimeout(timerExpired, wait);
+ }
+ return result;
+ }
+ debounced.cancel = cancel;
+ debounced.flush = flush;
+ return debounced;
}
/**
- * This method invokes `interceptor` and returns `value`. The interceptor
- * is invoked with one argument; (value). The purpose of this method is to
- * "tap into" a method chain sequence in order to modify intermediate results.
+ * Defers invoking the `func` until the current call stack has cleared. Any
+ * additional arguments are provided to `func` when it's invoked.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Seq
- * @param {*} value The value to provide to `interceptor`.
- * @param {Function} interceptor The function to invoke.
- * @returns {*} Returns `value`.
+ * @category Function
+ * @param {Function} func The function to defer.
+ * @param {...*} [args] The arguments to invoke `func` with.
+ * @returns {number} Returns the timer id.
* @example
*
- * _([1, 2, 3])
- * .tap(function(array) {
- * // Mutate input array.
- * array.pop();
- * })
- * .reverse()
- * .value();
- * // => [2, 1]
+ * _.defer(function(text) {
+ * console.log(text);
+ * }, 'deferred');
+ * // => Logs 'deferred' after one millisecond.
*/
- function tap(value, interceptor) {
- interceptor(value);
- return value;
- }
+ var defer = baseRest(function(func, args) {
+ return baseDelay(func, 1, args);
+ });
/**
- * This method is like `_.tap` except that it returns the result of `interceptor`.
- * The purpose of this method is to "pass thru" values replacing intermediate
- * results in a method chain sequence.
+ * Invokes `func` after `wait` milliseconds. Any additional arguments are
+ * provided to `func` when it's invoked.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Seq
- * @param {*} value The value to provide to `interceptor`.
- * @param {Function} interceptor The function to invoke.
- * @returns {*} Returns the result of `interceptor`.
- * @example
- *
- * _(' abc ')
- * .chain()
- * .trim()
- * .thru(function(value) {
- * return [value];
- * })
- * .value();
- * // => ['abc']
- */
- function thru(value, interceptor) {
- return interceptor(value);
- }
-
- /**
- * This method is the wrapper version of `_.at`.
- *
- * @name at
- * @memberOf _
- * @since 1.0.0
- * @category Seq
- * @param {...(string|string[])} [paths] The property paths to pick.
- * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to delay.
+ * @param {number} wait The number of milliseconds to delay invocation.
+ * @param {...*} [args] The arguments to invoke `func` with.
+ * @returns {number} Returns the timer id.
* @example
*
- * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
- *
- * _(object).at(['a[0].b.c', 'a[1]']).value();
- * // => [3, 4]
+ * _.delay(function(text) {
+ * console.log(text);
+ * }, 1000, 'later');
+ * // => Logs 'later' after one second.
*/
- var wrapperAt = flatRest(function(paths) {
- var length = paths.length,
- start = length ? paths[0] : 0,
- value = this.__wrapped__,
- interceptor = function(object) { return baseAt(object, paths); };
-
- if (length > 1 || this.__actions__.length ||
- !(value instanceof LazyWrapper) || !isIndex(start)) {
- return this.thru(interceptor);
- }
- value = value.slice(start, +start + (length ? 1 : 0));
- value.__actions__.push({
- 'func': thru,
- 'args': [interceptor],
- 'thisArg': undefined
- });
- return new LodashWrapper(value, this.__chain__).thru(function(array) {
- if (length && !array.length) {
- array.push(undefined);
- }
- return array;
- });
+ var delay = baseRest(function(func, wait, args) {
+ return baseDelay(func, toNumber(wait) || 0, args);
});
/**
- * Creates a `lodash` wrapper instance with explicit method chain sequences enabled.
+ * Creates a function that invokes `func` with arguments reversed.
*
- * @name chain
+ * @static
* @memberOf _
- * @since 0.1.0
- * @category Seq
- * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to flip arguments for.
+ * @returns {Function} Returns the new flipped function.
* @example
*
- * var users = [
- * { 'user': 'barney', 'age': 36 },
- * { 'user': 'fred', 'age': 40 }
- * ];
- *
- * // A sequence without explicit chaining.
- * _(users).head();
- * // => { 'user': 'barney', 'age': 36 }
+ * var flipped = _.flip(function() {
+ * return _.toArray(arguments);
+ * });
*
- * // A sequence with explicit chaining.
- * _(users)
- * .chain()
- * .head()
- * .pick('user')
- * .value();
- * // => { 'user': 'barney' }
+ * flipped('a', 'b', 'c', 'd');
+ * // => ['d', 'c', 'b', 'a']
*/
- function wrapperChain() {
- return chain(this);
+ function flip(func) {
+ return createWrap(func, WRAP_FLIP_FLAG);
}
/**
- * Executes the chain sequence and returns the wrapped result.
+ * Creates a function that memoizes the result of `func`. If `resolver` is
+ * provided, it determines the cache key for storing the result based on the
+ * arguments provided to the memoized function. By default, the first argument
+ * provided to the memoized function is used as the map cache key. The `func`
+ * is invoked with the `this` binding of the memoized function.
*
- * @name commit
+ * **Note:** The cache is exposed as the `cache` property on the memoized
+ * function. Its creation may be customized by replacing the `_.memoize.Cache`
+ * constructor with one whose instances implement the
+ * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
+ * method interface of `clear`, `delete`, `get`, `has`, and `set`.
+ *
+ * @static
* @memberOf _
- * @since 3.2.0
- * @category Seq
- * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to have its output memoized.
+ * @param {Function} [resolver] The function to resolve the cache key.
+ * @returns {Function} Returns the new memoized function.
* @example
*
- * var array = [1, 2];
- * var wrapped = _(array).push(3);
+ * var object = { 'a': 1, 'b': 2 };
+ * var other = { 'c': 3, 'd': 4 };
*
- * console.log(array);
+ * var values = _.memoize(_.values);
+ * values(object);
* // => [1, 2]
*
- * wrapped = wrapped.commit();
- * console.log(array);
- * // => [1, 2, 3]
+ * values(other);
+ * // => [3, 4]
*
- * wrapped.last();
- * // => 3
+ * object.a = 2;
+ * values(object);
+ * // => [1, 2]
*
- * console.log(array);
- * // => [1, 2, 3]
+ * // Modify the result cache.
+ * values.cache.set(object, ['a', 'b']);
+ * values(object);
+ * // => ['a', 'b']
+ *
+ * // Replace `_.memoize.Cache`.
+ * _.memoize.Cache = WeakMap;
*/
- function wrapperCommit() {
- return new LodashWrapper(this.value(), this.__chain__);
+ function memoize(func, resolver) {
+ if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ var memoized = function() {
+ var args = arguments,
+ key = resolver ? resolver.apply(this, args) : args[0],
+ cache = memoized.cache;
+
+ if (cache.has(key)) {
+ return cache.get(key);
+ }
+ var result = func.apply(this, args);
+ memoized.cache = cache.set(key, result) || cache;
+ return result;
+ };
+ memoized.cache = new (memoize.Cache || MapCache);
+ return memoized;
}
+ // Expose `MapCache`.
+ memoize.Cache = MapCache;
+
/**
- * Gets the next value on a wrapped object following the
- * [iterator protocol](https://mdn.io/iteration_protocols#iterator).
+ * Creates a function that negates the result of the predicate `func`. The
+ * `func` predicate is invoked with the `this` binding and arguments of the
+ * created function.
*
- * @name next
+ * @static
* @memberOf _
- * @since 4.0.0
- * @category Seq
- * @returns {Object} Returns the next iterator value.
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} predicate The predicate to negate.
+ * @returns {Function} Returns the new negated function.
* @example
*
- * var wrapped = _([1, 2]);
- *
- * wrapped.next();
- * // => { 'done': false, 'value': 1 }
- *
- * wrapped.next();
- * // => { 'done': false, 'value': 2 }
+ * function isEven(n) {
+ * return n % 2 == 0;
+ * }
*
- * wrapped.next();
- * // => { 'done': true, 'value': undefined }
+ * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+ * // => [1, 3, 5]
*/
- function wrapperNext() {
- if (this.__values__ === undefined) {
- this.__values__ = toArray(this.value());
+ function negate(predicate) {
+ if (typeof predicate != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
}
- var done = this.__index__ >= this.__values__.length,
- value = done ? undefined : this.__values__[this.__index__++];
-
- return { 'done': done, 'value': value };
+ return function() {
+ var args = arguments;
+ switch (args.length) {
+ case 0: return !predicate.call(this);
+ case 1: return !predicate.call(this, args[0]);
+ case 2: return !predicate.call(this, args[0], args[1]);
+ case 3: return !predicate.call(this, args[0], args[1], args[2]);
+ }
+ return !predicate.apply(this, args);
+ };
}
/**
- * Enables the wrapper to be iterable.
+ * Creates a function that is restricted to invoking `func` once. Repeat calls
+ * to the function return the value of the first invocation. The `func` is
+ * invoked with the `this` binding and arguments of the created function.
*
- * @name Symbol.iterator
+ * @static
* @memberOf _
- * @since 4.0.0
- * @category Seq
- * @returns {Object} Returns the wrapper object.
+ * @since 0.1.0
+ * @category Function
+ * @param {Function} func The function to restrict.
+ * @returns {Function} Returns the new restricted function.
* @example
*
- * var wrapped = _([1, 2]);
- *
- * wrapped[Symbol.iterator]() === wrapped;
- * // => true
- *
- * Array.from(wrapped);
- * // => [1, 2]
+ * var initialize = _.once(createApplication);
+ * initialize();
+ * initialize();
+ * // => `createApplication` is invoked once
*/
- function wrapperToIterator() {
- return this;
+ function once(func) {
+ return before(2, func);
}
/**
- * Creates a clone of the chain sequence planting `value` as the wrapped value.
+ * Creates a function that invokes `func` with its arguments transformed.
*
- * @name plant
+ * @static
+ * @since 4.0.0
* @memberOf _
- * @since 3.2.0
- * @category Seq
- * @param {*} value The value to plant.
- * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @category Function
+ * @param {Function} func The function to wrap.
+ * @param {...(Function|Function[])} [transforms=[_.identity]]
+ * The argument transforms.
+ * @returns {Function} Returns the new function.
* @example
*
+ * function doubled(n) {
+ * return n * 2;
+ * }
+ *
* function square(n) {
* return n * n;
* }
*
- * var wrapped = _([1, 2]).map(square);
- * var other = wrapped.plant([3, 4]);
+ * var func = _.overArgs(function(x, y) {
+ * return [x, y];
+ * }, [square, doubled]);
*
- * other.value();
- * // => [9, 16]
+ * func(9, 3);
+ * // => [81, 6]
*
- * wrapped.value();
- * // => [1, 4]
+ * func(10, 5);
+ * // => [100, 10]
*/
- function wrapperPlant(value) {
- var result,
- parent = this;
+ var overArgs = castRest(function(func, transforms) {
+ transforms = (transforms.length == 1 && isArray(transforms[0]))
+ ? arrayMap(transforms[0], baseUnary(getIteratee()))
+ : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));
- while (parent instanceof baseLodash) {
- var clone = wrapperClone(parent);
- clone.__index__ = 0;
- clone.__values__ = undefined;
- if (result) {
- previous.__wrapped__ = clone;
- } else {
- result = clone;
+ var funcsLength = transforms.length;
+ return baseRest(function(args) {
+ var index = -1,
+ length = nativeMin(args.length, funcsLength);
+
+ while (++index < length) {
+ args[index] = transforms[index].call(this, args[index]);
}
- var previous = clone;
- parent = parent.__wrapped__;
- }
- previous.__wrapped__ = value;
- return result;
- }
+ return apply(func, this, args);
+ });
+ });
/**
- * This method is the wrapper version of `_.reverse`.
+ * Creates a function that invokes `func` with `partials` prepended to the
+ * arguments it receives. This method is like `_.bind` except it does **not**
+ * alter the `this` binding.
*
- * **Note:** This method mutates the wrapped array.
+ * The `_.partial.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
*
- * @name reverse
+ * **Note:** This method doesn't set the "length" property of partially
+ * applied functions.
+ *
+ * @static
* @memberOf _
- * @since 0.1.0
- * @category Seq
- * @returns {Object} Returns the new `lodash` wrapper instance.
+ * @since 0.2.0
+ * @category Function
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
* @example
*
- * var array = [1, 2, 3];
+ * function greet(greeting, name) {
+ * return greeting + ' ' + name;
+ * }
*
- * _(array).reverse().value()
- * // => [3, 2, 1]
+ * var sayHelloTo = _.partial(greet, 'hello');
+ * sayHelloTo('fred');
+ * // => 'hello fred'
*
- * console.log(array);
- * // => [3, 2, 1]
+ * // Partially applied with placeholders.
+ * var greetFred = _.partial(greet, _, 'fred');
+ * greetFred('hi');
+ * // => 'hi fred'
*/
- function wrapperReverse() {
- var value = this.__wrapped__;
- if (value instanceof LazyWrapper) {
- var wrapped = value;
- if (this.__actions__.length) {
- wrapped = new LazyWrapper(this);
- }
- wrapped = wrapped.reverse();
- wrapped.__actions__.push({
- 'func': thru,
- 'args': [reverse],
- 'thisArg': undefined
- });
- return new LodashWrapper(wrapped, this.__chain__);
- }
- return this.thru(reverse);
- }
+ var partial = baseRest(function(func, partials) {
+ var holders = replaceHolders(partials, getHolder(partial));
+ return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);
+ });
+
+ /**
+ * This method is like `_.partial` except that partially applied arguments
+ * are appended to the arguments it receives.
+ *
+ * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
+ * builds, may be used as a placeholder for partially applied arguments.
+ *
+ * **Note:** This method doesn't set the "length" property of partially
+ * applied functions.
+ *
+ * @static
+ * @memberOf _
+ * @since 1.0.0
+ * @category Function
+ * @param {Function} func The function to partially apply arguments to.
+ * @param {...*} [partials] The arguments to be partially applied.
+ * @returns {Function} Returns the new partially applied function.
+ * @example
+ *
+ * function greet(greeting, name) {
+ * return greeting + ' ' + name;
+ * }
+ *
+ * var greetFred = _.partialRight(greet, 'fred');
+ * greetFred('hi');
+ * // => 'hi fred'
+ *
+ * // Partially applied with placeholders.
+ * var sayHelloTo = _.partialRight(greet, 'hello', _);
+ * sayHelloTo('fred');
+ * // => 'hello fred'
+ */
+ var partialRight = baseRest(function(func, partials) {
+ var holders = replaceHolders(partials, getHolder(partialRight));
+ return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);
+ });
/**
- * Executes the chain sequence to resolve the unwrapped value.
+ * Creates a function that invokes `func` with arguments arranged according
+ * to the specified `indexes` where the argument value at the first index is
+ * provided as the first argument, the argument value at the second index is
+ * provided as the second argument, and so on.
*
- * @name value
+ * @static
* @memberOf _
- * @since 0.1.0
- * @alias toJSON, valueOf
- * @category Seq
- * @returns {*} Returns the resolved unwrapped value.
+ * @since 3.0.0
+ * @category Function
+ * @param {Function} func The function to rearrange arguments for.
+ * @param {...(number|number[])} indexes The arranged argument indexes.
+ * @returns {Function} Returns the new function.
* @example
*
- * _([1, 2, 3]).value();
- * // => [1, 2, 3]
+ * var rearged = _.rearg(function(a, b, c) {
+ * return [a, b, c];
+ * }, [2, 0, 1]);
+ *
+ * rearged('b', 'c', 'a')
+ * // => ['a', 'b', 'c']
*/
- function wrapperValue() {
- return baseWrapperValue(this.__wrapped__, this.__actions__);
- }
-
- /*------------------------------------------------------------------------*/
+ var rearg = flatRest(function(func, indexes) {
+ return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);
+ });
/**
- * Creates an object composed of keys generated from the results of running
- * each element of `collection` thru `iteratee`. The corresponding value of
- * each key is the number of times the key was returned by `iteratee`. The
- * iteratee is invoked with one argument: (value).
+ * Creates a function that invokes `func` with the `this` binding of the
+ * created function and arguments from `start` and beyond provided as
+ * an array.
+ *
+ * **Note:** This method is based on the
+ * [rest parameter](https://mdn.io/rest_parameters).
*
* @static
* @memberOf _
- * @since 0.5.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
- * @returns {Object} Returns the composed aggregate object.
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to apply a rest parameter to.
+ * @param {number} [start=func.length-1] The start position of the rest parameter.
+ * @returns {Function} Returns the new function.
* @example
*
- * _.countBy([6.1, 4.2, 6.3], Math.floor);
- * // => { '4': 1, '6': 2 }
+ * var say = _.rest(function(what, names) {
+ * return what + ' ' + _.initial(names).join(', ') +
+ * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
+ * });
*
- * // The `_.property` iteratee shorthand.
- * _.countBy(['one', 'two', 'three'], 'length');
- * // => { '3': 2, '5': 1 }
+ * say('hello', 'fred', 'barney', 'pebbles');
+ * // => 'hello fred, barney, & pebbles'
*/
- var countBy = createAggregator(function(result, value, key) {
- if (hasOwnProperty.call(result, key)) {
- ++result[key];
- } else {
- baseAssignValue(result, key, 1);
+ function rest(func, start) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
}
- });
+ start = start === undefined ? start : toInteger(start);
+ return baseRest(func, start);
+ }
/**
- * Checks if `predicate` returns truthy for **all** elements of `collection`.
- * Iteration is stopped once `predicate` returns falsey. The predicate is
- * invoked with three arguments: (value, index|key, collection).
+ * Creates a function that invokes `func` with the `this` binding of the
+ * create function and an array of arguments much like
+ * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).
*
- * **Note:** This method returns `true` for
- * [empty collections](https://en.wikipedia.org/wiki/Empty_set) because
- * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of
- * elements of empty collections.
+ * **Note:** This method is based on the
+ * [spread operator](https://mdn.io/spread_operator).
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {boolean} Returns `true` if all elements pass the predicate check,
- * else `false`.
+ * @since 3.2.0
+ * @category Function
+ * @param {Function} func The function to spread arguments over.
+ * @param {number} [start=0] The start position of the spread.
+ * @returns {Function} Returns the new function.
* @example
*
- * _.every([true, 1, null, 'yes'], Boolean);
- * // => false
- *
- * var users = [
- * { 'user': 'barney', 'age': 36, 'active': false },
- * { 'user': 'fred', 'age': 40, 'active': false }
- * ];
+ * var say = _.spread(function(who, what) {
+ * return who + ' says ' + what;
+ * });
*
- * // The `_.matches` iteratee shorthand.
- * _.every(users, { 'user': 'barney', 'active': false });
- * // => false
+ * say(['fred', 'hello']);
+ * // => 'fred says hello'
*
- * // The `_.matchesProperty` iteratee shorthand.
- * _.every(users, ['active', false]);
- * // => true
+ * var numbers = Promise.all([
+ * Promise.resolve(40),
+ * Promise.resolve(36)
+ * ]);
*
- * // The `_.property` iteratee shorthand.
- * _.every(users, 'active');
- * // => false
+ * numbers.then(_.spread(function(x, y) {
+ * return x + y;
+ * }));
+ * // => a Promise of 76
*/
- function every(collection, predicate, guard) {
- var func = isArray(collection) ? arrayEvery : baseEvery;
- if (guard && isIterateeCall(collection, predicate, guard)) {
- predicate = undefined;
+ function spread(func, start) {
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
}
- return func(collection, getIteratee(predicate, 3));
+ start = start == null ? 0 : nativeMax(toInteger(start), 0);
+ return baseRest(function(args) {
+ var array = args[start],
+ otherArgs = castSlice(args, 0, start);
+
+ if (array) {
+ arrayPush(otherArgs, array);
+ }
+ return apply(func, this, otherArgs);
+ });
}
/**
- * Iterates over elements of `collection`, returning an array of all elements
- * `predicate` returns truthy for. The predicate is invoked with three
- * arguments: (value, index|key, collection).
+ * Creates a throttled function that only invokes `func` at most once per
+ * every `wait` milliseconds. The throttled function comes with a `cancel`
+ * method to cancel delayed `func` invocations and a `flush` method to
+ * immediately invoke them. Provide `options` to indicate whether `func`
+ * should be invoked on the leading and/or trailing edge of the `wait`
+ * timeout. The `func` is invoked with the last arguments provided to the
+ * throttled function. Subsequent calls to the throttled function return the
+ * result of the last `func` invocation.
*
- * **Note:** Unlike `_.remove`, this method returns a new array.
+ * **Note:** If `leading` and `trailing` options are `true`, `func` is
+ * invoked on the trailing edge of the timeout only if the throttled function
+ * is invoked more than once during the `wait` timeout.
+ *
+ * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
+ * until to the next tick, similar to `setTimeout` with a timeout of `0`.
+ *
+ * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
+ * for details over the differences between `_.throttle` and `_.debounce`.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the new filtered array.
- * @see _.reject
+ * @category Function
+ * @param {Function} func The function to throttle.
+ * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
+ * @param {Object} [options={}] The options object.
+ * @param {boolean} [options.leading=true]
+ * Specify invoking on the leading edge of the timeout.
+ * @param {boolean} [options.trailing=true]
+ * Specify invoking on the trailing edge of the timeout.
+ * @returns {Function} Returns the new throttled function.
* @example
*
- * var users = [
- * { 'user': 'barney', 'age': 36, 'active': true },
- * { 'user': 'fred', 'age': 40, 'active': false }
- * ];
- *
- * _.filter(users, function(o) { return !o.active; });
- * // => objects for ['fred']
+ * // Avoid excessively updating the position while scrolling.
+ * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
*
- * // The `_.matches` iteratee shorthand.
- * _.filter(users, { 'age': 36, 'active': true });
- * // => objects for ['barney']
+ * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
+ * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
+ * jQuery(element).on('click', throttled);
*
- * // The `_.matchesProperty` iteratee shorthand.
- * _.filter(users, ['active', false]);
- * // => objects for ['fred']
+ * // Cancel the trailing throttled invocation.
+ * jQuery(window).on('popstate', throttled.cancel);
+ */
+ function throttle(func, wait, options) {
+ var leading = true,
+ trailing = true;
+
+ if (typeof func != 'function') {
+ throw new TypeError(FUNC_ERROR_TEXT);
+ }
+ if (isObject(options)) {
+ leading = 'leading' in options ? !!options.leading : leading;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
+ }
+ return debounce(func, wait, {
+ 'leading': leading,
+ 'maxWait': wait,
+ 'trailing': trailing
+ });
+ }
+
+ /**
+ * Creates a function that accepts up to one argument, ignoring any
+ * additional arguments.
*
- * // The `_.property` iteratee shorthand.
- * _.filter(users, 'active');
- * // => objects for ['barney']
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Function
+ * @param {Function} func The function to cap arguments for.
+ * @returns {Function} Returns the new capped function.
+ * @example
*
- * // Combining several predicates using `_.overEvery` or `_.overSome`.
- * _.filter(users, _.overSome([{ 'age': 36 }, ['age', 40]]));
- * // => objects for ['fred', 'barney']
+ * _.map(['6', '8', '10'], _.unary(parseInt));
+ * // => [6, 8, 10]
*/
- function filter(collection, predicate) {
- var func = isArray(collection) ? arrayFilter : baseFilter;
- return func(collection, getIteratee(predicate, 3));
+ function unary(func) {
+ return ary(func, 1);
}
/**
- * Iterates over elements of `collection`, returning the first element
- * `predicate` returns truthy for. The predicate is invoked with three
- * arguments: (value, index|key, collection).
+ * Creates a function that provides `value` to `wrapper` as its first
+ * argument. Any additional arguments provided to the function are appended
+ * to those provided to the `wrapper`. The wrapper is invoked with the `this`
+ * binding of the created function.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to inspect.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @param {number} [fromIndex=0] The index to search from.
- * @returns {*} Returns the matched element, else `undefined`.
+ * @category Function
+ * @param {*} value The value to wrap.
+ * @param {Function} [wrapper=identity] The wrapper function.
+ * @returns {Function} Returns the new function.
* @example
*
- * var users = [
- * { 'user': 'barney', 'age': 36, 'active': true },
- * { 'user': 'fred', 'age': 40, 'active': false },
- * { 'user': 'pebbles', 'age': 1, 'active': true }
- * ];
- *
- * _.find(users, function(o) { return o.age < 40; });
- * // => object for 'barney'
- *
- * // The `_.matches` iteratee shorthand.
- * _.find(users, { 'age': 1, 'active': true });
- * // => object for 'pebbles'
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.find(users, ['active', false]);
- * // => object for 'fred'
+ * var p = _.wrap(_.escape, function(func, text) {
+ * return '' + func(text) + '
';
+ * });
*
- * // The `_.property` iteratee shorthand.
- * _.find(users, 'active');
- * // => object for 'barney'
+ * p('fred, barney, & pebbles');
+ * // => 'fred, barney, & pebbles
'
*/
- var find = createFind(findIndex);
+ function wrap(value, wrapper) {
+ return partial(castFunction(wrapper), value);
+ }
+
+ /*------------------------------------------------------------------------*/
/**
- * This method is like `_.find` except that it iterates over elements of
- * `collection` from right to left.
+ * Casts `value` as an array if it's not one.
*
* @static
* @memberOf _
- * @since 2.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to inspect.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @param {number} [fromIndex=collection.length-1] The index to search from.
- * @returns {*} Returns the matched element, else `undefined`.
+ * @since 4.4.0
+ * @category Lang
+ * @param {*} value The value to inspect.
+ * @returns {Array} Returns the cast array.
* @example
*
- * _.findLast([1, 2, 3, 4], function(n) {
- * return n % 2 == 1;
- * });
- * // => 3
- */
- var findLast = createFind(findLastIndex);
-
- /**
- * Creates a flattened array of values by running each element in `collection`
- * thru `iteratee` and flattening the mapped results. The iteratee is invoked
- * with three arguments: (value, index|key, collection).
+ * _.castArray(1);
+ * // => [1]
*
- * @static
- * @memberOf _
- * @since 4.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the new flattened array.
- * @example
+ * _.castArray({ 'a': 1 });
+ * // => [{ 'a': 1 }]
*
- * function duplicate(n) {
- * return [n, n];
- * }
+ * _.castArray('abc');
+ * // => ['abc']
*
- * _.flatMap([1, 2], duplicate);
- * // => [1, 1, 2, 2]
+ * _.castArray(null);
+ * // => [null]
+ *
+ * _.castArray(undefined);
+ * // => [undefined]
+ *
+ * _.castArray();
+ * // => []
+ *
+ * var array = [1, 2, 3];
+ * console.log(_.castArray(array) === array);
+ * // => true
*/
- function flatMap(collection, iteratee) {
- return baseFlatten(map(collection, iteratee), 1);
+ function castArray() {
+ if (!arguments.length) {
+ return [];
+ }
+ var value = arguments[0];
+ return isArray(value) ? value : [value];
}
/**
- * This method is like `_.flatMap` except that it recursively flattens the
- * mapped results.
+ * Creates a shallow clone of `value`.
+ *
+ * **Note:** This method is loosely based on the
+ * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
+ * and supports cloning arrays, array buffers, booleans, date objects, maps,
+ * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
+ * arrays. The own enumerable properties of `arguments` objects are cloned
+ * as plain objects. An empty object is returned for uncloneable values such
+ * as error objects, functions, DOM nodes, and WeakMaps.
*
* @static
* @memberOf _
- * @since 4.7.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the new flattened array.
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to clone.
+ * @returns {*} Returns the cloned value.
+ * @see _.cloneDeep
* @example
*
- * function duplicate(n) {
- * return [[[n, n]]];
- * }
+ * var objects = [{ 'a': 1 }, { 'b': 2 }];
*
- * _.flatMapDeep([1, 2], duplicate);
- * // => [1, 1, 2, 2]
+ * var shallow = _.clone(objects);
+ * console.log(shallow[0] === objects[0]);
+ * // => true
*/
- function flatMapDeep(collection, iteratee) {
- return baseFlatten(map(collection, iteratee), INFINITY);
+ function clone(value) {
+ return baseClone(value, CLONE_SYMBOLS_FLAG);
}
/**
- * This method is like `_.flatMap` except that it recursively flattens the
- * mapped results up to `depth` times.
+ * This method is like `_.clone` except that it accepts `customizer` which
+ * is invoked to produce the cloned value. If `customizer` returns `undefined`,
+ * cloning is handled by the method instead. The `customizer` is invoked with
+ * up to four arguments; (value [, index|key, object, stack]).
*
* @static
* @memberOf _
- * @since 4.7.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @param {number} [depth=1] The maximum recursion depth.
- * @returns {Array} Returns the new flattened array.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to clone.
+ * @param {Function} [customizer] The function to customize cloning.
+ * @returns {*} Returns the cloned value.
+ * @see _.cloneDeepWith
* @example
*
- * function duplicate(n) {
- * return [[[n, n]]];
+ * function customizer(value) {
+ * if (_.isElement(value)) {
+ * return value.cloneNode(false);
+ * }
* }
*
- * _.flatMapDepth([1, 2], duplicate, 2);
- * // => [[1, 1], [2, 2]]
+ * var el = _.cloneWith(document.body, customizer);
+ *
+ * console.log(el === document.body);
+ * // => false
+ * console.log(el.nodeName);
+ * // => 'BODY'
+ * console.log(el.childNodes.length);
+ * // => 0
*/
- function flatMapDepth(collection, iteratee, depth) {
- depth = depth === undefined ? 1 : toInteger(depth);
- return baseFlatten(map(collection, iteratee), depth);
+ function cloneWith(value, customizer) {
+ customizer = typeof customizer == 'function' ? customizer : undefined;
+ return baseClone(value, CLONE_SYMBOLS_FLAG, customizer);
}
/**
- * Iterates over elements of `collection` and invokes `iteratee` for each element.
- * The iteratee is invoked with three arguments: (value, index|key, collection).
- * Iteratee functions may exit iteration early by explicitly returning `false`.
- *
- * **Note:** As with other "Collections" methods, objects with a "length"
- * property are iterated like arrays. To avoid this behavior use `_.forIn`
- * or `_.forOwn` for object iteration.
+ * This method is like `_.clone` except that it recursively clones `value`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @alias each
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @returns {Array|Object} Returns `collection`.
- * @see _.forEachRight
+ * @since 1.0.0
+ * @category Lang
+ * @param {*} value The value to recursively clone.
+ * @returns {*} Returns the deep cloned value.
+ * @see _.clone
* @example
*
- * _.forEach([1, 2], function(value) {
- * console.log(value);
- * });
- * // => Logs `1` then `2`.
+ * var objects = [{ 'a': 1 }, { 'b': 2 }];
*
- * _.forEach({ 'a': 1, 'b': 2 }, function(value, key) {
- * console.log(key);
- * });
- * // => Logs 'a' then 'b' (iteration order is not guaranteed).
+ * var deep = _.cloneDeep(objects);
+ * console.log(deep[0] === objects[0]);
+ * // => false
*/
- function forEach(collection, iteratee) {
- var func = isArray(collection) ? arrayEach : baseEach;
- return func(collection, getIteratee(iteratee, 3));
+ function cloneDeep(value) {
+ return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
}
/**
- * This method is like `_.forEach` except that it iterates over elements of
- * `collection` from right to left.
+ * This method is like `_.cloneWith` except that it recursively clones `value`.
*
* @static
* @memberOf _
- * @since 2.0.0
- * @alias eachRight
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @returns {Array|Object} Returns `collection`.
- * @see _.forEach
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to recursively clone.
+ * @param {Function} [customizer] The function to customize cloning.
+ * @returns {*} Returns the deep cloned value.
+ * @see _.cloneWith
* @example
*
- * _.forEachRight([1, 2], function(value) {
- * console.log(value);
- * });
- * // => Logs `2` then `1`.
+ * function customizer(value) {
+ * if (_.isElement(value)) {
+ * return value.cloneNode(true);
+ * }
+ * }
+ *
+ * var el = _.cloneDeepWith(document.body, customizer);
+ *
+ * console.log(el === document.body);
+ * // => false
+ * console.log(el.nodeName);
+ * // => 'BODY'
+ * console.log(el.childNodes.length);
+ * // => 20
*/
- function forEachRight(collection, iteratee) {
- var func = isArray(collection) ? arrayEachRight : baseEachRight;
- return func(collection, getIteratee(iteratee, 3));
+ function cloneDeepWith(value, customizer) {
+ customizer = typeof customizer == 'function' ? customizer : undefined;
+ return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer);
}
/**
- * Creates an object composed of keys generated from the results of running
- * each element of `collection` thru `iteratee`. The order of grouped values
- * is determined by the order they occur in `collection`. The corresponding
- * value of each key is an array of elements responsible for generating the
- * key. The iteratee is invoked with one argument: (value).
+ * Checks if `object` conforms to `source` by invoking the predicate
+ * properties of `source` with the corresponding property values of `object`.
+ *
+ * **Note:** This method is equivalent to `_.conforms` when `source` is
+ * partially applied.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
- * @returns {Object} Returns the composed aggregate object.
+ * @since 4.14.0
+ * @category Lang
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property predicates to conform to.
+ * @returns {boolean} Returns `true` if `object` conforms, else `false`.
* @example
*
- * _.groupBy([6.1, 4.2, 6.3], Math.floor);
- * // => { '4': [4.2], '6': [6.1, 6.3] }
+ * var object = { 'a': 1, 'b': 2 };
*
- * // The `_.property` iteratee shorthand.
- * _.groupBy(['one', 'two', 'three'], 'length');
- * // => { '3': ['one', 'two'], '5': ['three'] }
+ * _.conformsTo(object, { 'b': function(n) { return n > 1; } });
+ * // => true
+ *
+ * _.conformsTo(object, { 'b': function(n) { return n > 2; } });
+ * // => false
*/
- var groupBy = createAggregator(function(result, value, key) {
- if (hasOwnProperty.call(result, key)) {
- result[key].push(value);
- } else {
- baseAssignValue(result, key, [value]);
- }
- });
+ function conformsTo(object, source) {
+ return source == null || baseConformsTo(object, source, keys(source));
+ }
/**
- * Checks if `value` is in `collection`. If `collection` is a string, it's
- * checked for a substring of `value`, otherwise
+ * Performs a
* [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * is used for equality comparisons. If `fromIndex` is negative, it's used as
- * the offset from the end of `collection`.
+ * comparison between two values to determine if they are equivalent.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Collection
- * @param {Array|Object|string} collection The collection to inspect.
- * @param {*} value The value to search for.
- * @param {number} [fromIndex=0] The index to search from.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
- * @returns {boolean} Returns `true` if `value` is found, else `false`.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
- * _.includes([1, 2, 3], 1);
+ * var object = { 'a': 1 };
+ * var other = { 'a': 1 };
+ *
+ * _.eq(object, object);
* // => true
*
- * _.includes([1, 2, 3], 1, 2);
+ * _.eq(object, other);
* // => false
*
- * _.includes({ 'a': 1, 'b': 2 }, 1);
+ * _.eq('a', 'a');
* // => true
*
- * _.includes('abcd', 'bc');
+ * _.eq('a', Object('a'));
+ * // => false
+ *
+ * _.eq(NaN, NaN);
* // => true
*/
- function includes(collection, value, fromIndex, guard) {
- collection = isArrayLike(collection) ? collection : values(collection);
- fromIndex = (fromIndex && !guard) ? toInteger(fromIndex) : 0;
-
- var length = collection.length;
- if (fromIndex < 0) {
- fromIndex = nativeMax(length + fromIndex, 0);
- }
- return isString(collection)
- ? (fromIndex <= length && collection.indexOf(value, fromIndex) > -1)
- : (!!length && baseIndexOf(collection, value, fromIndex) > -1);
+ function eq(value, other) {
+ return value === other || (value !== value && other !== other);
}
/**
- * Invokes the method at `path` of each element in `collection`, returning
- * an array of the results of each invoked method. Any additional arguments
- * are provided to each invoked method. If `path` is a function, it's invoked
- * for, and `this` bound to, each element in `collection`.
+ * Checks if `value` is greater than `other`.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Array|Function|string} path The path of the method to invoke or
- * the function invoked per iteration.
- * @param {...*} [args] The arguments to invoke each method with.
- * @returns {Array} Returns the array of results.
+ * @since 3.9.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is greater than `other`,
+ * else `false`.
+ * @see _.lt
* @example
*
- * _.invokeMap([[5, 1, 7], [3, 2, 1]], 'sort');
- * // => [[1, 5, 7], [1, 2, 3]]
+ * _.gt(3, 1);
+ * // => true
*
- * _.invokeMap([123, 456], String.prototype.split, '');
- * // => [['1', '2', '3'], ['4', '5', '6']]
+ * _.gt(3, 3);
+ * // => false
+ *
+ * _.gt(1, 3);
+ * // => false
*/
- var invokeMap = baseRest(function(collection, path, args) {
- var index = -1,
- isFunc = typeof path == 'function',
- result = isArrayLike(collection) ? Array(collection.length) : [];
-
- baseEach(collection, function(value) {
- result[++index] = isFunc ? apply(path, value, args) : baseInvoke(value, path, args);
- });
- return result;
- });
+ var gt = createRelationalOperation(baseGt);
/**
- * Creates an object composed of keys generated from the results of running
- * each element of `collection` thru `iteratee`. The corresponding value of
- * each key is the last element responsible for generating the key. The
- * iteratee is invoked with one argument: (value).
+ * Checks if `value` is greater than or equal to `other`.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The iteratee to transform keys.
- * @returns {Object} Returns the composed aggregate object.
+ * @since 3.9.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is greater than or equal to
+ * `other`, else `false`.
+ * @see _.lte
* @example
*
- * var array = [
- * { 'dir': 'left', 'code': 97 },
- * { 'dir': 'right', 'code': 100 }
- * ];
+ * _.gte(3, 1);
+ * // => true
*
- * _.keyBy(array, function(o) {
- * return String.fromCharCode(o.code);
- * });
- * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+ * _.gte(3, 3);
+ * // => true
*
- * _.keyBy(array, 'dir');
- * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+ * _.gte(1, 3);
+ * // => false
*/
- var keyBy = createAggregator(function(result, value, key) {
- baseAssignValue(result, key, value);
+ var gte = createRelationalOperation(function(value, other) {
+ return value >= other;
});
/**
- * Creates an array of values by running each element in `collection` thru
- * `iteratee`. The iteratee is invoked with three arguments:
- * (value, index|key, collection).
- *
- * Many lodash methods are guarded to work as iteratees for methods like
- * `_.every`, `_.filter`, `_.map`, `_.mapValues`, `_.reject`, and `_.some`.
- *
- * The guarded methods are:
- * `ary`, `chunk`, `curry`, `curryRight`, `drop`, `dropRight`, `every`,
- * `fill`, `invert`, `parseInt`, `random`, `range`, `rangeRight`, `repeat`,
- * `sampleSize`, `slice`, `some`, `sortBy`, `split`, `take`, `takeRight`,
- * `template`, `trim`, `trimEnd`, `trimStart`, and `words`
+ * Checks if `value` is likely an `arguments` object.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the new mapped array.
- * @example
- *
- * function square(n) {
- * return n * n;
- * }
- *
- * _.map([4, 8], square);
- * // => [16, 64]
- *
- * _.map({ 'a': 4, 'b': 8 }, square);
- * // => [16, 64] (iteration order is not guaranteed)
- *
- * var users = [
- * { 'user': 'barney' },
- * { 'user': 'fred' }
- * ];
- *
- * // The `_.property` iteratee shorthand.
- * _.map(users, 'user');
- * // => ['barney', 'fred']
- */
- function map(collection, iteratee) {
- var func = isArray(collection) ? arrayMap : baseMap;
- return func(collection, getIteratee(iteratee, 3));
- }
-
- /**
- * This method is like `_.sortBy` except that it allows specifying the sort
- * orders of the iteratees to sort by. If `orders` is unspecified, all values
- * are sorted in ascending order. Otherwise, specify an order of "desc" for
- * descending or "asc" for ascending sort order of corresponding values.
- *
- * @static
- * @memberOf _
- * @since 4.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[_.identity]]
- * The iteratees to sort by.
- * @param {string[]} [orders] The sort orders of `iteratees`.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.reduce`.
- * @returns {Array} Returns the new sorted array.
- * @example
- *
- * var users = [
- * { 'user': 'fred', 'age': 48 },
- * { 'user': 'barney', 'age': 34 },
- * { 'user': 'fred', 'age': 40 },
- * { 'user': 'barney', 'age': 36 }
- * ];
- *
- * // Sort by `user` in ascending order and by `age` in descending order.
- * _.orderBy(users, ['user', 'age'], ['asc', 'desc']);
- * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]
- */
- function orderBy(collection, iteratees, orders, guard) {
- if (collection == null) {
- return [];
- }
- if (!isArray(iteratees)) {
- iteratees = iteratees == null ? [] : [iteratees];
- }
- orders = guard ? undefined : orders;
- if (!isArray(orders)) {
- orders = orders == null ? [] : [orders];
- }
- return baseOrderBy(collection, iteratees, orders);
- }
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+ * else `false`.
+ * @example
+ *
+ * _.isArguments(function() { return arguments; }());
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
+ return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
+ !propertyIsEnumerable.call(value, 'callee');
+ };
/**
- * Creates an array of elements split into two groups, the first of which
- * contains elements `predicate` returns truthy for, the second of which
- * contains elements `predicate` returns falsey for. The predicate is
- * invoked with one argument: (value).
+ * Checks if `value` is classified as an `Array` object.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the array of grouped elements.
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
- * var users = [
- * { 'user': 'barney', 'age': 36, 'active': false },
- * { 'user': 'fred', 'age': 40, 'active': true },
- * { 'user': 'pebbles', 'age': 1, 'active': false }
- * ];
- *
- * _.partition(users, function(o) { return o.active; });
- * // => objects for [['fred'], ['barney', 'pebbles']]
+ * _.isArray([1, 2, 3]);
+ * // => true
*
- * // The `_.matches` iteratee shorthand.
- * _.partition(users, { 'age': 1, 'active': false });
- * // => objects for [['pebbles'], ['barney', 'fred']]
+ * _.isArray(document.body.children);
+ * // => false
*
- * // The `_.matchesProperty` iteratee shorthand.
- * _.partition(users, ['active', false]);
- * // => objects for [['barney', 'pebbles'], ['fred']]
+ * _.isArray('abc');
+ * // => false
*
- * // The `_.property` iteratee shorthand.
- * _.partition(users, 'active');
- * // => objects for [['fred'], ['barney', 'pebbles']]
+ * _.isArray(_.noop);
+ * // => false
*/
- var partition = createAggregator(function(result, value, key) {
- result[key ? 0 : 1].push(value);
- }, function() { return [[], []]; });
+ var isArray = Array.isArray;
/**
- * Reduces `collection` to a value which is the accumulated result of running
- * each element in `collection` thru `iteratee`, where each successive
- * invocation is supplied the return value of the previous. If `accumulator`
- * is not given, the first element of `collection` is used as the initial
- * value. The iteratee is invoked with four arguments:
- * (accumulator, value, index|key, collection).
+ * Checks if `value` is classified as an `ArrayBuffer` object.
*
- * Many lodash methods are guarded to work as iteratees for methods like
- * `_.reduce`, `_.reduceRight`, and `_.transform`.
+ * @static
+ * @memberOf _
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
+ * @example
*
- * The guarded methods are:
- * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`,
- * and `sortBy`
+ * _.isArrayBuffer(new ArrayBuffer(2));
+ * // => true
+ *
+ * _.isArrayBuffer(new Array(2));
+ * // => false
+ */
+ var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer;
+
+ /**
+ * Checks if `value` is array-like. A value is considered array-like if it's
+ * not a function and has a `value.length` that's an integer greater than or
+ * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @param {*} [accumulator] The initial value.
- * @returns {*} Returns the accumulated value.
- * @see _.reduceRight
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
* @example
*
- * _.reduce([1, 2], function(sum, n) {
- * return sum + n;
- * }, 0);
- * // => 3
+ * _.isArrayLike([1, 2, 3]);
+ * // => true
*
- * _.reduce({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
- * (result[value] || (result[value] = [])).push(key);
- * return result;
- * }, {});
- * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed)
+ * _.isArrayLike(document.body.children);
+ * // => true
+ *
+ * _.isArrayLike('abc');
+ * // => true
+ *
+ * _.isArrayLike(_.noop);
+ * // => false
*/
- function reduce(collection, iteratee, accumulator) {
- var func = isArray(collection) ? arrayReduce : baseReduce,
- initAccum = arguments.length < 3;
-
- return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEach);
+ function isArrayLike(value) {
+ return value != null && isLength(value.length) && !isFunction(value);
}
/**
- * This method is like `_.reduce` except that it iterates over elements of
- * `collection` from right to left.
+ * This method is like `_.isArrayLike` except that it also checks if `value`
+ * is an object.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @param {*} [accumulator] The initial value.
- * @returns {*} Returns the accumulated value.
- * @see _.reduce
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an array-like object,
+ * else `false`.
* @example
*
- * var array = [[0, 1], [2, 3], [4, 5]];
+ * _.isArrayLikeObject([1, 2, 3]);
+ * // => true
*
- * _.reduceRight(array, function(flattened, other) {
- * return flattened.concat(other);
- * }, []);
- * // => [4, 5, 2, 3, 0, 1]
+ * _.isArrayLikeObject(document.body.children);
+ * // => true
+ *
+ * _.isArrayLikeObject('abc');
+ * // => false
+ *
+ * _.isArrayLikeObject(_.noop);
+ * // => false
*/
- function reduceRight(collection, iteratee, accumulator) {
- var func = isArray(collection) ? arrayReduceRight : baseReduce,
- initAccum = arguments.length < 3;
-
- return func(collection, getIteratee(iteratee, 4), accumulator, initAccum, baseEachRight);
+ function isArrayLikeObject(value) {
+ return isObjectLike(value) && isArrayLike(value);
}
/**
- * The opposite of `_.filter`; this method returns the elements of `collection`
- * that `predicate` does **not** return truthy for.
+ * Checks if `value` is classified as a boolean primitive or object.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {Array} Returns the new filtered array.
- * @see _.filter
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
* @example
*
- * var users = [
- * { 'user': 'barney', 'age': 36, 'active': false },
- * { 'user': 'fred', 'age': 40, 'active': true }
- * ];
- *
- * _.reject(users, function(o) { return !o.active; });
- * // => objects for ['fred']
- *
- * // The `_.matches` iteratee shorthand.
- * _.reject(users, { 'age': 40, 'active': true });
- * // => objects for ['barney']
- *
- * // The `_.matchesProperty` iteratee shorthand.
- * _.reject(users, ['active', false]);
- * // => objects for ['fred']
+ * _.isBoolean(false);
+ * // => true
*
- * // The `_.property` iteratee shorthand.
- * _.reject(users, 'active');
- * // => objects for ['barney']
+ * _.isBoolean(null);
+ * // => false
*/
- function reject(collection, predicate) {
- var func = isArray(collection) ? arrayFilter : baseFilter;
- return func(collection, negate(getIteratee(predicate, 3)));
+ function isBoolean(value) {
+ return value === true || value === false ||
+ (isObjectLike(value) && baseGetTag(value) == boolTag);
}
/**
- * Gets a random element from `collection`.
+ * Checks if `value` is a buffer.
*
* @static
* @memberOf _
- * @since 2.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to sample.
- * @returns {*} Returns the random element.
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
* @example
*
- * _.sample([1, 2, 3, 4]);
- * // => 2
+ * _.isBuffer(new Buffer(2));
+ * // => true
+ *
+ * _.isBuffer(new Uint8Array(2));
+ * // => false
*/
- function sample(collection) {
- var func = isArray(collection) ? arraySample : baseSample;
- return func(collection);
- }
+ var isBuffer = nativeIsBuffer || stubFalse;
/**
- * Gets `n` random elements at unique keys from `collection` up to the
- * size of `collection`.
+ * Checks if `value` is classified as a `Date` object.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Collection
- * @param {Array|Object} collection The collection to sample.
- * @param {number} [n=1] The number of elements to sample.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Array} Returns the random elements.
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
* @example
*
- * _.sampleSize([1, 2, 3], 2);
- * // => [3, 1]
+ * _.isDate(new Date);
+ * // => true
*
- * _.sampleSize([1, 2, 3], 4);
- * // => [2, 3, 1]
+ * _.isDate('Mon April 23 2012');
+ * // => false
*/
- function sampleSize(collection, n, guard) {
- if ((guard ? isIterateeCall(collection, n, guard) : n === undefined)) {
- n = 1;
- } else {
- n = toInteger(n);
- }
- var func = isArray(collection) ? arraySampleSize : baseSampleSize;
- return func(collection, n);
- }
+ var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
/**
- * Creates an array of shuffled values, using a version of the
- * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle).
+ * Checks if `value` is likely a DOM element.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to shuffle.
- * @returns {Array} Returns the new shuffled array.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
* @example
*
- * _.shuffle([1, 2, 3, 4]);
- * // => [4, 1, 3, 2]
+ * _.isElement(document.body);
+ * // => true
+ *
+ * _.isElement('');
+ * // => false
*/
- function shuffle(collection) {
- var func = isArray(collection) ? arrayShuffle : baseShuffle;
- return func(collection);
+ function isElement(value) {
+ return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value);
}
/**
- * Gets the size of `collection` by returning its length for array-like
- * values or the number of own enumerable string keyed properties for objects.
+ * Checks if `value` is an empty object, collection, map, or set.
+ *
+ * Objects are considered empty if they have no own enumerable string keyed
+ * properties.
+ *
+ * Array-like values such as `arguments` objects, arrays, buffers, strings, or
+ * jQuery-like collections are considered empty if they have a `length` of `0`.
+ * Similarly, maps and sets are considered empty if they have a `size` of `0`.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object|string} collection The collection to inspect.
- * @returns {number} Returns the collection size.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is empty, else `false`.
* @example
*
- * _.size([1, 2, 3]);
- * // => 3
+ * _.isEmpty(null);
+ * // => true
*
- * _.size({ 'a': 1, 'b': 2 });
- * // => 2
+ * _.isEmpty(true);
+ * // => true
*
- * _.size('pebbles');
- * // => 7
+ * _.isEmpty(1);
+ * // => true
+ *
+ * _.isEmpty([1, 2, 3]);
+ * // => false
+ *
+ * _.isEmpty({ 'a': 1 });
+ * // => false
*/
- function size(collection) {
- if (collection == null) {
- return 0;
+ function isEmpty(value) {
+ if (value == null) {
+ return true;
}
- if (isArrayLike(collection)) {
- return isString(collection) ? stringSize(collection) : collection.length;
+ if (isArrayLike(value) &&
+ (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
+ isBuffer(value) || isTypedArray(value) || isArguments(value))) {
+ return !value.length;
}
- var tag = getTag(collection);
+ var tag = getTag(value);
if (tag == mapTag || tag == setTag) {
- return collection.size;
+ return !value.size;
}
- return baseKeys(collection).length;
+ if (isPrototype(value)) {
+ return !baseKeys(value).length;
+ }
+ for (var key in value) {
+ if (hasOwnProperty.call(value, key)) {
+ return false;
+ }
+ }
+ return true;
}
/**
- * Checks if `predicate` returns truthy for **any** element of `collection`.
- * Iteration is stopped once `predicate` returns truthy. The predicate is
- * invoked with three arguments: (value, index|key, collection).
+ * Performs a deep comparison between two values to determine if they are
+ * equivalent.
+ *
+ * **Note:** This method supports comparing arrays, array buffers, booleans,
+ * date objects, error objects, maps, numbers, `Object` objects, regexes,
+ * sets, strings, symbols, and typed arrays. `Object` objects are compared
+ * by their own, not inherited, enumerable properties. Functions and DOM
+ * nodes are compared by strict equality, i.e. `===`.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {boolean} Returns `true` if any element passes the predicate check,
- * else `false`.
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
- * _.some([null, 0, 'yes', false], Boolean);
- * // => true
- *
- * var users = [
- * { 'user': 'barney', 'active': true },
- * { 'user': 'fred', 'active': false }
- * ];
- *
- * // The `_.matches` iteratee shorthand.
- * _.some(users, { 'user': 'barney', 'active': false });
- * // => false
+ * var object = { 'a': 1 };
+ * var other = { 'a': 1 };
*
- * // The `_.matchesProperty` iteratee shorthand.
- * _.some(users, ['active', false]);
+ * _.isEqual(object, other);
* // => true
*
- * // The `_.property` iteratee shorthand.
- * _.some(users, 'active');
- * // => true
+ * object === other;
+ * // => false
*/
- function some(collection, predicate, guard) {
- var func = isArray(collection) ? arraySome : baseSome;
- if (guard && isIterateeCall(collection, predicate, guard)) {
- predicate = undefined;
- }
- return func(collection, getIteratee(predicate, 3));
+ function isEqual(value, other) {
+ return baseIsEqual(value, other);
}
/**
- * Creates an array of elements, sorted in ascending order by the results of
- * running each element in a collection thru each iteratee. This method
- * performs a stable sort, that is, it preserves the original sort order of
- * equal elements. The iteratees are invoked with one argument: (value).
+ * This method is like `_.isEqual` except that it accepts `customizer` which
+ * is invoked to compare values. If `customizer` returns `undefined`, comparisons
+ * are handled by the method instead. The `customizer` is invoked with up to
+ * six arguments: (objValue, othValue [, index|key, object, other, stack]).
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Collection
- * @param {Array|Object} collection The collection to iterate over.
- * @param {...(Function|Function[])} [iteratees=[_.identity]]
- * The iteratees to sort by.
- * @returns {Array} Returns the new sorted array.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
- * var users = [
- * { 'user': 'fred', 'age': 48 },
- * { 'user': 'barney', 'age': 36 },
- * { 'user': 'fred', 'age': 30 },
- * { 'user': 'barney', 'age': 34 }
- * ];
- *
- * _.sortBy(users, [function(o) { return o.user; }]);
- * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 30]]
+ * function isGreeting(value) {
+ * return /^h(?:i|ello)$/.test(value);
+ * }
*
- * _.sortBy(users, ['user', 'age']);
- * // => objects for [['barney', 34], ['barney', 36], ['fred', 30], ['fred', 48]]
- */
- var sortBy = baseRest(function(collection, iteratees) {
- if (collection == null) {
- return [];
- }
- var length = iteratees.length;
- if (length > 1 && isIterateeCall(collection, iteratees[0], iteratees[1])) {
- iteratees = [];
- } else if (length > 2 && isIterateeCall(iteratees[0], iteratees[1], iteratees[2])) {
- iteratees = [iteratees[0]];
- }
- return baseOrderBy(collection, baseFlatten(iteratees, 1), []);
- });
-
- /*------------------------------------------------------------------------*/
-
- /**
- * Gets the timestamp of the number of milliseconds that have elapsed since
- * the Unix epoch (1 January 1970 00:00:00 UTC).
+ * function customizer(objValue, othValue) {
+ * if (isGreeting(objValue) && isGreeting(othValue)) {
+ * return true;
+ * }
+ * }
*
- * @static
- * @memberOf _
- * @since 2.4.0
- * @category Date
- * @returns {number} Returns the timestamp.
- * @example
+ * var array = ['hello', 'goodbye'];
+ * var other = ['hi', 'goodbye'];
*
- * _.defer(function(stamp) {
- * console.log(_.now() - stamp);
- * }, _.now());
- * // => Logs the number of milliseconds it took for the deferred invocation.
+ * _.isEqualWith(array, other, customizer);
+ * // => true
*/
- var now = ctxNow || function() {
- return root.Date.now();
- };
-
- /*------------------------------------------------------------------------*/
+ function isEqualWith(value, other, customizer) {
+ customizer = typeof customizer == 'function' ? customizer : undefined;
+ var result = customizer ? customizer(value, other) : undefined;
+ return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result;
+ }
/**
- * The opposite of `_.before`; this method creates a function that invokes
- * `func` once it's called `n` or more times.
+ * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
+ * `SyntaxError`, `TypeError`, or `URIError` object.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Function
- * @param {number} n The number of calls before `func` is invoked.
- * @param {Function} func The function to restrict.
- * @returns {Function} Returns the new restricted function.
+ * @since 3.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
* @example
*
- * var saves = ['profile', 'settings'];
- *
- * var done = _.after(saves.length, function() {
- * console.log('done saving!');
- * });
+ * _.isError(new Error);
+ * // => true
*
- * _.forEach(saves, function(type) {
- * asyncSave({ 'type': type, 'complete': done });
- * });
- * // => Logs 'done saving!' after the two async saves have completed.
+ * _.isError(Error);
+ * // => false
*/
- function after(n, func) {
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
+ function isError(value) {
+ if (!isObjectLike(value)) {
+ return false;
}
- n = toInteger(n);
- return function() {
- if (--n < 1) {
- return func.apply(this, arguments);
- }
- };
+ var tag = baseGetTag(value);
+ return tag == errorTag || tag == domExcTag ||
+ (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value));
}
/**
- * Creates a function that invokes `func`, with up to `n` arguments,
- * ignoring any additional arguments.
+ * Checks if `value` is a finite primitive number.
+ *
+ * **Note:** This method is based on
+ * [`Number.isFinite`](https://mdn.io/Number/isFinite).
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Function
- * @param {Function} func The function to cap arguments for.
- * @param {number} [n=func.length] The arity cap.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Function} Returns the new capped function.
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
* @example
*
- * _.map(['6', '8', '10'], _.ary(parseInt, 1));
- * // => [6, 8, 10]
+ * _.isFinite(3);
+ * // => true
+ *
+ * _.isFinite(Number.MIN_VALUE);
+ * // => true
+ *
+ * _.isFinite(Infinity);
+ * // => false
+ *
+ * _.isFinite('3');
+ * // => false
*/
- function ary(func, n, guard) {
- n = guard ? undefined : n;
- n = (func && n == null) ? func.length : n;
- return createWrap(func, WRAP_ARY_FLAG, undefined, undefined, undefined, undefined, n);
+ function isFinite(value) {
+ return typeof value == 'number' && nativeIsFinite(value);
}
/**
- * Creates a function that invokes `func`, with the `this` binding and arguments
- * of the created function, while it's called less than `n` times. Subsequent
- * calls to the created function return the result of the last `func` invocation.
+ * Checks if `value` is classified as a `Function` object.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Function
- * @param {number} n The number of calls at which `func` is no longer invoked.
- * @param {Function} func The function to restrict.
- * @returns {Function} Returns the new restricted function.
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
* @example
*
- * jQuery(element).on('click', _.before(5, addContactToList));
- * // => Allows adding up to 4 contacts to the list.
+ * _.isFunction(_);
+ * // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
*/
- function before(n, func) {
- var result;
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
+ function isFunction(value) {
+ if (!isObject(value)) {
+ return false;
}
- n = toInteger(n);
- return function() {
- if (--n > 0) {
- result = func.apply(this, arguments);
- }
- if (n <= 1) {
- func = undefined;
- }
- return result;
- };
+ // The use of `Object#toString` avoids issues with the `typeof` operator
+ // in Safari 9 which returns 'object' for typed arrays and other constructors.
+ var tag = baseGetTag(value);
+ return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}
/**
- * Creates a function that invokes `func` with the `this` binding of `thisArg`
- * and `partials` prepended to the arguments it receives.
- *
- * The `_.bind.placeholder` value, which defaults to `_` in monolithic builds,
- * may be used as a placeholder for partially applied arguments.
+ * Checks if `value` is an integer.
*
- * **Note:** Unlike native `Function#bind`, this method doesn't set the "length"
- * property of bound functions.
+ * **Note:** This method is based on
+ * [`Number.isInteger`](https://mdn.io/Number/isInteger).
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Function
- * @param {Function} func The function to bind.
- * @param {*} thisArg The `this` binding of `func`.
- * @param {...*} [partials] The arguments to be partially applied.
- * @returns {Function} Returns the new bound function.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an integer, else `false`.
* @example
*
- * function greet(greeting, punctuation) {
- * return greeting + ' ' + this.user + punctuation;
- * }
+ * _.isInteger(3);
+ * // => true
*
- * var object = { 'user': 'fred' };
+ * _.isInteger(Number.MIN_VALUE);
+ * // => false
*
- * var bound = _.bind(greet, object, 'hi');
- * bound('!');
- * // => 'hi fred!'
+ * _.isInteger(Infinity);
+ * // => false
*
- * // Bound with placeholders.
- * var bound = _.bind(greet, object, _, '!');
- * bound('hi');
- * // => 'hi fred!'
+ * _.isInteger('3');
+ * // => false
*/
- var bind = baseRest(function(func, thisArg, partials) {
- var bitmask = WRAP_BIND_FLAG;
- if (partials.length) {
- var holders = replaceHolders(partials, getHolder(bind));
- bitmask |= WRAP_PARTIAL_FLAG;
- }
- return createWrap(func, bitmask, thisArg, partials, holders);
- });
+ function isInteger(value) {
+ return typeof value == 'number' && value == toInteger(value);
+ }
/**
- * Creates a function that invokes the method at `object[key]` with `partials`
- * prepended to the arguments it receives.
- *
- * This method differs from `_.bind` by allowing bound functions to reference
- * methods that may be redefined or don't yet exist. See
- * [Peter Michaux's article](http://peter.michaux.ca/articles/lazy-function-definition-pattern)
- * for more details.
+ * Checks if `value` is a valid array-like length.
*
- * The `_.bindKey.placeholder` value, which defaults to `_` in monolithic
- * builds, may be used as a placeholder for partially applied arguments.
+ * **Note:** This method is loosely based on
+ * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
*
* @static
* @memberOf _
- * @since 0.10.0
- * @category Function
- * @param {Object} object The object to invoke the method on.
- * @param {string} key The key of the method.
- * @param {...*} [partials] The arguments to be partially applied.
- * @returns {Function} Returns the new bound function.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
* @example
*
- * var object = {
- * 'user': 'fred',
- * 'greet': function(greeting, punctuation) {
- * return greeting + ' ' + this.user + punctuation;
- * }
- * };
- *
- * var bound = _.bindKey(object, 'greet', 'hi');
- * bound('!');
- * // => 'hi fred!'
+ * _.isLength(3);
+ * // => true
*
- * object.greet = function(greeting, punctuation) {
- * return greeting + 'ya ' + this.user + punctuation;
- * };
+ * _.isLength(Number.MIN_VALUE);
+ * // => false
*
- * bound('!');
- * // => 'hiya fred!'
+ * _.isLength(Infinity);
+ * // => false
*
- * // Bound with placeholders.
- * var bound = _.bindKey(object, 'greet', _, '!');
- * bound('hi');
- * // => 'hiya fred!'
+ * _.isLength('3');
+ * // => false
*/
- var bindKey = baseRest(function(object, key, partials) {
- var bitmask = WRAP_BIND_FLAG | WRAP_BIND_KEY_FLAG;
- if (partials.length) {
- var holders = replaceHolders(partials, getHolder(bindKey));
- bitmask |= WRAP_PARTIAL_FLAG;
- }
- return createWrap(key, bitmask, object, partials, holders);
- });
+ function isLength(value) {
+ return typeof value == 'number' &&
+ value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+ }
/**
- * Creates a function that accepts arguments of `func` and either invokes
- * `func` returning its result, if at least `arity` number of arguments have
- * been provided, or returns a function that accepts the remaining `func`
- * arguments, and so on. The arity of `func` may be specified if `func.length`
- * is not sufficient.
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
- * The `_.curry.placeholder` value, which defaults to `_` in monolithic builds,
- * may be used as a placeholder for provided arguments.
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
*
- * **Note:** This method doesn't set the "length" property of curried functions.
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+ function isObject(value) {
+ var type = typeof value;
+ return value != null && (type == 'object' || type == 'function');
+ }
+
+ /**
+ * Checks if `value` is object-like. A value is object-like if it's not `null`
+ * and has a `typeof` result of "object".
*
* @static
* @memberOf _
- * @since 2.0.0
- * @category Function
- * @param {Function} func The function to curry.
- * @param {number} [arity=func.length] The arity of `func`.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Function} Returns the new curried function.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
- * var abc = function(a, b, c) {
- * return [a, b, c];
- * };
+ * _.isObjectLike({});
+ * // => true
*
- * var curried = _.curry(abc);
+ * _.isObjectLike([1, 2, 3]);
+ * // => true
*
- * curried(1)(2)(3);
- * // => [1, 2, 3]
+ * _.isObjectLike(_.noop);
+ * // => false
*
- * curried(1, 2)(3);
- * // => [1, 2, 3]
+ * _.isObjectLike(null);
+ * // => false
+ */
+ function isObjectLike(value) {
+ return value != null && typeof value == 'object';
+ }
+
+ /**
+ * Checks if `value` is classified as a `Map` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+ * @example
*
- * curried(1, 2, 3);
- * // => [1, 2, 3]
+ * _.isMap(new Map);
+ * // => true
*
- * // Curried with placeholders.
- * curried(1)(_, 3)(2);
- * // => [1, 2, 3]
+ * _.isMap(new WeakMap);
+ * // => false
*/
- function curry(func, arity, guard) {
- arity = guard ? undefined : arity;
- var result = createWrap(func, WRAP_CURRY_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
- result.placeholder = curry.placeholder;
- return result;
- }
+ var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;
/**
- * This method is like `_.curry` except that arguments are applied to `func`
- * in the manner of `_.partialRight` instead of `_.partial`.
+ * Performs a partial deep comparison between `object` and `source` to
+ * determine if `object` contains equivalent property values.
*
- * The `_.curryRight.placeholder` value, which defaults to `_` in monolithic
- * builds, may be used as a placeholder for provided arguments.
+ * **Note:** This method is equivalent to `_.matches` when `source` is
+ * partially applied.
*
- * **Note:** This method doesn't set the "length" property of curried functions.
+ * Partial comparisons will match empty array and empty object `source`
+ * values against any array or object value, respectively. See `_.isEqual`
+ * for a list of supported value comparisons.
*
* @static
* @memberOf _
* @since 3.0.0
- * @category Function
- * @param {Function} func The function to curry.
- * @param {number} [arity=func.length] The arity of `func`.
- * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
- * @returns {Function} Returns the new curried function.
+ * @category Lang
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
* @example
*
- * var abc = function(a, b, c) {
- * return [a, b, c];
- * };
- *
- * var curried = _.curryRight(abc);
- *
- * curried(3)(2)(1);
- * // => [1, 2, 3]
- *
- * curried(2, 3)(1);
- * // => [1, 2, 3]
+ * var object = { 'a': 1, 'b': 2 };
*
- * curried(1, 2, 3);
- * // => [1, 2, 3]
+ * _.isMatch(object, { 'b': 2 });
+ * // => true
*
- * // Curried with placeholders.
- * curried(3)(1, _)(2);
- * // => [1, 2, 3]
+ * _.isMatch(object, { 'b': 1 });
+ * // => false
*/
- function curryRight(func, arity, guard) {
- arity = guard ? undefined : arity;
- var result = createWrap(func, WRAP_CURRY_RIGHT_FLAG, undefined, undefined, undefined, undefined, undefined, arity);
- result.placeholder = curryRight.placeholder;
- return result;
+ function isMatch(object, source) {
+ return object === source || baseIsMatch(object, source, getMatchData(source));
}
/**
- * Creates a debounced function that delays invoking `func` until after `wait`
- * milliseconds have elapsed since the last time the debounced function was
- * invoked. The debounced function comes with a `cancel` method to cancel
- * delayed `func` invocations and a `flush` method to immediately invoke them.
- * Provide `options` to indicate whether `func` should be invoked on the
- * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
- * with the last arguments provided to the debounced function. Subsequent
- * calls to the debounced function return the result of the last `func`
- * invocation.
- *
- * **Note:** If `leading` and `trailing` options are `true`, `func` is
- * invoked on the trailing edge of the timeout only if the debounced function
- * is invoked more than once during the `wait` timeout.
- *
- * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
- * until to the next tick, similar to `setTimeout` with a timeout of `0`.
- *
- * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
- * for details over the differences between `_.debounce` and `_.throttle`.
+ * This method is like `_.isMatch` except that it accepts `customizer` which
+ * is invoked to compare values. If `customizer` returns `undefined`, comparisons
+ * are handled by the method instead. The `customizer` is invoked with five
+ * arguments: (objValue, srcValue, index|key, object, source).
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Function
- * @param {Function} func The function to debounce.
- * @param {number} [wait=0] The number of milliseconds to delay.
- * @param {Object} [options={}] The options object.
- * @param {boolean} [options.leading=false]
- * Specify invoking on the leading edge of the timeout.
- * @param {number} [options.maxWait]
- * The maximum time `func` is allowed to be delayed before it's invoked.
- * @param {boolean} [options.trailing=true]
- * Specify invoking on the trailing edge of the timeout.
- * @returns {Function} Returns the new debounced function.
+ * @since 4.0.0
+ * @category Lang
+ * @param {Object} object The object to inspect.
+ * @param {Object} source The object of property values to match.
+ * @param {Function} [customizer] The function to customize comparisons.
+ * @returns {boolean} Returns `true` if `object` is a match, else `false`.
* @example
*
- * // Avoid costly calculations while the window size is in flux.
- * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
+ * function isGreeting(value) {
+ * return /^h(?:i|ello)$/.test(value);
+ * }
*
- * // Invoke `sendMail` when clicked, debouncing subsequent calls.
- * jQuery(element).on('click', _.debounce(sendMail, 300, {
- * 'leading': true,
- * 'trailing': false
- * }));
+ * function customizer(objValue, srcValue) {
+ * if (isGreeting(objValue) && isGreeting(srcValue)) {
+ * return true;
+ * }
+ * }
*
- * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
- * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
- * var source = new EventSource('/stream');
- * jQuery(source).on('message', debounced);
+ * var object = { 'greeting': 'hello' };
+ * var source = { 'greeting': 'hi' };
*
- * // Cancel the trailing debounced invocation.
- * jQuery(window).on('popstate', debounced.cancel);
+ * _.isMatchWith(object, source, customizer);
+ * // => true
*/
- function debounce(func, wait, options) {
- var lastArgs,
- lastThis,
- maxWait,
- result,
- timerId,
- lastCallTime,
- lastInvokeTime = 0,
- leading = false,
- maxing = false,
- trailing = true;
-
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- wait = toNumber(wait) || 0;
- if (isObject(options)) {
- leading = !!options.leading;
- maxing = 'maxWait' in options;
- maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait;
- trailing = 'trailing' in options ? !!options.trailing : trailing;
- }
-
- function invokeFunc(time) {
- var args = lastArgs,
- thisArg = lastThis;
-
- lastArgs = lastThis = undefined;
- lastInvokeTime = time;
- result = func.apply(thisArg, args);
- return result;
- }
-
- function leadingEdge(time) {
- // Reset any `maxWait` timer.
- lastInvokeTime = time;
- // Start the timer for the trailing edge.
- timerId = setTimeout(timerExpired, wait);
- // Invoke the leading edge.
- return leading ? invokeFunc(time) : result;
- }
-
- function remainingWait(time) {
- var timeSinceLastCall = time - lastCallTime,
- timeSinceLastInvoke = time - lastInvokeTime,
- timeWaiting = wait - timeSinceLastCall;
-
- return maxing
- ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
- : timeWaiting;
- }
-
- function shouldInvoke(time) {
- var timeSinceLastCall = time - lastCallTime,
- timeSinceLastInvoke = time - lastInvokeTime;
-
- // Either this is the first call, activity has stopped and we're at the
- // trailing edge, the system time has gone backwards and we're treating
- // it as the trailing edge, or we've hit the `maxWait` limit.
- return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
- (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
- }
-
- function timerExpired() {
- var time = now();
- if (shouldInvoke(time)) {
- return trailingEdge(time);
- }
- // Restart the timer.
- timerId = setTimeout(timerExpired, remainingWait(time));
- }
-
- function trailingEdge(time) {
- timerId = undefined;
-
- // Only invoke if we have `lastArgs` which means `func` has been
- // debounced at least once.
- if (trailing && lastArgs) {
- return invokeFunc(time);
- }
- lastArgs = lastThis = undefined;
- return result;
- }
-
- function cancel() {
- if (timerId !== undefined) {
- clearTimeout(timerId);
- }
- lastInvokeTime = 0;
- lastArgs = lastCallTime = lastThis = timerId = undefined;
- }
-
- function flush() {
- return timerId === undefined ? result : trailingEdge(now());
- }
-
- function debounced() {
- var time = now(),
- isInvoking = shouldInvoke(time);
-
- lastArgs = arguments;
- lastThis = this;
- lastCallTime = time;
-
- if (isInvoking) {
- if (timerId === undefined) {
- return leadingEdge(lastCallTime);
- }
- if (maxing) {
- // Handle invocations in a tight loop.
- clearTimeout(timerId);
- timerId = setTimeout(timerExpired, wait);
- return invokeFunc(lastCallTime);
- }
- }
- if (timerId === undefined) {
- timerId = setTimeout(timerExpired, wait);
- }
- return result;
- }
- debounced.cancel = cancel;
- debounced.flush = flush;
- return debounced;
+ function isMatchWith(object, source, customizer) {
+ customizer = typeof customizer == 'function' ? customizer : undefined;
+ return baseIsMatch(object, source, getMatchData(source), customizer);
}
/**
- * Defers invoking the `func` until the current call stack has cleared. Any
- * additional arguments are provided to `func` when it's invoked.
+ * Checks if `value` is `NaN`.
+ *
+ * **Note:** This method is based on
+ * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as
+ * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for
+ * `undefined` and other non-number values.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Function
- * @param {Function} func The function to defer.
- * @param {...*} [args] The arguments to invoke `func` with.
- * @returns {number} Returns the timer id.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
* @example
*
- * _.defer(function(text) {
- * console.log(text);
- * }, 'deferred');
- * // => Logs 'deferred' after one millisecond.
+ * _.isNaN(NaN);
+ * // => true
+ *
+ * _.isNaN(new Number(NaN));
+ * // => true
+ *
+ * isNaN(undefined);
+ * // => true
+ *
+ * _.isNaN(undefined);
+ * // => false
*/
- var defer = baseRest(function(func, args) {
- return baseDelay(func, 1, args);
- });
+ function isNaN(value) {
+ // An `NaN` primitive is the only value that is not equal to itself.
+ // Perform the `toStringTag` check first to avoid errors with some
+ // ActiveX objects in IE.
+ return isNumber(value) && value != +value;
+ }
/**
- * Invokes `func` after `wait` milliseconds. Any additional arguments are
- * provided to `func` when it's invoked.
+ * Checks if `value` is a pristine native function.
+ *
+ * **Note:** This method can't reliably detect native functions in the presence
+ * of the core-js package because core-js circumvents this kind of detection.
+ * Despite multiple requests, the core-js maintainer has made it clear: any
+ * attempt to fix the detection will be obstructed. As a result, we're left
+ * with little choice but to throw an error. Unfortunately, this also affects
+ * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill),
+ * which rely on core-js.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function,
+ * else `false`.
+ * @example
+ *
+ * _.isNative(Array.prototype.push);
+ * // => true
+ *
+ * _.isNative(_);
+ * // => false
+ */
+ function isNative(value) {
+ if (isMaskable(value)) {
+ throw new Error(CORE_ERROR_TEXT);
+ }
+ return baseIsNative(value);
+ }
+
+ /**
+ * Checks if `value` is `null`.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Function
- * @param {Function} func The function to delay.
- * @param {number} wait The number of milliseconds to delay invocation.
- * @param {...*} [args] The arguments to invoke `func` with.
- * @returns {number} Returns the timer id.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
* @example
*
- * _.delay(function(text) {
- * console.log(text);
- * }, 1000, 'later');
- * // => Logs 'later' after one second.
+ * _.isNull(null);
+ * // => true
+ *
+ * _.isNull(void 0);
+ * // => false
*/
- var delay = baseRest(function(func, wait, args) {
- return baseDelay(func, toNumber(wait) || 0, args);
- });
+ function isNull(value) {
+ return value === null;
+ }
/**
- * Creates a function that invokes `func` with arguments reversed.
+ * Checks if `value` is `null` or `undefined`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Function
- * @param {Function} func The function to flip arguments for.
- * @returns {Function} Returns the new flipped function.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
* @example
*
- * var flipped = _.flip(function() {
- * return _.toArray(arguments);
- * });
+ * _.isNil(null);
+ * // => true
*
- * flipped('a', 'b', 'c', 'd');
- * // => ['d', 'c', 'b', 'a']
+ * _.isNil(void 0);
+ * // => true
+ *
+ * _.isNil(NaN);
+ * // => false
*/
- function flip(func) {
- return createWrap(func, WRAP_FLIP_FLAG);
+ function isNil(value) {
+ return value == null;
}
/**
- * Creates a function that memoizes the result of `func`. If `resolver` is
- * provided, it determines the cache key for storing the result based on the
- * arguments provided to the memoized function. By default, the first argument
- * provided to the memoized function is used as the map cache key. The `func`
- * is invoked with the `this` binding of the memoized function.
+ * Checks if `value` is classified as a `Number` primitive or object.
*
- * **Note:** The cache is exposed as the `cache` property on the memoized
- * function. Its creation may be customized by replacing the `_.memoize.Cache`
- * constructor with one whose instances implement the
- * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
- * method interface of `clear`, `delete`, `get`, `has`, and `set`.
+ * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
+ * classified as numbers, use the `_.isFinite` method.
*
* @static
* @memberOf _
* @since 0.1.0
- * @category Function
- * @param {Function} func The function to have its output memoized.
- * @param {Function} [resolver] The function to resolve the cache key.
- * @returns {Function} Returns the new memoized function.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a number, else `false`.
* @example
*
- * var object = { 'a': 1, 'b': 2 };
- * var other = { 'c': 3, 'd': 4 };
+ * _.isNumber(3);
+ * // => true
*
- * var values = _.memoize(_.values);
- * values(object);
- * // => [1, 2]
+ * _.isNumber(Number.MIN_VALUE);
+ * // => true
*
- * values(other);
- * // => [3, 4]
+ * _.isNumber(Infinity);
+ * // => true
*
- * object.a = 2;
- * values(object);
- * // => [1, 2]
+ * _.isNumber('3');
+ * // => false
+ */
+ function isNumber(value) {
+ return typeof value == 'number' ||
+ (isObjectLike(value) && baseGetTag(value) == numberTag);
+ }
+
+ /**
+ * Checks if `value` is a plain object, that is, an object created by the
+ * `Object` constructor or one with a `[[Prototype]]` of `null`.
*
- * // Modify the result cache.
- * values.cache.set(object, ['a', 'b']);
- * values(object);
- * // => ['a', 'b']
+ * @static
+ * @memberOf _
+ * @since 0.8.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @example
*
- * // Replace `_.memoize.Cache`.
- * _.memoize.Cache = WeakMap;
+ * function Foo() {
+ * this.a = 1;
+ * }
+ *
+ * _.isPlainObject(new Foo);
+ * // => false
+ *
+ * _.isPlainObject([1, 2, 3]);
+ * // => false
+ *
+ * _.isPlainObject({ 'x': 0, 'y': 0 });
+ * // => true
+ *
+ * _.isPlainObject(Object.create(null));
+ * // => true
+ */
+ function isPlainObject(value) {
+ if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
+ return false;
+ }
+ var proto = getPrototype(value);
+ if (proto === null) {
+ return true;
+ }
+ var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
+ return typeof Ctor == 'function' && Ctor instanceof Ctor &&
+ funcToString.call(Ctor) == objectCtorString;
+ }
+
+ /**
+ * Checks if `value` is classified as a `RegExp` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+ * @example
+ *
+ * _.isRegExp(/abc/);
+ * // => true
+ *
+ * _.isRegExp('/abc/');
+ * // => false
+ */
+ var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;
+
+ /**
+ * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754
+ * double precision number which isn't the result of a rounded unsafe integer.
+ *
+ * **Note:** This method is based on
+ * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.
+ * @example
+ *
+ * _.isSafeInteger(3);
+ * // => true
+ *
+ * _.isSafeInteger(Number.MIN_VALUE);
+ * // => false
+ *
+ * _.isSafeInteger(Infinity);
+ * // => false
+ *
+ * _.isSafeInteger('3');
+ * // => false
*/
- function memoize(func, resolver) {
- if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- var memoized = function() {
- var args = arguments,
- key = resolver ? resolver.apply(this, args) : args[0],
- cache = memoized.cache;
-
- if (cache.has(key)) {
- return cache.get(key);
- }
- var result = func.apply(this, args);
- memoized.cache = cache.set(key, result) || cache;
- return result;
- };
- memoized.cache = new (memoize.Cache || MapCache);
- return memoized;
+ function isSafeInteger(value) {
+ return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;
}
- // Expose `MapCache`.
- memoize.Cache = MapCache;
-
/**
- * Creates a function that negates the result of the predicate `func`. The
- * `func` predicate is invoked with the `this` binding and arguments of the
- * created function.
+ * Checks if `value` is classified as a `Set` object.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Function
- * @param {Function} predicate The predicate to negate.
- * @returns {Function} Returns the new negated function.
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a set, else `false`.
* @example
*
- * function isEven(n) {
- * return n % 2 == 0;
- * }
+ * _.isSet(new Set);
+ * // => true
*
- * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
- * // => [1, 3, 5]
+ * _.isSet(new WeakSet);
+ * // => false
*/
- function negate(predicate) {
- if (typeof predicate != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- return function() {
- var args = arguments;
- switch (args.length) {
- case 0: return !predicate.call(this);
- case 1: return !predicate.call(this, args[0]);
- case 2: return !predicate.call(this, args[0], args[1]);
- case 3: return !predicate.call(this, args[0], args[1], args[2]);
- }
- return !predicate.apply(this, args);
- };
- }
+ var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;
/**
- * Creates a function that is restricted to invoking `func` once. Repeat calls
- * to the function return the value of the first invocation. The `func` is
- * invoked with the `this` binding and arguments of the created function.
+ * Checks if `value` is classified as a `String` primitive or object.
*
* @static
- * @memberOf _
* @since 0.1.0
- * @category Function
- * @param {Function} func The function to restrict.
- * @returns {Function} Returns the new restricted function.
+ * @memberOf _
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a string, else `false`.
* @example
*
- * var initialize = _.once(createApplication);
- * initialize();
- * initialize();
- * // => `createApplication` is invoked once
+ * _.isString('abc');
+ * // => true
+ *
+ * _.isString(1);
+ * // => false
*/
- function once(func) {
- return before(2, func);
+ function isString(value) {
+ return typeof value == 'string' ||
+ (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
}
/**
- * Creates a function that invokes `func` with its arguments transformed.
+ * Checks if `value` is classified as a `Symbol` primitive or object.
*
* @static
- * @since 4.0.0
* @memberOf _
- * @category Function
- * @param {Function} func The function to wrap.
- * @param {...(Function|Function[])} [transforms=[_.identity]]
- * The argument transforms.
- * @returns {Function} Returns the new function.
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
* @example
*
- * function doubled(n) {
- * return n * 2;
- * }
+ * _.isSymbol(Symbol.iterator);
+ * // => true
*
- * function square(n) {
- * return n * n;
- * }
+ * _.isSymbol('abc');
+ * // => false
+ */
+ function isSymbol(value) {
+ return typeof value == 'symbol' ||
+ (isObjectLike(value) && baseGetTag(value) == symbolTag);
+ }
+
+ /**
+ * Checks if `value` is classified as a typed array.
*
- * var func = _.overArgs(function(x, y) {
- * return [x, y];
- * }, [square, doubled]);
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ * @example
*
- * func(9, 3);
- * // => [81, 6]
+ * _.isTypedArray(new Uint8Array);
+ * // => true
*
- * func(10, 5);
- * // => [100, 10]
+ * _.isTypedArray([]);
+ * // => false
*/
- var overArgs = castRest(function(func, transforms) {
- transforms = (transforms.length == 1 && isArray(transforms[0]))
- ? arrayMap(transforms[0], baseUnary(getIteratee()))
- : arrayMap(baseFlatten(transforms, 1), baseUnary(getIteratee()));
-
- var funcsLength = transforms.length;
- return baseRest(function(args) {
- var index = -1,
- length = nativeMin(args.length, funcsLength);
-
- while (++index < length) {
- args[index] = transforms[index].call(this, args[index]);
- }
- return apply(func, this, args);
- });
- });
+ var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
/**
- * Creates a function that invokes `func` with `partials` prepended to the
- * arguments it receives. This method is like `_.bind` except it does **not**
- * alter the `this` binding.
- *
- * The `_.partial.placeholder` value, which defaults to `_` in monolithic
- * builds, may be used as a placeholder for partially applied arguments.
- *
- * **Note:** This method doesn't set the "length" property of partially
- * applied functions.
+ * Checks if `value` is `undefined`.
*
* @static
+ * @since 0.1.0
* @memberOf _
- * @since 0.2.0
- * @category Function
- * @param {Function} func The function to partially apply arguments to.
- * @param {...*} [partials] The arguments to be partially applied.
- * @returns {Function} Returns the new partially applied function.
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
* @example
*
- * function greet(greeting, name) {
- * return greeting + ' ' + name;
- * }
- *
- * var sayHelloTo = _.partial(greet, 'hello');
- * sayHelloTo('fred');
- * // => 'hello fred'
+ * _.isUndefined(void 0);
+ * // => true
*
- * // Partially applied with placeholders.
- * var greetFred = _.partial(greet, _, 'fred');
- * greetFred('hi');
- * // => 'hi fred'
+ * _.isUndefined(null);
+ * // => false
*/
- var partial = baseRest(function(func, partials) {
- var holders = replaceHolders(partials, getHolder(partial));
- return createWrap(func, WRAP_PARTIAL_FLAG, undefined, partials, holders);
- });
+ function isUndefined(value) {
+ return value === undefined;
+ }
/**
- * This method is like `_.partial` except that partially applied arguments
- * are appended to the arguments it receives.
- *
- * The `_.partialRight.placeholder` value, which defaults to `_` in monolithic
- * builds, may be used as a placeholder for partially applied arguments.
- *
- * **Note:** This method doesn't set the "length" property of partially
- * applied functions.
+ * Checks if `value` is classified as a `WeakMap` object.
*
* @static
* @memberOf _
- * @since 1.0.0
- * @category Function
- * @param {Function} func The function to partially apply arguments to.
- * @param {...*} [partials] The arguments to be partially applied.
- * @returns {Function} Returns the new partially applied function.
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a weak map, else `false`.
* @example
*
- * function greet(greeting, name) {
- * return greeting + ' ' + name;
- * }
- *
- * var greetFred = _.partialRight(greet, 'fred');
- * greetFred('hi');
- * // => 'hi fred'
+ * _.isWeakMap(new WeakMap);
+ * // => true
*
- * // Partially applied with placeholders.
- * var sayHelloTo = _.partialRight(greet, 'hello', _);
- * sayHelloTo('fred');
- * // => 'hello fred'
+ * _.isWeakMap(new Map);
+ * // => false
*/
- var partialRight = baseRest(function(func, partials) {
- var holders = replaceHolders(partials, getHolder(partialRight));
- return createWrap(func, WRAP_PARTIAL_RIGHT_FLAG, undefined, partials, holders);
- });
+ function isWeakMap(value) {
+ return isObjectLike(value) && getTag(value) == weakMapTag;
+ }
/**
- * Creates a function that invokes `func` with arguments arranged according
- * to the specified `indexes` where the argument value at the first index is
- * provided as the first argument, the argument value at the second index is
- * provided as the second argument, and so on.
+ * Checks if `value` is classified as a `WeakSet` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.3.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a weak set, else `false`.
+ * @example
+ *
+ * _.isWeakSet(new WeakSet);
+ * // => true
+ *
+ * _.isWeakSet(new Set);
+ * // => false
+ */
+ function isWeakSet(value) {
+ return isObjectLike(value) && baseGetTag(value) == weakSetTag;
+ }
+
+ /**
+ * Checks if `value` is less than `other`.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Function
- * @param {Function} func The function to rearrange arguments for.
- * @param {...(number|number[])} indexes The arranged argument indexes.
- * @returns {Function} Returns the new function.
+ * @since 3.9.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is less than `other`,
+ * else `false`.
+ * @see _.gt
* @example
*
- * var rearged = _.rearg(function(a, b, c) {
- * return [a, b, c];
- * }, [2, 0, 1]);
+ * _.lt(1, 3);
+ * // => true
*
- * rearged('b', 'c', 'a')
- * // => ['a', 'b', 'c']
+ * _.lt(3, 3);
+ * // => false
+ *
+ * _.lt(3, 1);
+ * // => false
*/
- var rearg = flatRest(function(func, indexes) {
- return createWrap(func, WRAP_REARG_FLAG, undefined, undefined, undefined, indexes);
- });
+ var lt = createRelationalOperation(baseLt);
/**
- * Creates a function that invokes `func` with the `this` binding of the
- * created function and arguments from `start` and beyond provided as
- * an array.
- *
- * **Note:** This method is based on the
- * [rest parameter](https://mdn.io/rest_parameters).
+ * Checks if `value` is less than or equal to `other`.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Function
- * @param {Function} func The function to apply a rest parameter to.
- * @param {number} [start=func.length-1] The start position of the rest parameter.
- * @returns {Function} Returns the new function.
+ * @since 3.9.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if `value` is less than or equal to
+ * `other`, else `false`.
+ * @see _.gte
* @example
*
- * var say = _.rest(function(what, names) {
- * return what + ' ' + _.initial(names).join(', ') +
- * (_.size(names) > 1 ? ', & ' : '') + _.last(names);
- * });
+ * _.lte(1, 3);
+ * // => true
*
- * say('hello', 'fred', 'barney', 'pebbles');
- * // => 'hello fred, barney, & pebbles'
+ * _.lte(3, 3);
+ * // => true
+ *
+ * _.lte(3, 1);
+ * // => false
*/
- function rest(func, start) {
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
- }
- start = start === undefined ? start : toInteger(start);
- return baseRest(func, start);
- }
+ var lte = createRelationalOperation(function(value, other) {
+ return value <= other;
+ });
/**
- * Creates a function that invokes `func` with the `this` binding of the
- * create function and an array of arguments much like
- * [`Function#apply`](http://www.ecma-international.org/ecma-262/7.0/#sec-function.prototype.apply).
- *
- * **Note:** This method is based on the
- * [spread operator](https://mdn.io/spread_operator).
+ * Converts `value` to an array.
*
* @static
+ * @since 0.1.0
* @memberOf _
- * @since 3.2.0
- * @category Function
- * @param {Function} func The function to spread arguments over.
- * @param {number} [start=0] The start position of the spread.
- * @returns {Function} Returns the new function.
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {Array} Returns the converted array.
* @example
*
- * var say = _.spread(function(who, what) {
- * return who + ' says ' + what;
- * });
+ * _.toArray({ 'a': 1, 'b': 2 });
+ * // => [1, 2]
*
- * say(['fred', 'hello']);
- * // => 'fred says hello'
+ * _.toArray('abc');
+ * // => ['a', 'b', 'c']
*
- * var numbers = Promise.all([
- * Promise.resolve(40),
- * Promise.resolve(36)
- * ]);
+ * _.toArray(1);
+ * // => []
*
- * numbers.then(_.spread(function(x, y) {
- * return x + y;
- * }));
- * // => a Promise of 76
+ * _.toArray(null);
+ * // => []
*/
- function spread(func, start) {
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
+ function toArray(value) {
+ if (!value) {
+ return [];
}
- start = start == null ? 0 : nativeMax(toInteger(start), 0);
- return baseRest(function(args) {
- var array = args[start],
- otherArgs = castSlice(args, 0, start);
+ if (isArrayLike(value)) {
+ return isString(value) ? stringToArray(value) : copyArray(value);
+ }
+ if (symIterator && value[symIterator]) {
+ return iteratorToArray(value[symIterator]());
+ }
+ var tag = getTag(value),
+ func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
- if (array) {
- arrayPush(otherArgs, array);
- }
- return apply(func, this, otherArgs);
- });
+ return func(value);
}
/**
- * Creates a throttled function that only invokes `func` at most once per
- * every `wait` milliseconds. The throttled function comes with a `cancel`
- * method to cancel delayed `func` invocations and a `flush` method to
- * immediately invoke them. Provide `options` to indicate whether `func`
- * should be invoked on the leading and/or trailing edge of the `wait`
- * timeout. The `func` is invoked with the last arguments provided to the
- * throttled function. Subsequent calls to the throttled function return the
- * result of the last `func` invocation.
- *
- * **Note:** If `leading` and `trailing` options are `true`, `func` is
- * invoked on the trailing edge of the timeout only if the throttled function
- * is invoked more than once during the `wait` timeout.
- *
- * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
- * until to the next tick, similar to `setTimeout` with a timeout of `0`.
- *
- * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
- * for details over the differences between `_.throttle` and `_.debounce`.
+ * Converts `value` to a finite number.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Function
- * @param {Function} func The function to throttle.
- * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
- * @param {Object} [options={}] The options object.
- * @param {boolean} [options.leading=true]
- * Specify invoking on the leading edge of the timeout.
- * @param {boolean} [options.trailing=true]
- * Specify invoking on the trailing edge of the timeout.
- * @returns {Function} Returns the new throttled function.
+ * @since 4.12.0
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted number.
* @example
*
- * // Avoid excessively updating the position while scrolling.
- * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
+ * _.toFinite(3.2);
+ * // => 3.2
*
- * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
- * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
- * jQuery(element).on('click', throttled);
+ * _.toFinite(Number.MIN_VALUE);
+ * // => 5e-324
*
- * // Cancel the trailing throttled invocation.
- * jQuery(window).on('popstate', throttled.cancel);
+ * _.toFinite(Infinity);
+ * // => 1.7976931348623157e+308
+ *
+ * _.toFinite('3.2');
+ * // => 3.2
*/
- function throttle(func, wait, options) {
- var leading = true,
- trailing = true;
-
- if (typeof func != 'function') {
- throw new TypeError(FUNC_ERROR_TEXT);
+ function toFinite(value) {
+ if (!value) {
+ return value === 0 ? value : 0;
}
- if (isObject(options)) {
- leading = 'leading' in options ? !!options.leading : leading;
- trailing = 'trailing' in options ? !!options.trailing : trailing;
+ value = toNumber(value);
+ if (value === INFINITY || value === -INFINITY) {
+ var sign = (value < 0 ? -1 : 1);
+ return sign * MAX_INTEGER;
}
- return debounce(func, wait, {
- 'leading': leading,
- 'maxWait': wait,
- 'trailing': trailing
- });
+ return value === value ? value : 0;
}
/**
- * Creates a function that accepts up to one argument, ignoring any
- * additional arguments.
+ * Converts `value` to an integer.
+ *
+ * **Note:** This method is loosely based on
+ * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Function
- * @param {Function} func The function to cap arguments for.
- * @returns {Function} Returns the new capped function.
+ * @category Lang
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
* @example
*
- * _.map(['6', '8', '10'], _.unary(parseInt));
- * // => [6, 8, 10]
- */
- function unary(func) {
- return ary(func, 1);
- }
-
- /**
- * Creates a function that provides `value` to `wrapper` as its first
- * argument. Any additional arguments provided to the function are appended
- * to those provided to the `wrapper`. The wrapper is invoked with the `this`
- * binding of the created function.
+ * _.toInteger(3.2);
+ * // => 3
*
- * @static
- * @memberOf _
- * @since 0.1.0
- * @category Function
- * @param {*} value The value to wrap.
- * @param {Function} [wrapper=identity] The wrapper function.
- * @returns {Function} Returns the new function.
- * @example
+ * _.toInteger(Number.MIN_VALUE);
+ * // => 0
*
- * var p = _.wrap(_.escape, function(func, text) {
- * return '' + func(text) + '
';
- * });
+ * _.toInteger(Infinity);
+ * // => 1.7976931348623157e+308
*
- * p('fred, barney, & pebbles');
- * // => 'fred, barney, & pebbles
'
+ * _.toInteger('3.2');
+ * // => 3
*/
- function wrap(value, wrapper) {
- return partial(castFunction(wrapper), value);
- }
+ function toInteger(value) {
+ var result = toFinite(value),
+ remainder = result % 1;
- /*------------------------------------------------------------------------*/
+ return result === result ? (remainder ? result - remainder : result) : 0;
+ }
/**
- * Casts `value` as an array if it's not one.
+ * Converts `value` to an integer suitable for use as the length of an
+ * array-like object.
+ *
+ * **Note:** This method is based on
+ * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
*
* @static
* @memberOf _
- * @since 4.4.0
+ * @since 4.0.0
* @category Lang
- * @param {*} value The value to inspect.
- * @returns {Array} Returns the cast array.
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
* @example
*
- * _.castArray(1);
- * // => [1]
- *
- * _.castArray({ 'a': 1 });
- * // => [{ 'a': 1 }]
- *
- * _.castArray('abc');
- * // => ['abc']
- *
- * _.castArray(null);
- * // => [null]
+ * _.toLength(3.2);
+ * // => 3
*
- * _.castArray(undefined);
- * // => [undefined]
+ * _.toLength(Number.MIN_VALUE);
+ * // => 0
*
- * _.castArray();
- * // => []
+ * _.toLength(Infinity);
+ * // => 4294967295
*
- * var array = [1, 2, 3];
- * console.log(_.castArray(array) === array);
- * // => true
+ * _.toLength('3.2');
+ * // => 3
*/
- function castArray() {
- if (!arguments.length) {
- return [];
- }
- var value = arguments[0];
- return isArray(value) ? value : [value];
+ function toLength(value) {
+ return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;
}
/**
- * Creates a shallow clone of `value`.
- *
- * **Note:** This method is loosely based on the
- * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
- * and supports cloning arrays, array buffers, booleans, date objects, maps,
- * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
- * arrays. The own enumerable properties of `arguments` objects are cloned
- * as plain objects. An empty object is returned for uncloneable values such
- * as error objects, functions, DOM nodes, and WeakMaps.
+ * Converts `value` to a number.
*
* @static
* @memberOf _
- * @since 0.1.0
+ * @since 4.0.0
* @category Lang
- * @param {*} value The value to clone.
- * @returns {*} Returns the cloned value.
- * @see _.cloneDeep
+ * @param {*} value The value to process.
+ * @returns {number} Returns the number.
* @example
*
- * var objects = [{ 'a': 1 }, { 'b': 2 }];
+ * _.toNumber(3.2);
+ * // => 3.2
*
- * var shallow = _.clone(objects);
- * console.log(shallow[0] === objects[0]);
- * // => true
+ * _.toNumber(Number.MIN_VALUE);
+ * // => 5e-324
+ *
+ * _.toNumber(Infinity);
+ * // => Infinity
+ *
+ * _.toNumber('3.2');
+ * // => 3.2
*/
- function clone(value) {
- return baseClone(value, CLONE_SYMBOLS_FLAG);
+ function toNumber(value) {
+ if (typeof value == 'number') {
+ return value;
+ }
+ if (isSymbol(value)) {
+ return NAN;
+ }
+ if (isObject(value)) {
+ var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+ value = isObject(other) ? (other + '') : other;
+ }
+ if (typeof value != 'string') {
+ return value === 0 ? value : +value;
+ }
+ value = value.replace(reTrim, '');
+ var isBinary = reIsBinary.test(value);
+ return (isBinary || reIsOctal.test(value))
+ ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+ : (reIsBadHex.test(value) ? NAN : +value);
}
/**
- * This method is like `_.clone` except that it accepts `customizer` which
- * is invoked to produce the cloned value. If `customizer` returns `undefined`,
- * cloning is handled by the method instead. The `customizer` is invoked with
- * up to four arguments; (value [, index|key, object, stack]).
+ * Converts `value` to a plain object flattening inherited enumerable string
+ * keyed properties of `value` to own properties of the plain object.
*
* @static
* @memberOf _
- * @since 4.0.0
+ * @since 3.0.0
* @category Lang
- * @param {*} value The value to clone.
- * @param {Function} [customizer] The function to customize cloning.
- * @returns {*} Returns the cloned value.
- * @see _.cloneDeepWith
+ * @param {*} value The value to convert.
+ * @returns {Object} Returns the converted plain object.
* @example
*
- * function customizer(value) {
- * if (_.isElement(value)) {
- * return value.cloneNode(false);
- * }
+ * function Foo() {
+ * this.b = 2;
* }
*
- * var el = _.cloneWith(document.body, customizer);
+ * Foo.prototype.c = 3;
*
- * console.log(el === document.body);
- * // => false
- * console.log(el.nodeName);
- * // => 'BODY'
- * console.log(el.childNodes.length);
- * // => 0
+ * _.assign({ 'a': 1 }, new Foo);
+ * // => { 'a': 1, 'b': 2 }
+ *
+ * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
+ * // => { 'a': 1, 'b': 2, 'c': 3 }
*/
- function cloneWith(value, customizer) {
- customizer = typeof customizer == 'function' ? customizer : undefined;
- return baseClone(value, CLONE_SYMBOLS_FLAG, customizer);
+ function toPlainObject(value) {
+ return copyObject(value, keysIn(value));
}
/**
- * This method is like `_.clone` except that it recursively clones `value`.
+ * Converts `value` to a safe integer. A safe integer can be compared and
+ * represented correctly.
*
* @static
* @memberOf _
- * @since 1.0.0
+ * @since 4.0.0
* @category Lang
- * @param {*} value The value to recursively clone.
- * @returns {*} Returns the deep cloned value.
- * @see _.clone
+ * @param {*} value The value to convert.
+ * @returns {number} Returns the converted integer.
* @example
*
- * var objects = [{ 'a': 1 }, { 'b': 2 }];
+ * _.toSafeInteger(3.2);
+ * // => 3
*
- * var deep = _.cloneDeep(objects);
- * console.log(deep[0] === objects[0]);
- * // => false
+ * _.toSafeInteger(Number.MIN_VALUE);
+ * // => 0
+ *
+ * _.toSafeInteger(Infinity);
+ * // => 9007199254740991
+ *
+ * _.toSafeInteger('3.2');
+ * // => 3
*/
- function cloneDeep(value) {
- return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG);
+ function toSafeInteger(value) {
+ return value
+ ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)
+ : (value === 0 ? value : 0);
}
/**
- * This method is like `_.cloneWith` except that it recursively clones `value`.
+ * Converts `value` to a string. An empty string is returned for `null`
+ * and `undefined` values. The sign of `-0` is preserved.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Lang
- * @param {*} value The value to recursively clone.
- * @param {Function} [customizer] The function to customize cloning.
- * @returns {*} Returns the deep cloned value.
- * @see _.cloneWith
+ * @param {*} value The value to convert.
+ * @returns {string} Returns the converted string.
* @example
*
- * function customizer(value) {
- * if (_.isElement(value)) {
- * return value.cloneNode(true);
- * }
- * }
+ * _.toString(null);
+ * // => ''
*
- * var el = _.cloneDeepWith(document.body, customizer);
+ * _.toString(-0);
+ * // => '-0'
*
- * console.log(el === document.body);
- * // => false
- * console.log(el.nodeName);
- * // => 'BODY'
- * console.log(el.childNodes.length);
- * // => 20
+ * _.toString([1, 2, 3]);
+ * // => '1,2,3'
*/
- function cloneDeepWith(value, customizer) {
- customizer = typeof customizer == 'function' ? customizer : undefined;
- return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer);
+ function toString(value) {
+ return value == null ? '' : baseToString(value);
}
+ /*------------------------------------------------------------------------*/
+
/**
- * Checks if `object` conforms to `source` by invoking the predicate
- * properties of `source` with the corresponding property values of `object`.
+ * Assigns own enumerable string keyed properties of source objects to the
+ * destination object. Source objects are applied from left to right.
+ * Subsequent sources overwrite property assignments of previous sources.
*
- * **Note:** This method is equivalent to `_.conforms` when `source` is
- * partially applied.
+ * **Note:** This method mutates `object` and is loosely based on
+ * [`Object.assign`](https://mdn.io/Object/assign).
*
* @static
* @memberOf _
- * @since 4.14.0
- * @category Lang
- * @param {Object} object The object to inspect.
- * @param {Object} source The object of property predicates to conform to.
- * @returns {boolean} Returns `true` if `object` conforms, else `false`.
+ * @since 0.10.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.assignIn
* @example
*
- * var object = { 'a': 1, 'b': 2 };
+ * function Foo() {
+ * this.a = 1;
+ * }
*
- * _.conformsTo(object, { 'b': function(n) { return n > 1; } });
- * // => true
+ * function Bar() {
+ * this.c = 3;
+ * }
*
- * _.conformsTo(object, { 'b': function(n) { return n > 2; } });
- * // => false
+ * Foo.prototype.b = 2;
+ * Bar.prototype.d = 4;
+ *
+ * _.assign({ 'a': 0 }, new Foo, new Bar);
+ * // => { 'a': 1, 'c': 3 }
*/
- function conformsTo(object, source) {
- return source == null || baseConformsTo(object, source, keys(source));
- }
+ var assign = createAssigner(function(object, source) {
+ if (isPrototype(source) || isArrayLike(source)) {
+ copyObject(source, keys(source), object);
+ return;
+ }
+ for (var key in source) {
+ if (hasOwnProperty.call(source, key)) {
+ assignValue(object, key, source[key]);
+ }
+ }
+ });
/**
- * Performs a
- * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
- * comparison between two values to determine if they are equivalent.
+ * This method is like `_.assign` except that it iterates over own and
+ * inherited source properties.
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @alias extend
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.assign
* @example
*
- * var object = { 'a': 1 };
- * var other = { 'a': 1 };
- *
- * _.eq(object, object);
- * // => true
- *
- * _.eq(object, other);
- * // => false
+ * function Foo() {
+ * this.a = 1;
+ * }
*
- * _.eq('a', 'a');
- * // => true
+ * function Bar() {
+ * this.c = 3;
+ * }
*
- * _.eq('a', Object('a'));
- * // => false
+ * Foo.prototype.b = 2;
+ * Bar.prototype.d = 4;
*
- * _.eq(NaN, NaN);
- * // => true
+ * _.assignIn({ 'a': 0 }, new Foo, new Bar);
+ * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
*/
- function eq(value, other) {
- return value === other || (value !== value && other !== other);
- }
+ var assignIn = createAssigner(function(object, source) {
+ copyObject(source, keysIn(source), object);
+ });
/**
- * Checks if `value` is greater than `other`.
+ * This method is like `_.assignIn` except that it accepts `customizer`
+ * which is invoked to produce the assigned values. If `customizer` returns
+ * `undefined`, assignment is handled by the method instead. The `customizer`
+ * is invoked with five arguments: (objValue, srcValue, key, object, source).
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
- * @since 3.9.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if `value` is greater than `other`,
- * else `false`.
- * @see _.lt
+ * @since 4.0.0
+ * @alias extendWith
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @returns {Object} Returns `object`.
+ * @see _.assignWith
* @example
*
- * _.gt(3, 1);
- * // => true
+ * function customizer(objValue, srcValue) {
+ * return _.isUndefined(objValue) ? srcValue : objValue;
+ * }
*
- * _.gt(3, 3);
- * // => false
+ * var defaults = _.partialRight(_.assignInWith, customizer);
*
- * _.gt(1, 3);
- * // => false
+ * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+ * // => { 'a': 1, 'b': 2 }
*/
- var gt = createRelationalOperation(baseGt);
+ var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {
+ copyObject(source, keysIn(source), object, customizer);
+ });
/**
- * Checks if `value` is greater than or equal to `other`.
+ * This method is like `_.assign` except that it accepts `customizer`
+ * which is invoked to produce the assigned values. If `customizer` returns
+ * `undefined`, assignment is handled by the method instead. The `customizer`
+ * is invoked with five arguments: (objValue, srcValue, key, object, source).
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
- * @since 3.9.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if `value` is greater than or equal to
- * `other`, else `false`.
- * @see _.lte
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @returns {Object} Returns `object`.
+ * @see _.assignInWith
* @example
*
- * _.gte(3, 1);
- * // => true
+ * function customizer(objValue, srcValue) {
+ * return _.isUndefined(objValue) ? srcValue : objValue;
+ * }
*
- * _.gte(3, 3);
- * // => true
+ * var defaults = _.partialRight(_.assignWith, customizer);
*
- * _.gte(1, 3);
- * // => false
+ * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+ * // => { 'a': 1, 'b': 2 }
*/
- var gte = createRelationalOperation(function(value, other) {
- return value >= other;
+ var assignWith = createAssigner(function(object, source, srcIndex, customizer) {
+ copyObject(source, keys(source), object, customizer);
});
/**
- * Checks if `value` is likely an `arguments` object.
+ * Creates an array of values corresponding to `paths` of `object`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an `arguments` object,
- * else `false`.
+ * @since 1.0.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {...(string|string[])} [paths] The property paths to pick.
+ * @returns {Array} Returns the picked values.
* @example
*
- * _.isArguments(function() { return arguments; }());
- * // => true
+ * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
*
- * _.isArguments([1, 2, 3]);
- * // => false
+ * _.at(object, ['a[0].b.c', 'a[1]']);
+ * // => [3, 4]
*/
- var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
- return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
- !propertyIsEnumerable.call(value, 'callee');
- };
+ var at = flatRest(baseAt);
/**
- * Checks if `value` is classified as an `Array` object.
+ * Creates an object that inherits from the `prototype` object. If a
+ * `properties` object is given, its own enumerable string keyed properties
+ * are assigned to the created object.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+ * @since 2.3.0
+ * @category Object
+ * @param {Object} prototype The object to inherit from.
+ * @param {Object} [properties] The properties to assign to the object.
+ * @returns {Object} Returns the new object.
* @example
*
- * _.isArray([1, 2, 3]);
- * // => true
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
*
- * _.isArray(document.body.children);
- * // => false
+ * function Circle() {
+ * Shape.call(this);
+ * }
*
- * _.isArray('abc');
- * // => false
+ * Circle.prototype = _.create(Shape.prototype, {
+ * 'constructor': Circle
+ * });
*
- * _.isArray(_.noop);
- * // => false
+ * var circle = new Circle;
+ * circle instanceof Circle;
+ * // => true
+ *
+ * circle instanceof Shape;
+ * // => true
*/
- var isArray = Array.isArray;
+ function create(prototype, properties) {
+ var result = baseCreate(prototype);
+ return properties == null ? result : baseAssign(result, properties);
+ }
/**
- * Checks if `value` is classified as an `ArrayBuffer` object.
+ * Assigns own and inherited enumerable string keyed properties of source
+ * objects to the destination object for all destination properties that
+ * resolve to `undefined`. Source objects are applied from left to right.
+ * Once a property is set, additional values of the same property are ignored.
+ *
+ * **Note:** This method mutates `object`.
*
* @static
+ * @since 0.1.0
* @memberOf _
- * @since 4.3.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`.
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.defaultsDeep
* @example
*
- * _.isArrayBuffer(new ArrayBuffer(2));
- * // => true
+ * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
+ * // => { 'a': 1, 'b': 2 }
+ */
+ var defaults = baseRest(function(args) {
+ args.push(undefined, customDefaultsAssignIn);
+ return apply(assignInWith, undefined, args);
+ });
+
+ /**
+ * This method is like `_.defaults` except that it recursively assigns
+ * default properties.
*
- * _.isArrayBuffer(new Array(2));
- * // => false
+ * **Note:** This method mutates `object`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.10.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
+ * @see _.defaults
+ * @example
+ *
+ * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
+ * // => { 'a': { 'b': 2, 'c': 3 } }
*/
- var isArrayBuffer = nodeIsArrayBuffer ? baseUnary(nodeIsArrayBuffer) : baseIsArrayBuffer;
+ var defaultsDeep = baseRest(function(args) {
+ args.push(undefined, customDefaultsMerge);
+ return apply(mergeWith, undefined, args);
+ });
/**
- * Checks if `value` is array-like. A value is considered array-like if it's
- * not a function and has a `value.length` that's an integer greater than or
- * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+ * This method is like `_.find` except that it returns the key of the first
+ * element `predicate` returns truthy for instead of the element itself.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+ * @since 1.1.0
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {string|undefined} Returns the key of the matched element,
+ * else `undefined`.
* @example
*
- * _.isArrayLike([1, 2, 3]);
- * // => true
+ * var users = {
+ * 'barney': { 'age': 36, 'active': true },
+ * 'fred': { 'age': 40, 'active': false },
+ * 'pebbles': { 'age': 1, 'active': true }
+ * };
+ *
+ * _.findKey(users, function(o) { return o.age < 40; });
+ * // => 'barney' (iteration order is not guaranteed)
*
- * _.isArrayLike(document.body.children);
- * // => true
+ * // The `_.matches` iteratee shorthand.
+ * _.findKey(users, { 'age': 1, 'active': true });
+ * // => 'pebbles'
*
- * _.isArrayLike('abc');
- * // => true
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findKey(users, ['active', false]);
+ * // => 'fred'
*
- * _.isArrayLike(_.noop);
- * // => false
+ * // The `_.property` iteratee shorthand.
+ * _.findKey(users, 'active');
+ * // => 'barney'
*/
- function isArrayLike(value) {
- return value != null && isLength(value.length) && !isFunction(value);
+ function findKey(object, predicate) {
+ return baseFindKey(object, getIteratee(predicate, 3), baseForOwn);
}
/**
- * This method is like `_.isArrayLike` except that it also checks if `value`
- * is an object.
+ * This method is like `_.findKey` except that it iterates over elements of
+ * a collection in the opposite order.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an array-like object,
- * else `false`.
+ * @since 2.0.0
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @param {Function} [predicate=_.identity] The function invoked per iteration.
+ * @returns {string|undefined} Returns the key of the matched element,
+ * else `undefined`.
* @example
*
- * _.isArrayLikeObject([1, 2, 3]);
- * // => true
+ * var users = {
+ * 'barney': { 'age': 36, 'active': true },
+ * 'fred': { 'age': 40, 'active': false },
+ * 'pebbles': { 'age': 1, 'active': true }
+ * };
*
- * _.isArrayLikeObject(document.body.children);
- * // => true
+ * _.findLastKey(users, function(o) { return o.age < 40; });
+ * // => returns 'pebbles' assuming `_.findKey` returns 'barney'
*
- * _.isArrayLikeObject('abc');
- * // => false
+ * // The `_.matches` iteratee shorthand.
+ * _.findLastKey(users, { 'age': 36, 'active': true });
+ * // => 'barney'
*
- * _.isArrayLikeObject(_.noop);
- * // => false
+ * // The `_.matchesProperty` iteratee shorthand.
+ * _.findLastKey(users, ['active', false]);
+ * // => 'fred'
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.findLastKey(users, 'active');
+ * // => 'pebbles'
*/
- function isArrayLikeObject(value) {
- return isObjectLike(value) && isArrayLike(value);
+ function findLastKey(object, predicate) {
+ return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight);
}
/**
- * Checks if `value` is classified as a boolean primitive or object.
+ * Iterates over own and inherited enumerable string keyed properties of an
+ * object and invokes `iteratee` for each property. The iteratee is invoked
+ * with three arguments: (value, key, object). Iteratee functions may exit
+ * iteration early by explicitly returning `false`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a boolean, else `false`.
+ * @since 0.3.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ * @see _.forInRight
* @example
*
- * _.isBoolean(false);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isBoolean(null);
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.forIn(new Foo, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'a', 'b', then 'c' (iteration order is not guaranteed).
*/
- function isBoolean(value) {
- return value === true || value === false ||
- (isObjectLike(value) && baseGetTag(value) == boolTag);
+ function forIn(object, iteratee) {
+ return object == null
+ ? object
+ : baseFor(object, getIteratee(iteratee, 3), keysIn);
}
/**
- * Checks if `value` is a buffer.
+ * This method is like `_.forIn` except that it iterates over properties of
+ * `object` in the opposite order.
*
* @static
* @memberOf _
- * @since 4.3.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
+ * @since 2.0.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ * @see _.forIn
* @example
*
- * _.isBuffer(new Buffer(2));
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isBuffer(new Uint8Array(2));
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.forInRight(new Foo, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'c', 'b', then 'a' assuming `_.forIn` logs 'a', 'b', then 'c'.
*/
- var isBuffer = nativeIsBuffer || stubFalse;
+ function forInRight(object, iteratee) {
+ return object == null
+ ? object
+ : baseForRight(object, getIteratee(iteratee, 3), keysIn);
+ }
/**
- * Checks if `value` is classified as a `Date` object.
+ * Iterates over own enumerable string keyed properties of an object and
+ * invokes `iteratee` for each property. The iteratee is invoked with three
+ * arguments: (value, key, object). Iteratee functions may exit iteration
+ * early by explicitly returning `false`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
+ * @since 0.3.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ * @see _.forOwnRight
* @example
*
- * _.isDate(new Date);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isDate('Mon April 23 2012');
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.forOwn(new Foo, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'a' then 'b' (iteration order is not guaranteed).
*/
- var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
+ function forOwn(object, iteratee) {
+ return object && baseForOwn(object, getIteratee(iteratee, 3));
+ }
/**
- * Checks if `value` is likely a DOM element.
+ * This method is like `_.forOwn` except that it iterates over properties of
+ * `object` in the opposite order.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
+ * @since 2.0.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Object} Returns `object`.
+ * @see _.forOwn
* @example
*
- * _.isElement(document.body);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isElement('');
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.forOwnRight(new Foo, function(value, key) {
+ * console.log(key);
+ * });
+ * // => Logs 'b' then 'a' assuming `_.forOwn` logs 'a' then 'b'.
*/
- function isElement(value) {
- return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value);
+ function forOwnRight(object, iteratee) {
+ return object && baseForOwnRight(object, getIteratee(iteratee, 3));
}
/**
- * Checks if `value` is an empty object, collection, map, or set.
- *
- * Objects are considered empty if they have no own enumerable string keyed
- * properties.
- *
- * Array-like values such as `arguments` objects, arrays, buffers, strings, or
- * jQuery-like collections are considered empty if they have a `length` of `0`.
- * Similarly, maps and sets are considered empty if they have a `size` of `0`.
+ * Creates an array of function property names from own enumerable properties
+ * of `object`.
*
* @static
- * @memberOf _
* @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the function names.
+ * @see _.functionsIn
* @example
*
- * _.isEmpty(null);
- * // => true
- *
- * _.isEmpty(true);
- * // => true
- *
- * _.isEmpty(1);
- * // => true
- *
- * _.isEmpty([1, 2, 3]);
- * // => false
+ * function Foo() {
+ * this.a = _.constant('a');
+ * this.b = _.constant('b');
+ * }
*
- * _.isEmpty({ 'a': 1 });
- * // => false
- */
- function isEmpty(value) {
- if (value == null) {
- return true;
- }
- if (isArrayLike(value) &&
- (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
- isBuffer(value) || isTypedArray(value) || isArguments(value))) {
- return !value.length;
- }
- var tag = getTag(value);
- if (tag == mapTag || tag == setTag) {
- return !value.size;
- }
- if (isPrototype(value)) {
- return !baseKeys(value).length;
- }
- for (var key in value) {
- if (hasOwnProperty.call(value, key)) {
- return false;
- }
- }
- return true;
+ * Foo.prototype.c = _.constant('c');
+ *
+ * _.functions(new Foo);
+ * // => ['a', 'b']
+ */
+ function functions(object) {
+ return object == null ? [] : baseFunctions(object, keys(object));
}
/**
- * Performs a deep comparison between two values to determine if they are
- * equivalent.
- *
- * **Note:** This method supports comparing arrays, array buffers, booleans,
- * date objects, error objects, maps, numbers, `Object` objects, regexes,
- * sets, strings, symbols, and typed arrays. `Object` objects are compared
- * by their own, not inherited, enumerable properties. Functions and DOM
- * nodes are compared by strict equality, i.e. `===`.
+ * Creates an array of function property names from own and inherited
+ * enumerable properties of `object`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the function names.
+ * @see _.functions
* @example
*
- * var object = { 'a': 1 };
- * var other = { 'a': 1 };
+ * function Foo() {
+ * this.a = _.constant('a');
+ * this.b = _.constant('b');
+ * }
*
- * _.isEqual(object, other);
- * // => true
+ * Foo.prototype.c = _.constant('c');
*
- * object === other;
- * // => false
+ * _.functionsIn(new Foo);
+ * // => ['a', 'b', 'c']
*/
- function isEqual(value, other) {
- return baseIsEqual(value, other);
+ function functionsIn(object) {
+ return object == null ? [] : baseFunctions(object, keysIn(object));
}
/**
- * This method is like `_.isEqual` except that it accepts `customizer` which
- * is invoked to compare values. If `customizer` returns `undefined`, comparisons
- * are handled by the method instead. The `customizer` is invoked with up to
- * six arguments: (objValue, othValue [, index|key, object, other, stack]).
+ * Gets the value at `path` of `object`. If the resolved value is
+ * `undefined`, the `defaultValue` is returned in its place.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @param {Function} [customizer] The function to customize comparisons.
- * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @since 3.7.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to get.
+ * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+ * @returns {*} Returns the resolved value.
* @example
*
- * function isGreeting(value) {
- * return /^h(?:i|ello)$/.test(value);
- * }
+ * var object = { 'a': [{ 'b': { 'c': 3 } }] };
*
- * function customizer(objValue, othValue) {
- * if (isGreeting(objValue) && isGreeting(othValue)) {
- * return true;
- * }
- * }
+ * _.get(object, 'a[0].b.c');
+ * // => 3
*
- * var array = ['hello', 'goodbye'];
- * var other = ['hi', 'goodbye'];
+ * _.get(object, ['a', '0', 'b', 'c']);
+ * // => 3
*
- * _.isEqualWith(array, other, customizer);
- * // => true
+ * _.get(object, 'a.b.c', 'default');
+ * // => 'default'
*/
- function isEqualWith(value, other, customizer) {
- customizer = typeof customizer == 'function' ? customizer : undefined;
- var result = customizer ? customizer(value, other) : undefined;
- return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result;
+ function get(object, path, defaultValue) {
+ var result = object == null ? undefined : baseGet(object, path);
+ return result === undefined ? defaultValue : result;
}
/**
- * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
- * `SyntaxError`, `TypeError`, or `URIError` object.
+ * Checks if `path` is a direct property of `object`.
*
* @static
+ * @since 0.1.0
* @memberOf _
- * @since 3.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
* @example
*
- * _.isError(new Error);
+ * var object = { 'a': { 'b': 2 } };
+ * var other = _.create({ 'a': _.create({ 'b': 2 }) });
+ *
+ * _.has(object, 'a');
* // => true
*
- * _.isError(Error);
+ * _.has(object, 'a.b');
+ * // => true
+ *
+ * _.has(object, ['a', 'b']);
+ * // => true
+ *
+ * _.has(other, 'a');
* // => false
*/
- function isError(value) {
- if (!isObjectLike(value)) {
- return false;
- }
- var tag = baseGetTag(value);
- return tag == errorTag || tag == domExcTag ||
- (typeof value.message == 'string' && typeof value.name == 'string' && !isPlainObject(value));
+ function has(object, path) {
+ return object != null && hasPath(object, path, baseHas);
}
/**
- * Checks if `value` is a finite primitive number.
- *
- * **Note:** This method is based on
- * [`Number.isFinite`](https://mdn.io/Number/isFinite).
+ * Checks if `path` is a direct or inherited property of `object`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a finite number, else `false`.
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path to check.
+ * @returns {boolean} Returns `true` if `path` exists, else `false`.
* @example
*
- * _.isFinite(3);
+ * var object = _.create({ 'a': _.create({ 'b': 2 }) });
+ *
+ * _.hasIn(object, 'a');
* // => true
*
- * _.isFinite(Number.MIN_VALUE);
+ * _.hasIn(object, 'a.b');
* // => true
*
- * _.isFinite(Infinity);
- * // => false
+ * _.hasIn(object, ['a', 'b']);
+ * // => true
*
- * _.isFinite('3');
+ * _.hasIn(object, 'b');
* // => false
*/
- function isFinite(value) {
- return typeof value == 'number' && nativeIsFinite(value);
+ function hasIn(object, path) {
+ return object != null && hasPath(object, path, baseHasIn);
}
/**
- * Checks if `value` is classified as a `Function` object.
+ * Creates an object composed of the inverted keys and values of `object`.
+ * If `object` contains duplicate values, subsequent values overwrite
+ * property assignments of previous values.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+ * @since 0.7.0
+ * @category Object
+ * @param {Object} object The object to invert.
+ * @returns {Object} Returns the new inverted object.
* @example
*
- * _.isFunction(_);
- * // => true
+ * var object = { 'a': 1, 'b': 2, 'c': 1 };
*
- * _.isFunction(/abc/);
- * // => false
+ * _.invert(object);
+ * // => { '1': 'c', '2': 'b' }
*/
- function isFunction(value) {
- if (!isObject(value)) {
- return false;
- }
- // The use of `Object#toString` avoids issues with the `typeof` operator
- // in Safari 9 which returns 'object' for typed arrays and other constructors.
- var tag = baseGetTag(value);
- return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
- }
+ var invert = createInverter(function(result, value, key) {
+ result[value] = key;
+ }, constant(identity));
/**
- * Checks if `value` is an integer.
- *
- * **Note:** This method is based on
- * [`Number.isInteger`](https://mdn.io/Number/isInteger).
+ * This method is like `_.invert` except that the inverted object is generated
+ * from the results of running each element of `object` thru `iteratee`. The
+ * corresponding inverted value of each inverted key is an array of keys
+ * responsible for generating the inverted value. The iteratee is invoked
+ * with one argument: (value).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an integer, else `false`.
+ * @since 4.1.0
+ * @category Object
+ * @param {Object} object The object to invert.
+ * @param {Function} [iteratee=_.identity] The iteratee invoked per element.
+ * @returns {Object} Returns the new inverted object.
* @example
*
- * _.isInteger(3);
- * // => true
- *
- * _.isInteger(Number.MIN_VALUE);
- * // => false
+ * var object = { 'a': 1, 'b': 2, 'c': 1 };
*
- * _.isInteger(Infinity);
- * // => false
+ * _.invertBy(object);
+ * // => { '1': ['a', 'c'], '2': ['b'] }
*
- * _.isInteger('3');
- * // => false
+ * _.invertBy(object, function(value) {
+ * return 'group' + value;
+ * });
+ * // => { 'group1': ['a', 'c'], 'group2': ['b'] }
*/
- function isInteger(value) {
- return typeof value == 'number' && value == toInteger(value);
- }
+ var invertBy = createInverter(function(result, value, key) {
+ if (hasOwnProperty.call(result, value)) {
+ result[value].push(key);
+ } else {
+ result[value] = [key];
+ }
+ }, getIteratee);
/**
- * Checks if `value` is a valid array-like length.
- *
- * **Note:** This method is loosely based on
- * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+ * Invokes the method at `path` of `object`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the method to invoke.
+ * @param {...*} [args] The arguments to invoke the method with.
+ * @returns {*} Returns the result of the invoked method.
+ * @example
+ *
+ * var object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] };
+ *
+ * _.invoke(object, 'a[0].b.c.slice', 1, 3);
+ * // => [2, 3]
+ */
+ var invoke = baseRest(baseInvoke);
+
+ /**
+ * Creates an array of the own enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects. See the
+ * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
+ * for more details.
+ *
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
* @example
*
- * _.isLength(3);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isLength(Number.MIN_VALUE);
- * // => false
+ * Foo.prototype.c = 3;
*
- * _.isLength(Infinity);
- * // => false
+ * _.keys(new Foo);
+ * // => ['a', 'b'] (iteration order is not guaranteed)
*
- * _.isLength('3');
- * // => false
+ * _.keys('hi');
+ * // => ['0', '1']
*/
- function isLength(value) {
- return typeof value == 'number' &&
- value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+ function keys(object) {
+ return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object);
}
/**
- * Checks if `value` is the
- * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
- * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ * Creates an array of the own and inherited enumerable property names of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @since 3.0.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property names.
* @example
*
- * _.isObject({});
- * // => true
- *
- * _.isObject([1, 2, 3]);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isObject(_.noop);
- * // => true
+ * Foo.prototype.c = 3;
*
- * _.isObject(null);
- * // => false
+ * _.keysIn(new Foo);
+ * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
*/
- function isObject(value) {
- var type = typeof value;
- return value != null && (type == 'object' || type == 'function');
+ function keysIn(object) {
+ return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
}
/**
- * Checks if `value` is object-like. A value is object-like if it's not `null`
- * and has a `typeof` result of "object".
+ * The opposite of `_.mapValues`; this method creates an object with the
+ * same values as `object` and keys generated by running each own enumerable
+ * string keyed property of `object` thru `iteratee`. The iteratee is invoked
+ * with three arguments: (value, key, object).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+ * @since 3.8.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Object} Returns the new mapped object.
+ * @see _.mapValues
* @example
*
- * _.isObjectLike({});
- * // => true
- *
- * _.isObjectLike([1, 2, 3]);
- * // => true
- *
- * _.isObjectLike(_.noop);
- * // => false
- *
- * _.isObjectLike(null);
- * // => false
+ * _.mapKeys({ 'a': 1, 'b': 2 }, function(value, key) {
+ * return key + value;
+ * });
+ * // => { 'a1': 1, 'b2': 2 }
*/
- function isObjectLike(value) {
- return value != null && typeof value == 'object';
+ function mapKeys(object, iteratee) {
+ var result = {};
+ iteratee = getIteratee(iteratee, 3);
+
+ baseForOwn(object, function(value, key, object) {
+ baseAssignValue(result, iteratee(value, key, object), value);
+ });
+ return result;
}
/**
- * Checks if `value` is classified as a `Map` object.
+ * Creates an object with the same keys as `object` and values generated
+ * by running each own enumerable string keyed property of `object` thru
+ * `iteratee`. The iteratee is invoked with three arguments:
+ * (value, key, object).
*
* @static
* @memberOf _
- * @since 4.3.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a map, else `false`.
+ * @since 2.4.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @returns {Object} Returns the new mapped object.
+ * @see _.mapKeys
* @example
*
- * _.isMap(new Map);
- * // => true
+ * var users = {
+ * 'fred': { 'user': 'fred', 'age': 40 },
+ * 'pebbles': { 'user': 'pebbles', 'age': 1 }
+ * };
*
- * _.isMap(new WeakMap);
- * // => false
+ * _.mapValues(users, function(o) { return o.age; });
+ * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
+ *
+ * // The `_.property` iteratee shorthand.
+ * _.mapValues(users, 'age');
+ * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
*/
- var isMap = nodeIsMap ? baseUnary(nodeIsMap) : baseIsMap;
+ function mapValues(object, iteratee) {
+ var result = {};
+ iteratee = getIteratee(iteratee, 3);
+
+ baseForOwn(object, function(value, key, object) {
+ baseAssignValue(result, key, iteratee(value, key, object));
+ });
+ return result;
+ }
/**
- * Performs a partial deep comparison between `object` and `source` to
- * determine if `object` contains equivalent property values.
- *
- * **Note:** This method is equivalent to `_.matches` when `source` is
- * partially applied.
+ * This method is like `_.assign` except that it recursively merges own and
+ * inherited enumerable string keyed properties of source objects into the
+ * destination object. Source properties that resolve to `undefined` are
+ * skipped if a destination value exists. Array and plain object properties
+ * are merged recursively. Other objects and value types are overridden by
+ * assignment. Source objects are applied from left to right. Subsequent
+ * sources overwrite property assignments of previous sources.
*
- * Partial comparisons will match empty array and empty object `source`
- * values against any array or object value, respectively. See `_.isEqual`
- * for a list of supported value comparisons.
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Lang
- * @param {Object} object The object to inspect.
- * @param {Object} source The object of property values to match.
- * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ * @since 0.5.0
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} [sources] The source objects.
+ * @returns {Object} Returns `object`.
* @example
*
- * var object = { 'a': 1, 'b': 2 };
+ * var object = {
+ * 'a': [{ 'b': 2 }, { 'd': 4 }]
+ * };
*
- * _.isMatch(object, { 'b': 2 });
- * // => true
+ * var other = {
+ * 'a': [{ 'c': 3 }, { 'e': 5 }]
+ * };
*
- * _.isMatch(object, { 'b': 1 });
- * // => false
+ * _.merge(object, other);
+ * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
*/
- function isMatch(object, source) {
- return object === source || baseIsMatch(object, source, getMatchData(source));
- }
+ var merge = createAssigner(function(object, source, srcIndex) {
+ baseMerge(object, source, srcIndex);
+ });
/**
- * This method is like `_.isMatch` except that it accepts `customizer` which
- * is invoked to compare values. If `customizer` returns `undefined`, comparisons
- * are handled by the method instead. The `customizer` is invoked with five
- * arguments: (objValue, srcValue, index|key, object, source).
+ * This method is like `_.merge` except that it accepts `customizer` which
+ * is invoked to produce the merged values of the destination and source
+ * properties. If `customizer` returns `undefined`, merging is handled by the
+ * method instead. The `customizer` is invoked with six arguments:
+ * (objValue, srcValue, key, object, source, stack).
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Lang
- * @param {Object} object The object to inspect.
- * @param {Object} source The object of property values to match.
- * @param {Function} [customizer] The function to customize comparisons.
- * @returns {boolean} Returns `true` if `object` is a match, else `false`.
+ * @category Object
+ * @param {Object} object The destination object.
+ * @param {...Object} sources The source objects.
+ * @param {Function} customizer The function to customize assigned values.
+ * @returns {Object} Returns `object`.
* @example
*
- * function isGreeting(value) {
- * return /^h(?:i|ello)$/.test(value);
- * }
- *
* function customizer(objValue, srcValue) {
- * if (isGreeting(objValue) && isGreeting(srcValue)) {
- * return true;
+ * if (_.isArray(objValue)) {
+ * return objValue.concat(srcValue);
* }
* }
*
- * var object = { 'greeting': 'hello' };
- * var source = { 'greeting': 'hi' };
+ * var object = { 'a': [1], 'b': [2] };
+ * var other = { 'a': [3], 'b': [4] };
*
- * _.isMatchWith(object, source, customizer);
- * // => true
+ * _.mergeWith(object, other, customizer);
+ * // => { 'a': [1, 3], 'b': [2, 4] }
*/
- function isMatchWith(object, source, customizer) {
- customizer = typeof customizer == 'function' ? customizer : undefined;
- return baseIsMatch(object, source, getMatchData(source), customizer);
- }
+ var mergeWith = createAssigner(function(object, source, srcIndex, customizer) {
+ baseMerge(object, source, srcIndex, customizer);
+ });
/**
- * Checks if `value` is `NaN`.
+ * The opposite of `_.pick`; this method creates an object composed of the
+ * own and inherited enumerable property paths of `object` that are not omitted.
*
- * **Note:** This method is based on
- * [`Number.isNaN`](https://mdn.io/Number/isNaN) and is not the same as
- * global [`isNaN`](https://mdn.io/isNaN) which returns `true` for
- * `undefined` and other non-number values.
+ * **Note:** This method is considerably slower than `_.pick`.
*
* @static
- * @memberOf _
* @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
- * @example
- *
- * _.isNaN(NaN);
- * // => true
- *
- * _.isNaN(new Number(NaN));
- * // => true
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {...(string|string[])} [paths] The property paths to omit.
+ * @returns {Object} Returns the new object.
+ * @example
*
- * isNaN(undefined);
- * // => true
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
*
- * _.isNaN(undefined);
- * // => false
+ * _.omit(object, ['a', 'c']);
+ * // => { 'b': '2' }
*/
- function isNaN(value) {
- // An `NaN` primitive is the only value that is not equal to itself.
- // Perform the `toStringTag` check first to avoid errors with some
- // ActiveX objects in IE.
- return isNumber(value) && value != +value;
- }
+ var omit = flatRest(function(object, paths) {
+ var result = {};
+ if (object == null) {
+ return result;
+ }
+ var isDeep = false;
+ paths = arrayMap(paths, function(path) {
+ path = castPath(path, object);
+ isDeep || (isDeep = path.length > 1);
+ return path;
+ });
+ copyObject(object, getAllKeysIn(object), result);
+ if (isDeep) {
+ result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);
+ }
+ var length = paths.length;
+ while (length--) {
+ baseUnset(result, paths[length]);
+ }
+ return result;
+ });
/**
- * Checks if `value` is a pristine native function.
- *
- * **Note:** This method can't reliably detect native functions in the presence
- * of the core-js package because core-js circumvents this kind of detection.
- * Despite multiple requests, the core-js maintainer has made it clear: any
- * attempt to fix the detection will be obstructed. As a result, we're left
- * with little choice but to throw an error. Unfortunately, this also affects
- * packages, like [babel-polyfill](https://www.npmjs.com/package/babel-polyfill),
- * which rely on core-js.
+ * The opposite of `_.pickBy`; this method creates an object composed of
+ * the own and inherited enumerable string keyed properties of `object` that
+ * `predicate` doesn't return truthy for. The predicate is invoked with two
+ * arguments: (value, key).
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a native function,
- * else `false`.
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function} [predicate=_.identity] The function invoked per property.
+ * @returns {Object} Returns the new object.
* @example
*
- * _.isNative(Array.prototype.push);
- * // => true
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
*
- * _.isNative(_);
- * // => false
+ * _.omitBy(object, _.isNumber);
+ * // => { 'b': '2' }
*/
- function isNative(value) {
- if (isMaskable(value)) {
- throw new Error(CORE_ERROR_TEXT);
- }
- return baseIsNative(value);
+ function omitBy(object, predicate) {
+ return pickBy(object, negate(getIteratee(predicate)));
}
/**
- * Checks if `value` is `null`.
+ * Creates an object composed of the picked `object` properties.
*
* @static
- * @memberOf _
* @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {...(string|string[])} [paths] The property paths to pick.
+ * @returns {Object} Returns the new object.
* @example
*
- * _.isNull(null);
- * // => true
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
*
- * _.isNull(void 0);
- * // => false
+ * _.pick(object, ['a', 'c']);
+ * // => { 'a': 1, 'c': 3 }
*/
- function isNull(value) {
- return value === null;
- }
+ var pick = flatRest(function(object, paths) {
+ return object == null ? {} : basePick(object, paths);
+ });
/**
- * Checks if `value` is `null` or `undefined`.
+ * Creates an object composed of the `object` properties `predicate` returns
+ * truthy for. The predicate is invoked with two arguments: (value, key).
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is nullish, else `false`.
+ * @category Object
+ * @param {Object} object The source object.
+ * @param {Function} [predicate=_.identity] The function invoked per property.
+ * @returns {Object} Returns the new object.
* @example
*
- * _.isNil(null);
- * // => true
- *
- * _.isNil(void 0);
- * // => true
+ * var object = { 'a': 1, 'b': '2', 'c': 3 };
*
- * _.isNil(NaN);
- * // => false
+ * _.pickBy(object, _.isNumber);
+ * // => { 'a': 1, 'c': 3 }
*/
- function isNil(value) {
- return value == null;
+ function pickBy(object, predicate) {
+ if (object == null) {
+ return {};
+ }
+ var props = arrayMap(getAllKeysIn(object), function(prop) {
+ return [prop];
+ });
+ predicate = getIteratee(predicate);
+ return basePickBy(object, props, function(value, path) {
+ return predicate(value, path[0]);
+ });
}
/**
- * Checks if `value` is classified as a `Number` primitive or object.
- *
- * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are
- * classified as numbers, use the `_.isFinite` method.
+ * This method is like `_.get` except that if the resolved value is a
+ * function it's invoked with the `this` binding of its parent object and
+ * its result is returned.
*
* @static
- * @memberOf _
* @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a number, else `false`.
+ * @memberOf _
+ * @category Object
+ * @param {Object} object The object to query.
+ * @param {Array|string} path The path of the property to resolve.
+ * @param {*} [defaultValue] The value returned for `undefined` resolved values.
+ * @returns {*} Returns the resolved value.
* @example
*
- * _.isNumber(3);
- * // => true
+ * var object = { 'a': [{ 'b': { 'c1': 3, 'c2': _.constant(4) } }] };
*
- * _.isNumber(Number.MIN_VALUE);
- * // => true
+ * _.result(object, 'a[0].b.c1');
+ * // => 3
*
- * _.isNumber(Infinity);
- * // => true
+ * _.result(object, 'a[0].b.c2');
+ * // => 4
*
- * _.isNumber('3');
- * // => false
+ * _.result(object, 'a[0].b.c3', 'default');
+ * // => 'default'
+ *
+ * _.result(object, 'a[0].b.c3', _.constant('default'));
+ * // => 'default'
*/
- function isNumber(value) {
- return typeof value == 'number' ||
- (isObjectLike(value) && baseGetTag(value) == numberTag);
+ function result(object, path, defaultValue) {
+ path = castPath(path, object);
+
+ var index = -1,
+ length = path.length;
+
+ // Ensure the loop is entered when path is empty.
+ if (!length) {
+ length = 1;
+ object = undefined;
+ }
+ while (++index < length) {
+ var value = object == null ? undefined : object[toKey(path[index])];
+ if (value === undefined) {
+ index = length;
+ value = defaultValue;
+ }
+ object = isFunction(value) ? value.call(object) : value;
+ }
+ return object;
}
/**
- * Checks if `value` is a plain object, that is, an object created by the
- * `Object` constructor or one with a `[[Prototype]]` of `null`.
+ * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
+ * it's created. Arrays are created for missing index properties while objects
+ * are created for all other missing properties. Use `_.setWith` to customize
+ * `path` creation.
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
- * @since 0.8.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+ * @since 3.7.0
+ * @category Object
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns `object`.
* @example
*
- * function Foo() {
- * this.a = 1;
- * }
- *
- * _.isPlainObject(new Foo);
- * // => false
- *
- * _.isPlainObject([1, 2, 3]);
- * // => false
+ * var object = { 'a': [{ 'b': { 'c': 3 } }] };
*
- * _.isPlainObject({ 'x': 0, 'y': 0 });
- * // => true
+ * _.set(object, 'a[0].b.c', 4);
+ * console.log(object.a[0].b.c);
+ * // => 4
*
- * _.isPlainObject(Object.create(null));
- * // => true
+ * _.set(object, ['x', '0', 'y', 'z'], 5);
+ * console.log(object.x[0].y.z);
+ * // => 5
*/
- function isPlainObject(value) {
- if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
- return false;
- }
- var proto = getPrototype(value);
- if (proto === null) {
- return true;
- }
- var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
- return typeof Ctor == 'function' && Ctor instanceof Ctor &&
- funcToString.call(Ctor) == objectCtorString;
+ function set(object, path, value) {
+ return object == null ? object : baseSet(object, path, value);
}
/**
- * Checks if `value` is classified as a `RegExp` object.
+ * This method is like `_.set` except that it accepts `customizer` which is
+ * invoked to produce the objects of `path`. If `customizer` returns `undefined`
+ * path creation is handled by the method instead. The `customizer` is invoked
+ * with three arguments: (nsValue, key, nsObject).
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
- * @since 0.1.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a regexp, else `false`.
+ * @since 4.0.0
+ * @category Object
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {*} value The value to set.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @returns {Object} Returns `object`.
* @example
*
- * _.isRegExp(/abc/);
- * // => true
+ * var object = {};
*
- * _.isRegExp('/abc/');
- * // => false
+ * _.setWith(object, '[0][1]', 'a', Object);
+ * // => { '0': { '1': 'a' } }
*/
- var isRegExp = nodeIsRegExp ? baseUnary(nodeIsRegExp) : baseIsRegExp;
+ function setWith(object, path, value, customizer) {
+ customizer = typeof customizer == 'function' ? customizer : undefined;
+ return object == null ? object : baseSet(object, path, value, customizer);
+ }
/**
- * Checks if `value` is a safe integer. An integer is safe if it's an IEEE-754
- * double precision number which isn't the result of a rounded unsafe integer.
- *
- * **Note:** This method is based on
- * [`Number.isSafeInteger`](https://mdn.io/Number/isSafeInteger).
+ * Creates an array of own enumerable string keyed-value pairs for `object`
+ * which can be consumed by `_.fromPairs`. If `object` is a map or set, its
+ * entries are returned.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a safe integer, else `false`.
+ * @alias entries
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the key-value pairs.
* @example
*
- * _.isSafeInteger(3);
- * // => true
- *
- * _.isSafeInteger(Number.MIN_VALUE);
- * // => false
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isSafeInteger(Infinity);
- * // => false
+ * Foo.prototype.c = 3;
*
- * _.isSafeInteger('3');
- * // => false
+ * _.toPairs(new Foo);
+ * // => [['a', 1], ['b', 2]] (iteration order is not guaranteed)
*/
- function isSafeInteger(value) {
- return isInteger(value) && value >= -MAX_SAFE_INTEGER && value <= MAX_SAFE_INTEGER;
- }
+ var toPairs = createToPairs(keys);
/**
- * Checks if `value` is classified as a `Set` object.
+ * Creates an array of own and inherited enumerable string keyed-value pairs
+ * for `object` which can be consumed by `_.fromPairs`. If `object` is a map
+ * or set, its entries are returned.
*
* @static
* @memberOf _
- * @since 4.3.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a set, else `false`.
+ * @since 4.0.0
+ * @alias entriesIn
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the key-value pairs.
* @example
*
- * _.isSet(new Set);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isSet(new WeakSet);
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.toPairsIn(new Foo);
+ * // => [['a', 1], ['b', 2], ['c', 3]] (iteration order is not guaranteed)
*/
- var isSet = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;
+ var toPairsIn = createToPairs(keysIn);
/**
- * Checks if `value` is classified as a `String` primitive or object.
+ * An alternative to `_.reduce`; this method transforms `object` to a new
+ * `accumulator` object which is the result of running each of its own
+ * enumerable string keyed properties thru `iteratee`, with each invocation
+ * potentially mutating the `accumulator` object. If `accumulator` is not
+ * provided, a new object with the same `[[Prototype]]` will be used. The
+ * iteratee is invoked with four arguments: (accumulator, value, key, object).
+ * Iteratee functions may exit iteration early by explicitly returning `false`.
*
* @static
- * @since 0.1.0
* @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a string, else `false`.
+ * @since 1.3.0
+ * @category Object
+ * @param {Object} object The object to iterate over.
+ * @param {Function} [iteratee=_.identity] The function invoked per iteration.
+ * @param {*} [accumulator] The custom accumulator value.
+ * @returns {*} Returns the accumulated value.
* @example
*
- * _.isString('abc');
- * // => true
+ * _.transform([2, 3, 4], function(result, n) {
+ * result.push(n *= n);
+ * return n % 2 == 0;
+ * }, []);
+ * // => [4, 9]
*
- * _.isString(1);
- * // => false
+ * _.transform({ 'a': 1, 'b': 2, 'c': 1 }, function(result, value, key) {
+ * (result[value] || (result[value] = [])).push(key);
+ * }, {});
+ * // => { '1': ['a', 'c'], '2': ['b'] }
*/
- function isString(value) {
- return typeof value == 'string' ||
- (!isArray(value) && isObjectLike(value) && baseGetTag(value) == stringTag);
+ function transform(object, iteratee, accumulator) {
+ var isArr = isArray(object),
+ isArrLike = isArr || isBuffer(object) || isTypedArray(object);
+
+ iteratee = getIteratee(iteratee, 4);
+ if (accumulator == null) {
+ var Ctor = object && object.constructor;
+ if (isArrLike) {
+ accumulator = isArr ? new Ctor : [];
+ }
+ else if (isObject(object)) {
+ accumulator = isFunction(Ctor) ? baseCreate(getPrototype(object)) : {};
+ }
+ else {
+ accumulator = {};
+ }
+ }
+ (isArrLike ? arrayEach : baseForOwn)(object, function(value, index, object) {
+ return iteratee(accumulator, value, index, object);
+ });
+ return accumulator;
}
/**
- * Checks if `value` is classified as a `Symbol` primitive or object.
+ * Removes the property at `path` of `object`.
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+ * @category Object
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to unset.
+ * @returns {boolean} Returns `true` if the property is deleted, else `false`.
* @example
*
- * _.isSymbol(Symbol.iterator);
+ * var object = { 'a': [{ 'b': { 'c': 7 } }] };
+ * _.unset(object, 'a[0].b.c');
* // => true
*
- * _.isSymbol('abc');
- * // => false
+ * console.log(object);
+ * // => { 'a': [{ 'b': {} }] };
+ *
+ * _.unset(object, ['a', '0', 'b', 'c']);
+ * // => true
+ *
+ * console.log(object);
+ * // => { 'a': [{ 'b': {} }] };
*/
- function isSymbol(value) {
- return typeof value == 'symbol' ||
- (isObjectLike(value) && baseGetTag(value) == symbolTag);
+ function unset(object, path) {
+ return object == null ? true : baseUnset(object, path);
}
/**
- * Checks if `value` is classified as a typed array.
+ * This method is like `_.set` except that accepts `updater` to produce the
+ * value to set. Use `_.updateWith` to customize `path` creation. The `updater`
+ * is invoked with one argument: (value).
+ *
+ * **Note:** This method mutates `object`.
*
* @static
* @memberOf _
- * @since 3.0.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+ * @since 4.6.0
+ * @category Object
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {Function} updater The function to produce the updated value.
+ * @returns {Object} Returns `object`.
* @example
*
- * _.isTypedArray(new Uint8Array);
- * // => true
+ * var object = { 'a': [{ 'b': { 'c': 3 } }] };
*
- * _.isTypedArray([]);
- * // => false
+ * _.update(object, 'a[0].b.c', function(n) { return n * n; });
+ * console.log(object.a[0].b.c);
+ * // => 9
+ *
+ * _.update(object, 'x[0].y.z', function(n) { return n ? n + 1 : 0; });
+ * console.log(object.x[0].y.z);
+ * // => 0
*/
- var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
+ function update(object, path, updater) {
+ return object == null ? object : baseUpdate(object, path, castFunction(updater));
+ }
/**
- * Checks if `value` is `undefined`.
+ * This method is like `_.update` except that it accepts `customizer` which is
+ * invoked to produce the objects of `path`. If `customizer` returns `undefined`
+ * path creation is handled by the method instead. The `customizer` is invoked
+ * with three arguments: (nsValue, key, nsObject).
+ *
+ * **Note:** This method mutates `object`.
*
* @static
- * @since 0.1.0
* @memberOf _
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
+ * @since 4.6.0
+ * @category Object
+ * @param {Object} object The object to modify.
+ * @param {Array|string} path The path of the property to set.
+ * @param {Function} updater The function to produce the updated value.
+ * @param {Function} [customizer] The function to customize assigned values.
+ * @returns {Object} Returns `object`.
* @example
*
- * _.isUndefined(void 0);
- * // => true
+ * var object = {};
*
- * _.isUndefined(null);
- * // => false
+ * _.updateWith(object, '[0][1]', _.constant('a'), Object);
+ * // => { '0': { '1': 'a' } }
*/
- function isUndefined(value) {
- return value === undefined;
+ function updateWith(object, path, updater, customizer) {
+ customizer = typeof customizer == 'function' ? customizer : undefined;
+ return object == null ? object : baseUpdate(object, path, castFunction(updater), customizer);
}
/**
- * Checks if `value` is classified as a `WeakMap` object.
+ * Creates an array of the own enumerable string keyed property values of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
*
* @static
+ * @since 0.1.0
* @memberOf _
- * @since 4.3.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a weak map, else `false`.
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property values.
* @example
*
- * _.isWeakMap(new WeakMap);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isWeakMap(new Map);
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.values(new Foo);
+ * // => [1, 2] (iteration order is not guaranteed)
+ *
+ * _.values('hi');
+ * // => ['h', 'i']
*/
- function isWeakMap(value) {
- return isObjectLike(value) && getTag(value) == weakMapTag;
+ function values(object) {
+ return object == null ? [] : baseValues(object, keys(object));
}
/**
- * Checks if `value` is classified as a `WeakSet` object.
+ * Creates an array of the own and inherited enumerable string keyed property
+ * values of `object`.
+ *
+ * **Note:** Non-object values are coerced to objects.
*
* @static
* @memberOf _
- * @since 4.3.0
- * @category Lang
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if `value` is a weak set, else `false`.
+ * @since 3.0.0
+ * @category Object
+ * @param {Object} object The object to query.
+ * @returns {Array} Returns the array of property values.
* @example
*
- * _.isWeakSet(new WeakSet);
- * // => true
+ * function Foo() {
+ * this.a = 1;
+ * this.b = 2;
+ * }
*
- * _.isWeakSet(new Set);
- * // => false
+ * Foo.prototype.c = 3;
+ *
+ * _.valuesIn(new Foo);
+ * // => [1, 2, 3] (iteration order is not guaranteed)
*/
- function isWeakSet(value) {
- return isObjectLike(value) && baseGetTag(value) == weakSetTag;
+ function valuesIn(object) {
+ return object == null ? [] : baseValues(object, keysIn(object));
}
+ /*------------------------------------------------------------------------*/
+
/**
- * Checks if `value` is less than `other`.
+ * Clamps `number` within the inclusive `lower` and `upper` bounds.
*
* @static
* @memberOf _
- * @since 3.9.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if `value` is less than `other`,
- * else `false`.
- * @see _.gt
+ * @since 4.0.0
+ * @category Number
+ * @param {number} number The number to clamp.
+ * @param {number} [lower] The lower bound.
+ * @param {number} upper The upper bound.
+ * @returns {number} Returns the clamped number.
* @example
*
- * _.lt(1, 3);
- * // => true
- *
- * _.lt(3, 3);
- * // => false
+ * _.clamp(-10, -5, 5);
+ * // => -5
*
- * _.lt(3, 1);
- * // => false
+ * _.clamp(10, -5, 5);
+ * // => 5
*/
- var lt = createRelationalOperation(baseLt);
+ function clamp(number, lower, upper) {
+ if (upper === undefined) {
+ upper = lower;
+ lower = undefined;
+ }
+ if (upper !== undefined) {
+ upper = toNumber(upper);
+ upper = upper === upper ? upper : 0;
+ }
+ if (lower !== undefined) {
+ lower = toNumber(lower);
+ lower = lower === lower ? lower : 0;
+ }
+ return baseClamp(toNumber(number), lower, upper);
+ }
/**
- * Checks if `value` is less than or equal to `other`.
+ * Checks if `n` is between `start` and up to, but not including, `end`. If
+ * `end` is not specified, it's set to `start` with `start` then set to `0`.
+ * If `start` is greater than `end` the params are swapped to support
+ * negative ranges.
*
* @static
* @memberOf _
- * @since 3.9.0
- * @category Lang
- * @param {*} value The value to compare.
- * @param {*} other The other value to compare.
- * @returns {boolean} Returns `true` if `value` is less than or equal to
- * `other`, else `false`.
- * @see _.gte
+ * @since 3.3.0
+ * @category Number
+ * @param {number} number The number to check.
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @returns {boolean} Returns `true` if `number` is in the range, else `false`.
+ * @see _.range, _.rangeRight
* @example
*
- * _.lte(1, 3);
+ * _.inRange(3, 2, 4);
* // => true
*
- * _.lte(3, 3);
+ * _.inRange(4, 8);
* // => true
*
- * _.lte(3, 1);
+ * _.inRange(4, 2);
* // => false
- */
- var lte = createRelationalOperation(function(value, other) {
- return value <= other;
- });
-
- /**
- * Converts `value` to an array.
- *
- * @static
- * @since 0.1.0
- * @memberOf _
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {Array} Returns the converted array.
- * @example
*
- * _.toArray({ 'a': 1, 'b': 2 });
- * // => [1, 2]
+ * _.inRange(2, 2);
+ * // => false
*
- * _.toArray('abc');
- * // => ['a', 'b', 'c']
+ * _.inRange(1.2, 2);
+ * // => true
*
- * _.toArray(1);
- * // => []
+ * _.inRange(5.2, 4);
+ * // => false
*
- * _.toArray(null);
- * // => []
+ * _.inRange(-3, -2, -6);
+ * // => true
*/
- function toArray(value) {
- if (!value) {
- return [];
- }
- if (isArrayLike(value)) {
- return isString(value) ? stringToArray(value) : copyArray(value);
- }
- if (symIterator && value[symIterator]) {
- return iteratorToArray(value[symIterator]());
+ function inRange(number, start, end) {
+ start = toFinite(start);
+ if (end === undefined) {
+ end = start;
+ start = 0;
+ } else {
+ end = toFinite(end);
}
- var tag = getTag(value),
- func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values);
-
- return func(value);
+ number = toNumber(number);
+ return baseInRange(number, start, end);
}
/**
- * Converts `value` to a finite number.
+ * Produces a random number between the inclusive `lower` and `upper` bounds.
+ * If only one argument is provided a number between `0` and the given number
+ * is returned. If `floating` is `true`, or either `lower` or `upper` are
+ * floats, a floating-point number is returned instead of an integer.
+ *
+ * **Note:** JavaScript follows the IEEE-754 standard for resolving
+ * floating-point values which can produce unexpected results.
*
* @static
* @memberOf _
- * @since 4.12.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {number} Returns the converted number.
+ * @since 0.7.0
+ * @category Number
+ * @param {number} [lower=0] The lower bound.
+ * @param {number} [upper=1] The upper bound.
+ * @param {boolean} [floating] Specify returning a floating-point number.
+ * @returns {number} Returns the random number.
* @example
*
- * _.toFinite(3.2);
- * // => 3.2
+ * _.random(0, 5);
+ * // => an integer between 0 and 5
*
- * _.toFinite(Number.MIN_VALUE);
- * // => 5e-324
+ * _.random(5);
+ * // => also an integer between 0 and 5
*
- * _.toFinite(Infinity);
- * // => 1.7976931348623157e+308
+ * _.random(5, true);
+ * // => a floating-point number between 0 and 5
*
- * _.toFinite('3.2');
- * // => 3.2
+ * _.random(1.2, 5.2);
+ * // => a floating-point number between 1.2 and 5.2
*/
- function toFinite(value) {
- if (!value) {
- return value === 0 ? value : 0;
+ function random(lower, upper, floating) {
+ if (floating && typeof floating != 'boolean' && isIterateeCall(lower, upper, floating)) {
+ upper = floating = undefined;
}
- value = toNumber(value);
- if (value === INFINITY || value === -INFINITY) {
- var sign = (value < 0 ? -1 : 1);
- return sign * MAX_INTEGER;
+ if (floating === undefined) {
+ if (typeof upper == 'boolean') {
+ floating = upper;
+ upper = undefined;
+ }
+ else if (typeof lower == 'boolean') {
+ floating = lower;
+ lower = undefined;
+ }
}
- return value === value ? value : 0;
+ if (lower === undefined && upper === undefined) {
+ lower = 0;
+ upper = 1;
+ }
+ else {
+ lower = toFinite(lower);
+ if (upper === undefined) {
+ upper = lower;
+ lower = 0;
+ } else {
+ upper = toFinite(upper);
+ }
+ }
+ if (lower > upper) {
+ var temp = lower;
+ lower = upper;
+ upper = temp;
+ }
+ if (floating || lower % 1 || upper % 1) {
+ var rand = nativeRandom();
+ return nativeMin(lower + (rand * (upper - lower + freeParseFloat('1e-' + ((rand + '').length - 1)))), upper);
+ }
+ return baseRandom(lower, upper);
}
+ /*------------------------------------------------------------------------*/
+
/**
- * Converts `value` to an integer.
- *
- * **Note:** This method is loosely based on
- * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
+ * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {number} Returns the converted integer.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the camel cased string.
* @example
*
- * _.toInteger(3.2);
- * // => 3
+ * _.camelCase('Foo Bar');
+ * // => 'fooBar'
*
- * _.toInteger(Number.MIN_VALUE);
- * // => 0
+ * _.camelCase('--foo-bar--');
+ * // => 'fooBar'
+ *
+ * _.camelCase('__FOO_BAR__');
+ * // => 'fooBar'
+ */
+ var camelCase = createCompounder(function(result, word, index) {
+ word = word.toLowerCase();
+ return result + (index ? capitalize(word) : word);
+ });
+
+ /**
+ * Converts the first character of `string` to upper case and the remaining
+ * to lower case.
*
- * _.toInteger(Infinity);
- * // => 1.7976931348623157e+308
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to capitalize.
+ * @returns {string} Returns the capitalized string.
+ * @example
*
- * _.toInteger('3.2');
- * // => 3
+ * _.capitalize('FRED');
+ * // => 'Fred'
*/
- function toInteger(value) {
- var result = toFinite(value),
- remainder = result % 1;
-
- return result === result ? (remainder ? result - remainder : result) : 0;
+ function capitalize(string) {
+ return upperFirst(toString(string).toLowerCase());
}
/**
- * Converts `value` to an integer suitable for use as the length of an
- * array-like object.
- *
- * **Note:** This method is based on
- * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+ * Deburrs `string` by converting
+ * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
+ * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A)
+ * letters to basic Latin letters and removing
+ * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {number} Returns the converted integer.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to deburr.
+ * @returns {string} Returns the deburred string.
* @example
*
- * _.toLength(3.2);
- * // => 3
- *
- * _.toLength(Number.MIN_VALUE);
- * // => 0
- *
- * _.toLength(Infinity);
- * // => 4294967295
- *
- * _.toLength('3.2');
- * // => 3
+ * _.deburr('déjà vu');
+ * // => 'deja vu'
*/
- function toLength(value) {
- return value ? baseClamp(toInteger(value), 0, MAX_ARRAY_LENGTH) : 0;
+ function deburr(string) {
+ string = toString(string);
+ return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '');
}
/**
- * Converts `value` to a number.
+ * Checks if `string` ends with the given target string.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to process.
- * @returns {number} Returns the number.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to inspect.
+ * @param {string} [target] The string to search for.
+ * @param {number} [position=string.length] The position to search up to.
+ * @returns {boolean} Returns `true` if `string` ends with `target`,
+ * else `false`.
* @example
*
- * _.toNumber(3.2);
- * // => 3.2
- *
- * _.toNumber(Number.MIN_VALUE);
- * // => 5e-324
+ * _.endsWith('abc', 'c');
+ * // => true
*
- * _.toNumber(Infinity);
- * // => Infinity
+ * _.endsWith('abc', 'b');
+ * // => false
*
- * _.toNumber('3.2');
- * // => 3.2
+ * _.endsWith('abc', 'b', 2);
+ * // => true
*/
- function toNumber(value) {
- if (typeof value == 'number') {
- return value;
- }
- if (isSymbol(value)) {
- return NAN;
- }
- if (isObject(value)) {
- var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
- value = isObject(other) ? (other + '') : other;
- }
- if (typeof value != 'string') {
- return value === 0 ? value : +value;
- }
- value = baseTrim(value);
- var isBinary = reIsBinary.test(value);
- return (isBinary || reIsOctal.test(value))
- ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
- : (reIsBadHex.test(value) ? NAN : +value);
+ function endsWith(string, target, position) {
+ string = toString(string);
+ target = baseToString(target);
+
+ var length = string.length;
+ position = position === undefined
+ ? length
+ : baseClamp(toInteger(position), 0, length);
+
+ var end = position;
+ position -= target.length;
+ return position >= 0 && string.slice(position, end) == target;
}
/**
- * Converts `value` to a plain object flattening inherited enumerable string
- * keyed properties of `value` to own properties of the plain object.
+ * Converts the characters "&", "<", ">", '"', and "'" in `string` to their
+ * corresponding HTML entities.
*
- * @static
- * @memberOf _
- * @since 3.0.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {Object} Returns the converted plain object.
- * @example
+ * **Note:** No other characters are escaped. To escape additional
+ * characters use a third-party library like [_he_](https://mths.be/he).
*
- * function Foo() {
- * this.b = 2;
- * }
+ * Though the ">" character is escaped for symmetry, characters like
+ * ">" and "/" don't need escaping in HTML and have no special meaning
+ * unless they're part of a tag or unquoted attribute value. See
+ * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands)
+ * (under "semi-related fun fact") for more details.
*
- * Foo.prototype.c = 3;
+ * When working with HTML you should always
+ * [quote attribute values](http://wonko.com/post/html-escaping) to reduce
+ * XSS vectors.
*
- * _.assign({ 'a': 1 }, new Foo);
- * // => { 'a': 1, 'b': 2 }
+ * @static
+ * @since 0.1.0
+ * @memberOf _
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
*
- * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
- * // => { 'a': 1, 'b': 2, 'c': 3 }
+ * _.escape('fred, barney, & pebbles');
+ * // => 'fred, barney, & pebbles'
*/
- function toPlainObject(value) {
- return copyObject(value, keysIn(value));
+ function escape(string) {
+ string = toString(string);
+ return (string && reHasUnescapedHtml.test(string))
+ ? string.replace(reUnescapedHtml, escapeHtmlChar)
+ : string;
}
/**
- * Converts `value` to a safe integer. A safe integer can be compared and
- * represented correctly.
+ * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
+ * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {number} Returns the converted integer.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
* @example
*
- * _.toSafeInteger(3.2);
- * // => 3
- *
- * _.toSafeInteger(Number.MIN_VALUE);
- * // => 0
- *
- * _.toSafeInteger(Infinity);
- * // => 9007199254740991
- *
- * _.toSafeInteger('3.2');
- * // => 3
+ * _.escapeRegExp('[lodash](https://lodash.com/)');
+ * // => '\[lodash\]\(https://lodash\.com/\)'
*/
- function toSafeInteger(value) {
- return value
- ? baseClamp(toInteger(value), -MAX_SAFE_INTEGER, MAX_SAFE_INTEGER)
- : (value === 0 ? value : 0);
+ function escapeRegExp(string) {
+ string = toString(string);
+ return (string && reHasRegExpChar.test(string))
+ ? string.replace(reRegExpChar, '\\$&')
+ : string;
}
/**
- * Converts `value` to a string. An empty string is returned for `null`
- * and `undefined` values. The sign of `-0` is preserved.
+ * Converts `string` to
+ * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles).
*
* @static
* @memberOf _
- * @since 4.0.0
- * @category Lang
- * @param {*} value The value to convert.
- * @returns {string} Returns the converted string.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the kebab cased string.
* @example
*
- * _.toString(null);
- * // => ''
+ * _.kebabCase('Foo Bar');
+ * // => 'foo-bar'
*
- * _.toString(-0);
- * // => '-0'
+ * _.kebabCase('fooBar');
+ * // => 'foo-bar'
*
- * _.toString([1, 2, 3]);
- * // => '1,2,3'
+ * _.kebabCase('__FOO_BAR__');
+ * // => 'foo-bar'
*/
- function toString(value) {
- return value == null ? '' : baseToString(value);
- }
-
- /*------------------------------------------------------------------------*/
+ var kebabCase = createCompounder(function(result, word, index) {
+ return result + (index ? '-' : '') + word.toLowerCase();
+ });
/**
- * Assigns own enumerable string keyed properties of source objects to the
- * destination object. Source objects are applied from left to right.
- * Subsequent sources overwrite property assignments of previous sources.
- *
- * **Note:** This method mutates `object` and is loosely based on
- * [`Object.assign`](https://mdn.io/Object/assign).
+ * Converts `string`, as space separated words, to lower case.
*
* @static
* @memberOf _
- * @since 0.10.0
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} [sources] The source objects.
- * @returns {Object} Returns `object`.
- * @see _.assignIn
+ * @since 4.0.0
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the lower cased string.
* @example
*
- * function Foo() {
- * this.a = 1;
- * }
- *
- * function Bar() {
- * this.c = 3;
- * }
- *
- * Foo.prototype.b = 2;
- * Bar.prototype.d = 4;
+ * _.lowerCase('--Foo-Bar--');
+ * // => 'foo bar'
*
- * _.assign({ 'a': 0 }, new Foo, new Bar);
- * // => { 'a': 1, 'c': 3 }
+ * _.lowerCase('fooBar');
+ * // => 'foo bar'
+ *
+ * _.lowerCase('__FOO_BAR__');
+ * // => 'foo bar'
*/
- var assign = createAssigner(function(object, source) {
- if (isPrototype(source) || isArrayLike(source)) {
- copyObject(source, keys(source), object);
- return;
- }
- for (var key in source) {
- if (hasOwnProperty.call(source, key)) {
- assignValue(object, key, source[key]);
- }
- }
+ var lowerCase = createCompounder(function(result, word, index) {
+ return result + (index ? ' ' : '') + word.toLowerCase();
});
/**
- * This method is like `_.assign` except that it iterates over own and
- * inherited source properties.
- *
- * **Note:** This method mutates `object`.
+ * Converts the first character of `string` to lower case.
*
* @static
* @memberOf _
* @since 4.0.0
- * @alias extend
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} [sources] The source objects.
- * @returns {Object} Returns `object`.
- * @see _.assign
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the converted string.
* @example
*
- * function Foo() {
- * this.a = 1;
- * }
+ * _.lowerFirst('Fred');
+ * // => 'fred'
*
- * function Bar() {
- * this.c = 3;
- * }
+ * _.lowerFirst('FRED');
+ * // => 'fRED'
+ */
+ var lowerFirst = createCaseFirst('toLowerCase');
+
+ /**
+ * Pads `string` on the left and right sides if it's shorter than `length`.
+ * Padding characters are truncated if they can't be evenly divided by `length`.
*
- * Foo.prototype.b = 2;
- * Bar.prototype.d = 4;
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
*
- * _.assignIn({ 'a': 0 }, new Foo, new Bar);
- * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
+ * _.pad('abc', 8);
+ * // => ' abc '
+ *
+ * _.pad('abc', 8, '_-');
+ * // => '_-abc_-_'
+ *
+ * _.pad('abc', 3);
+ * // => 'abc'
*/
- var assignIn = createAssigner(function(object, source) {
- copyObject(source, keysIn(source), object);
- });
+ function pad(string, length, chars) {
+ string = toString(string);
+ length = toInteger(length);
+
+ var strLength = length ? stringSize(string) : 0;
+ if (!length || strLength >= length) {
+ return string;
+ }
+ var mid = (length - strLength) / 2;
+ return (
+ createPadding(nativeFloor(mid), chars) +
+ string +
+ createPadding(nativeCeil(mid), chars)
+ );
+ }
/**
- * This method is like `_.assignIn` except that it accepts `customizer`
- * which is invoked to produce the assigned values. If `customizer` returns
- * `undefined`, assignment is handled by the method instead. The `customizer`
- * is invoked with five arguments: (objValue, srcValue, key, object, source).
- *
- * **Note:** This method mutates `object`.
+ * Pads `string` on the right side if it's shorter than `length`. Padding
+ * characters are truncated if they exceed `length`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @alias extendWith
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} sources The source objects.
- * @param {Function} [customizer] The function to customize assigned values.
- * @returns {Object} Returns `object`.
- * @see _.assignWith
+ * @category String
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
* @example
*
- * function customizer(objValue, srcValue) {
- * return _.isUndefined(objValue) ? srcValue : objValue;
- * }
+ * _.padEnd('abc', 6);
+ * // => 'abc '
*
- * var defaults = _.partialRight(_.assignInWith, customizer);
+ * _.padEnd('abc', 6, '_-');
+ * // => 'abc_-_'
*
- * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
- * // => { 'a': 1, 'b': 2 }
+ * _.padEnd('abc', 3);
+ * // => 'abc'
*/
- var assignInWith = createAssigner(function(object, source, srcIndex, customizer) {
- copyObject(source, keysIn(source), object, customizer);
- });
+ function padEnd(string, length, chars) {
+ string = toString(string);
+ length = toInteger(length);
+
+ var strLength = length ? stringSize(string) : 0;
+ return (length && strLength < length)
+ ? (string + createPadding(length - strLength, chars))
+ : string;
+ }
/**
- * This method is like `_.assign` except that it accepts `customizer`
- * which is invoked to produce the assigned values. If `customizer` returns
- * `undefined`, assignment is handled by the method instead. The `customizer`
- * is invoked with five arguments: (objValue, srcValue, key, object, source).
- *
- * **Note:** This method mutates `object`.
+ * Pads `string` on the left side if it's shorter than `length`. Padding
+ * characters are truncated if they exceed `length`.
*
* @static
* @memberOf _
* @since 4.0.0
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} sources The source objects.
- * @param {Function} [customizer] The function to customize assigned values.
- * @returns {Object} Returns `object`.
- * @see _.assignInWith
+ * @category String
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
* @example
*
- * function customizer(objValue, srcValue) {
- * return _.isUndefined(objValue) ? srcValue : objValue;
- * }
+ * _.padStart('abc', 6);
+ * // => ' abc'
*
- * var defaults = _.partialRight(_.assignWith, customizer);
+ * _.padStart('abc', 6, '_-');
+ * // => '_-_abc'
*
- * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
- * // => { 'a': 1, 'b': 2 }
+ * _.padStart('abc', 3);
+ * // => 'abc'
*/
- var assignWith = createAssigner(function(object, source, srcIndex, customizer) {
- copyObject(source, keys(source), object, customizer);
- });
+ function padStart(string, length, chars) {
+ string = toString(string);
+ length = toInteger(length);
+
+ var strLength = length ? stringSize(string) : 0;
+ return (length && strLength < length)
+ ? (createPadding(length - strLength, chars) + string)
+ : string;
+ }
/**
- * Creates an array of values corresponding to `paths` of `object`.
+ * Converts `string` to an integer of the specified radix. If `radix` is
+ * `undefined` or `0`, a `radix` of `10` is used unless `value` is a
+ * hexadecimal, in which case a `radix` of `16` is used.
+ *
+ * **Note:** This method aligns with the
+ * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`.
*
* @static
* @memberOf _
- * @since 1.0.0
- * @category Object
- * @param {Object} object The object to iterate over.
- * @param {...(string|string[])} [paths] The property paths to pick.
- * @returns {Array} Returns the picked values.
+ * @since 1.1.0
+ * @category String
+ * @param {string} string The string to convert.
+ * @param {number} [radix=10] The radix to interpret `value` by.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {number} Returns the converted integer.
* @example
*
- * var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
+ * _.parseInt('08');
+ * // => 8
*
- * _.at(object, ['a[0].b.c', 'a[1]']);
- * // => [3, 4]
+ * _.map(['6', '08', '10'], _.parseInt);
+ * // => [6, 8, 10]
*/
- var at = flatRest(baseAt);
+ function parseInt(string, radix, guard) {
+ if (guard || radix == null) {
+ radix = 0;
+ } else if (radix) {
+ radix = +radix;
+ }
+ return nativeParseInt(toString(string).replace(reTrimStart, ''), radix || 0);
+ }
/**
- * Creates an object that inherits from the `prototype` object. If a
- * `properties` object is given, its own enumerable string keyed properties
- * are assigned to the created object.
+ * Repeats the given string `n` times.
*
* @static
* @memberOf _
- * @since 2.3.0
- * @category Object
- * @param {Object} prototype The object to inherit from.
- * @param {Object} [properties] The properties to assign to the object.
- * @returns {Object} Returns the new object.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to repeat.
+ * @param {number} [n=1] The number of times to repeat the string.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {string} Returns the repeated string.
* @example
*
- * function Shape() {
- * this.x = 0;
- * this.y = 0;
- * }
- *
- * function Circle() {
- * Shape.call(this);
- * }
- *
- * Circle.prototype = _.create(Shape.prototype, {
- * 'constructor': Circle
- * });
+ * _.repeat('*', 3);
+ * // => '***'
*
- * var circle = new Circle;
- * circle instanceof Circle;
- * // => true
+ * _.repeat('abc', 2);
+ * // => 'abcabc'
*
- * circle instanceof Shape;
- * // => true
+ * _.repeat('abc', 0);
+ * // => ''
*/
- function create(prototype, properties) {
- var result = baseCreate(prototype);
- return properties == null ? result : baseAssign(result, properties);
+ function repeat(string, n, guard) {
+ if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {
+ n = 1;
+ } else {
+ n = toInteger(n);
+ }
+ return baseRepeat(toString(string), n);
}
/**
- * Assigns own and inherited enumerable string keyed properties of source
- * objects to the destination object for all destination properties that
- * resolve to `undefined`. Source objects are applied from left to right.
- * Once a property is set, additional values of the same property are ignored.
+ * Replaces matches for `pattern` in `string` with `replacement`.
*
- * **Note:** This method mutates `object`.
+ * **Note:** This method is based on
+ * [`String#replace`](https://mdn.io/String/replace).
*
* @static
- * @since 0.1.0
* @memberOf _
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} [sources] The source objects.
- * @returns {Object} Returns `object`.
- * @see _.defaultsDeep
+ * @since 4.0.0
+ * @category String
+ * @param {string} [string=''] The string to modify.
+ * @param {RegExp|string} pattern The pattern to replace.
+ * @param {Function|string} replacement The match replacement.
+ * @returns {string} Returns the modified string.
* @example
*
- * _.defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 });
- * // => { 'a': 1, 'b': 2 }
+ * _.replace('Hi Fred', 'Fred', 'Barney');
+ * // => 'Hi Barney'
*/
- var defaults = baseRest(function(object, sources) {
- object = Object(object);
-
- var index = -1;
- var length = sources.length;
- var guard = length > 2 ? sources[2] : undefined;
-
- if (guard && isIterateeCall(sources[0], sources[1], guard)) {
- length = 1;
- }
-
- while (++index < length) {
- var source = sources[index];
- var props = keysIn(source);
- var propsIndex = -1;
- var propsLength = props.length;
-
- while (++propsIndex < propsLength) {
- var key = props[propsIndex];
- var value = object[key];
-
- if (value === undefined ||
- (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) {
- object[key] = source[key];
- }
- }
- }
+ function replace() {
+ var args = arguments,
+ string = toString(args[0]);
- return object;
- });
+ return args.length < 3 ? string : string.replace(args[1], args[2]);
+ }
/**
- * This method is like `_.defaults` except that it recursively assigns
- * default properties.
- *
- * **Note:** This method mutates `object`.
+ * Converts `string` to
+ * [snake case](https://en.wikipedia.org/wiki/Snake_case).
*
* @static
* @memberOf _
- * @since 3.10.0
- * @category Object
- * @param {Object} object The destination object.
- * @param {...Object} [sources] The source objects.
- * @returns {Object} Returns `object`.
- * @see _.defaults
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the snake cased string.
* @example
*
- * _.defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } });
- * // => { 'a': { 'b': 2, 'c': 3 } }
+ * _.snakeCase('Foo Bar');
+ * // => 'foo_bar'
+ *
+ * _.snakeCase('fooBar');
+ * // => 'foo_bar'
+ *
+ * _.snakeCase('--FOO-BAR--');
+ * // => 'foo_bar'
*/
- var defaultsDeep = baseRest(function(args) {
- args.push(undefined, customDefaultsMerge);
- return apply(mergeWith, undefined, args);
+ var snakeCase = createCompounder(function(result, word, index) {
+ return result + (index ? '_' : '') + word.toLowerCase();
});
/**
- * This method is like `_.find` except that it returns the key of the first
- * element `predicate` returns truthy for instead of the element itself.
+ * Splits `string` by `separator`.
+ *
+ * **Note:** This method is based on
+ * [`String#split`](https://mdn.io/String/split).
*
* @static
* @memberOf _
- * @since 1.1.0
- * @category Object
- * @param {Object} object The object to inspect.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {string|undefined} Returns the key of the matched element,
- * else `undefined`.
+ * @since 4.0.0
+ * @category String
+ * @param {string} [string=''] The string to split.
+ * @param {RegExp|string} separator The separator pattern to split by.
+ * @param {number} [limit] The length to truncate results to.
+ * @returns {Array} Returns the string segments.
* @example
*
- * var users = {
- * 'barney': { 'age': 36, 'active': true },
- * 'fred': { 'age': 40, 'active': false },
- * 'pebbles': { 'age': 1, 'active': true }
- * };
+ * _.split('a-b-c', '-', 2);
+ * // => ['a', 'b']
+ */
+ function split(string, separator, limit) {
+ if (limit && typeof limit != 'number' && isIterateeCall(string, separator, limit)) {
+ separator = limit = undefined;
+ }
+ limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0;
+ if (!limit) {
+ return [];
+ }
+ string = toString(string);
+ if (string && (
+ typeof separator == 'string' ||
+ (separator != null && !isRegExp(separator))
+ )) {
+ separator = baseToString(separator);
+ if (!separator && hasUnicode(string)) {
+ return castSlice(stringToArray(string), 0, limit);
+ }
+ }
+ return string.split(separator, limit);
+ }
+
+ /**
+ * Converts `string` to
+ * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage).
*
- * _.findKey(users, function(o) { return o.age < 40; });
- * // => 'barney' (iteration order is not guaranteed)
+ * @static
+ * @memberOf _
+ * @since 3.1.0
+ * @category String
+ * @param {string} [string=''] The string to convert.
+ * @returns {string} Returns the start cased string.
+ * @example
*
- * // The `_.matches` iteratee shorthand.
- * _.findKey(users, { 'age': 1, 'active': true });
- * // => 'pebbles'
+ * _.startCase('--foo-bar--');
+ * // => 'Foo Bar'
*
- * // The `_.matchesProperty` iteratee shorthand.
- * _.findKey(users, ['active', false]);
- * // => 'fred'
+ * _.startCase('fooBar');
+ * // => 'Foo Bar'
*
- * // The `_.property` iteratee shorthand.
- * _.findKey(users, 'active');
- * // => 'barney'
+ * _.startCase('__FOO_BAR__');
+ * // => 'FOO BAR'
*/
- function findKey(object, predicate) {
- return baseFindKey(object, getIteratee(predicate, 3), baseForOwn);
- }
+ var startCase = createCompounder(function(result, word, index) {
+ return result + (index ? ' ' : '') + upperFirst(word);
+ });
/**
- * This method is like `_.findKey` except that it iterates over elements of
- * a collection in the opposite order.
+ * Checks if `string` starts with the given target string.
*
* @static
* @memberOf _
- * @since 2.0.0
- * @category Object
- * @param {Object} object The object to inspect.
- * @param {Function} [predicate=_.identity] The function invoked per iteration.
- * @returns {string|undefined} Returns the key of the matched element,
- * else `undefined`.
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to inspect.
+ * @param {string} [target] The string to search for.
+ * @param {number} [position=0] The position to search from.
+ * @returns {boolean} Returns `true` if `string` starts with `target`,
+ * else `false`.
* @example
*
- * var users = {
- * 'barney': { 'age': 36, 'active': true },
- * 'fred': { 'age': 40, 'active': false },
- * 'pebbles': { 'age': 1, 'active': true }
- * };
- *
- * _.findLastKey(users, function(o) { return o.age < 40; });
- * // => returns 'pebbles' assuming `_.findKey` returns 'barney'
- *
- * // The `_.matches` iteratee shorthand.
- * _.findLastKey(users, { 'age': 36, 'active': true });
- * // => 'barney'
+ * _.startsWith('abc', 'a');
+ * // => true
*
- * // The `_.matchesProperty` iteratee shorthand.
- * _.findLastKey(users, ['active', false]);
- * // => 'fred'
+ * _.startsWith('abc', 'b');
+ * // => false
*
- * // The `_.property` iteratee shorthand.
- * _.findLastKey(users, 'active');
- * // => 'pebbles'
+ * _.startsWith('abc', 'b', 1);
+ * // => true
*/
- function findLastKey(object, predicate) {
- return baseFindKey(object, getIteratee(predicate, 3), baseForOwnRight);
+ function startsWith(string, target, position) {
+ string = toString(string);
+ position = position == null
+ ? 0
+ : baseClamp(toInteger(position), 0, string.length);
+
+ target = baseToString(target);
+ return string.slice(position, position + target.length) == target;
}
/**
- * Iterates over own and inherited enumerable string keyed properties of an
- * object and invokes `iteratee` for each property. The iteratee is invoked
- * with three arguments: (value, key, object). Iteratee functions may exit
- * iteration early by explicitly returning `false`.
+ * Creates a compiled template function that can interpolate data properties
+ * in "interpolate" delimiters, HTML-escape interpolated data properties in
+ * "escape" delimiters, and execute JavaScript in "evaluate" delimiters. Data
+ * properties may be accessed as free variables in the template. If a setting
+ * object is given, it takes precedence over `_.templateSettings` values.
+ *
+ * **Note:** In the development build `_.template` utilizes
+ * [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
+ * for easier debugging.
+ *
+ * For more information on precompiling templates see
+ * [lodash's custom builds documentation](https://lodash.com/custom-builds).
+ *
+ * For more information on Chrome extension sandboxes see
+ * [Chrome's extensions documentation](https://developer.chrome.com/extensions/sandboxingEval).
*
* @static
+ * @since 0.1.0
* @memberOf _
- * @since 0.3.0
- * @category Object
- * @param {Object} object The object to iterate over.
- * @param {Function} [iteratee=_.identity] The function invoked per iteration.
- * @returns {Object} Returns `object`.
- * @see _.forInRight
+ * @category String
+ * @param {string} [string=''] The template string.
+ * @param {Object} [options={}] The options object.
+ * @param {RegExp} [options.escape=_.templateSettings.escape]
+ * The HTML "escape" delimiter.
+ * @param {RegExp} [options.evaluate=_.templateSettings.evaluate]
+ * The "evaluate" delimiter.
+ * @param {Object} [options.imports=_.templateSettings.imports]
+ * An object to import into the template as free variables.
+ * @param {RegExp} [options.interpolate=_.templateSettings.interpolate]
+ * The "interpolate" delimiter.
+ * @param {string} [options.sourceURL='lodash.templateSources[n]']
+ * The sourceURL of the compiled template.
+ * @param {string} [options.variable='obj']
+ * The data object variable name.
+ * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+ * @returns {Function} Returns the compiled template function.
* @example
*
- * function Foo() {
- * this.a = 1;
- * this.b = 2;
- * }
+ * // Use the "interpolate" delimiter to create a compiled template.
+ * var compiled = _.template('hello <%= user %>!');
+ * compiled({ 'user': 'fred' });
+ * // => 'hello fred!'
+ *
+ * // Use the HTML "escape" delimiter to escape data property values.
+ * var compiled = _.template('<%- value %> ');
+ * compiled({ 'value': '
-{% endblock %}
\ No newline at end of file
+{% endblock %}
diff --git a/requirements.txt b/requirements.txt
index 8f5b6126..a9c41290 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,5 @@
#
-# This file is autogenerated by pip-compile with Python 3.10
+# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# ./scripts/update_dependencies.sh
@@ -10,8 +10,6 @@
# dlgr-griduniverse
# via -r requirements.in
-appnope==0.1.3
- # via ipython
apscheduler==3.10.1
# via dallinger
asttokens==2.2.1
@@ -26,9 +24,9 @@ backcall==0.2.0
# via ipython
blinker==1.6.2
# via flask
-boto3==1.28.5
+boto3==1.28.11
# via dallinger
-botocore==1.31.5
+botocore==1.31.11
# via
# boto3
# s3transfer
@@ -38,7 +36,7 @@ build==0.10.0
# pip-tools
cached-property==1.5.2
# via dallinger
-certifi==2023.5.7
+certifi==2023.7.22
# via
# requests
# selenium
@@ -57,12 +55,10 @@ cryptography==41.0.2
decorator==5.1.1
# via ipython
exceptiongroup==1.1.2
- # via
- # trio
- # trio-websocket
+ # via trio-websocket
executing==1.2.0
# via stack-data
-faker==19.1.0
+faker==19.2.0
# via
# dallinger
# dlgr-griduniverse
@@ -92,7 +88,7 @@ greenlet==2.0.2
# dallinger
# gevent
# sqlalchemy
-gunicorn==21.1.0
+gunicorn==21.2.0
# via dallinger
h11==0.14.0
# via wsproto
@@ -217,11 +213,6 @@ tenacity==8.2.2
# via dallinger
timeago==1.0.16
# via dallinger
-tomli==2.0.1
- # via
- # build
- # pip-tools
- # pyproject-hooks
traitlets==5.9.0
# via
# ipython
@@ -253,7 +244,7 @@ werkzeug==2.3.6
# via
# flask
# flask-login
-wheel==0.40.0
+wheel==0.41.0
# via pip-tools
wsproto==1.2.0
# via
diff --git a/setup.py b/setup.py
index 7111ec65..c0cc0b00 100644
--- a/setup.py
+++ b/setup.py
@@ -56,7 +56,10 @@
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Natural Language :: English',
- 'Programming Language :: Python :: 2.7',
+ "Programming Language :: Python :: 3.8",
+ "Programming Language :: Python :: 3.9",
+ "Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
],
entry_points={
'dallinger.experiments': [
@@ -66,6 +69,7 @@
extras_require={
'dev': [
'alabaster',
+ 'black',
'coverage',
'coverage_pth',
'codecov',
diff --git a/test/conftest.py b/test/conftest.py
index 3c212f27..b3328676 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -1,6 +1,7 @@
"""
Test fixtures for `dlgr.griduniverse` module.
"""
+import copy
import mock
import os
import pytest
@@ -10,27 +11,28 @@
skip_on_ci = pytest.mark.skipif(
- bool(os.environ.get('CI', False)),
- reason="Only runs outside of CI environment"
+ bool(os.environ.get("CI", False)), reason="Only runs outside of CI environment"
)
-@pytest.fixture(scope='module', autouse=True)
+@pytest.fixture(scope="module", autouse=True)
def reset_config():
yield
# Make sure dallinger_experiment module isn't kept between tests
import sys
- if 'dallinger_experiment' in sys.modules:
- del sys.modules['dallinger_experiment']
+
+ if "dallinger_experiment" in sys.modules:
+ del sys.modules["dallinger_experiment"]
# Make sure extra parameters aren't kept between tests
from dallinger.config import get_config
+
config = get_config()
config._reset(register_defaults=True)
-@pytest.fixture(scope='session')
+@pytest.fixture(scope="session")
def env():
# Heroku requires a home directory to start up
# We create a fake one using tempfile and set it into the
@@ -41,7 +43,7 @@ def env():
else:
fake_home = tempfile.mkdtemp()
environ_patched = environ_orig.copy()
- environ_patched.update({'HOME': fake_home})
+ environ_patched.update({"HOME": fake_home})
os.environ = environ_patched
yield environ_patched
os.environ = environ_orig
@@ -64,25 +66,26 @@ def stub_config():
dallinger.config.get_config()
"""
defaults = {
- u'aws_region': u'us-east-1',
- u'base_port': 5000,
- u'clock_on': True,
- u'dallinger_email_address': u'test@example.com',
- u'database_url': u'postgresql://postgres@localhost/dallinger',
- u'dyno_type': u'standard-2x',
- u'heroku_team': u'dallinger',
- u'host': u'localhost',
- u'logfile': u'server.log',
- u'loglevel': 0,
- u'mode': u'debug',
- u'num_dynos_web': 2,
- u'num_dynos_worker': 2,
- u'replay': False,
- u'threads': u'1',
- u'whimsical': True
+ "aws_region": "us-east-1",
+ "base_port": 5000,
+ "clock_on": True,
+ "dallinger_email_address": "test@example.com",
+ "database_url": "postgresql://postgres@localhost/dallinger",
+ "dyno_type": "standard-2x",
+ "heroku_team": "dallinger",
+ "host": "localhost",
+ "logfile": "server.log",
+ "loglevel": 0,
+ "mode": "debug",
+ "num_dynos_web": 2,
+ "num_dynos_worker": 2,
+ "replay": False,
+ "threads": "1",
+ "whimsical": True,
}
from dallinger.config import default_keys
from dallinger.config import Configuration
+
config = Configuration()
for key in default_keys:
config.register(*key)
@@ -98,6 +101,7 @@ def active_config(stub_config):
dallinger.config.get_config() and returns it.
"""
from dallinger.config import get_config
+
config = get_config()
config.data = stub_config.data
config.ready = True
@@ -105,10 +109,37 @@ def active_config(stub_config):
@pytest.fixture
-def output():
+def item_config():
+ return copy.deepcopy(
+ {
+ 1: {
+ "calories": 5,
+ "crossable": True,
+ "interactive": False,
+ "item_count": 8,
+ "item_id": 1,
+ "maturation_speed": 0.01,
+ "maturation_threshold": 0.5,
+ "n_uses": 1,
+ "name": "Food",
+ "plantable": False,
+ "planting_cost": 1,
+ "portable": True,
+ "probability_distribution": "random",
+ "public_good": 0.0,
+ "public_good_multiplier": 0.0,
+ "respawn": True,
+ "seasonal_growth_rate": 1.0,
+ "spawn_rate": 1.0,
+ "sprite": "#8a9b0f,#7a6b54",
+ }
+ }
+ )
- class Output(object):
+@pytest.fixture
+def output():
+ class Output(object):
def __init__(self):
self.log = mock.Mock()
self.error = mock.Mock()
@@ -120,6 +151,7 @@ def __init__(self):
@pytest.fixture
def db_session():
import dallinger.db
+
# The drop_all call can hang without this; see:
# https://stackoverflow.com/questions/13882407/sqlalchemy-blocked-on-dropping-tables
dallinger.db.session.close()
@@ -132,25 +164,35 @@ def db_session():
@pytest.fixture
def pubsub(exp):
import dallinger.db
- with mock.patch('dlgr.griduniverse.experiment.db.redis_conn', autospec=dallinger.db.redis_conn) as mock_redis:
+
+ with mock.patch(
+ "dlgr.griduniverse.experiment.db.redis_conn", autospec=dallinger.db.redis_conn
+ ) as mock_redis:
orig_conn = exp.redis_conn
exp.redis_conn = mock_redis
yield mock_redis
exp.redis_conn = orig_conn
+
@pytest.fixture
def fresh_gridworld():
from dlgr.griduniverse.experiment import Gridworld
- if hasattr(Gridworld, 'instance'):
- delattr(Gridworld, 'instance')
+
+ if hasattr(Gridworld, "instance"):
+ delattr(Gridworld, "instance")
+
+ yield
+
+ if hasattr(Gridworld, "instance"):
+ delattr(Gridworld, "instance")
@pytest.fixture
-def gridworld(fresh_gridworld, active_config):
+def gridworld(fresh_gridworld, active_config, item_config):
from dlgr.griduniverse.experiment import Gridworld
+
gw = Gridworld(
- log_event=mock.Mock(),
- **active_config.as_dict()
+ log_event=mock.Mock(), item_config=item_config, **active_config.as_dict()
)
yield gw
@@ -158,8 +200,9 @@ def gridworld(fresh_gridworld, active_config):
@pytest.fixture
def exp(db_session, active_config, fresh_gridworld):
from dallinger.experiments import Griduniverse
+
gu = Griduniverse(db_session)
- gu.app_id = 'test app'
+ gu.app_id = "test app"
gu.exp_config = active_config
gu.grid.players.clear()
@@ -170,29 +213,29 @@ def exp(db_session, active_config, fresh_gridworld):
@pytest.fixture
def fake_gsleep():
- with mock.patch('gevent.sleep') as fake_sleep:
+ with mock.patch("gevent.sleep") as fake_sleep:
yield fake_sleep
@pytest.fixture
def a(db_session):
- """ Provides a standard way of building model objects in tests.
+ """Provides a standard way of building model objects in tests.
- def test_using_all_defaults(self, a):
- participant = a.participant(worker_id=42)
+ def test_using_all_defaults(self, a):
+ participant = a.participant(worker_id=42)
"""
- class ModelFactory(object):
+ class ModelFactory(object):
def __init__(self, db):
self.db = db
def participant(self, **kw):
defaults = {
- 'recruiter_id': 'hotair',
- 'worker_id': '1',
- 'assignment_id': '1',
- 'hit_id': '1',
- 'mode': 'test'
+ "recruiter_id": "hotair",
+ "worker_id": "1",
+ "assignment_id": "1",
+ "hit_id": "1",
+ "mode": "test",
}
defaults.update(kw)
return self._build(models.Participant, defaults)
diff --git a/test/test_bots.py b/test/test_bots.py
index 89dc54c6..bcc446fe 100644
--- a/test/test_bots.py
+++ b/test/test_bots.py
@@ -11,32 +11,18 @@
@pytest.fixture
def overrecruited_response():
return {
- 'status': 'OK',
- 'participant': {
- 'id': 4,
- 'status': 'overrecruited'
- },
- 'quorum': {
- 'q': 1,
- 'n': 1,
- 'overrecruited': True
- }
+ "status": "OK",
+ "participant": {"id": 4, "status": "overrecruited"},
+ "quorum": {"q": 1, "n": 1, "overrecruited": True},
}
@pytest.fixture
def working_response():
return {
- 'status': 'OK',
- 'participant': {
- 'id': 4,
- 'status': 'working'
- },
- 'quorum': {
- 'q': 5,
- 'n': 1,
- 'overrecruited': False
- }
+ "status": "OK",
+ "participant": {"id": 4, "status": "working"},
+ "quorum": {"q": 5, "n": 1, "overrecruited": False},
}
@@ -54,15 +40,12 @@ def grid_state():
# WWWWWWWWWW
food_positions = [
{
- u'color': [0.534, 0.591, 0.087],
- u'id': food_id,
- u'maturity': 0.9,
- u'position': position
- } for food_id, position in enumerate([
- [1, 1],
- [4, 2],
- [4, 4]
- ])
+ "color": [0.534, 0.591, 0.087],
+ "id": food_id,
+ "maturity": 0.9,
+ "position": position,
+ }
+ for food_id, position in enumerate([[1, 1], [4, 2], [4, 4]])
]
wall_positions = [
[1, 3],
@@ -82,65 +65,65 @@ def grid_state():
wall_positions.append([0, i])
wall_positions.append([9, i])
wall_positions = [
- {u'color': [0.5, 0.5, 0.5], u'position': position} for position in wall_positions
+ {"color": [0.5, 0.5, 0.5], "position": position} for position in wall_positions
]
grid_data = {
- u'columns': 10,
- u'donation_active': False,
- u'food': food_positions,
- u'players': [
+ "columns": 10,
+ "donation_active": False,
+ "food": food_positions,
+ "players": [
{
- u'color': u'RED',
- u'id': 1,
- u'identity_visible': True,
- u'motion_auto': False,
- u'motion_direction': u'right',
- u'motion_speed_limit': 8,
- u'motion_timestamp': 0,
- u'name': u'Jeanne Brown',
- u'payoff': 0.0,
- u'position': [5, 5],
- u'score': 0.0
+ "color": "RED",
+ "id": 1,
+ "identity_visible": True,
+ "motion_auto": False,
+ "motion_direction": "right",
+ "motion_speed_limit": 8,
+ "motion_timestamp": 0,
+ "name": "Jeanne Brown",
+ "payoff": 0.0,
+ "position": [5, 5],
+ "score": 0.0,
},
{
- u'color': u'BLUE',
- u'id': 2,
- u'identity_visible': True,
- u'motion_auto': False,
- u'motion_direction': u'right',
- u'motion_speed_limit': 8,
- u'motion_timestamp': 0,
- u'name': u'Kelsey Houston',
- u'payoff': 0.0,
- u'position': [2, 2],
- u'score': 0.0
+ "color": "BLUE",
+ "id": 2,
+ "identity_visible": True,
+ "motion_auto": False,
+ "motion_direction": "right",
+ "motion_speed_limit": 8,
+ "motion_timestamp": 0,
+ "name": "Kelsey Houston",
+ "payoff": 0.0,
+ "position": [2, 2],
+ "score": 0.0,
},
{
- u'color': u'YELLOW',
- u'id': 3,
- u'identity_visible': True,
- u'motion_auto': False,
- u'motion_direction': u'right',
- u'motion_speed_limit': 8,
- u'motion_timestamp': 0,
- u'name': u'Rani Baker',
- u'payoff': 0.0,
- u'position': [5, 7],
- u'score': 0.0
- }
+ "color": "YELLOW",
+ "id": 3,
+ "identity_visible": True,
+ "motion_auto": False,
+ "motion_direction": "right",
+ "motion_speed_limit": 8,
+ "motion_timestamp": 0,
+ "name": "Rani Baker",
+ "payoff": 0.0,
+ "position": [5, 7],
+ "score": 0.0,
+ },
],
- u'round': 0,
- u'rows': 10,
- u'walls': wall_positions}
+ "round": 0,
+ "rows": 10,
+ "walls": wall_positions,
+ }
return json.dumps(grid_data)
class TestRandomMovementBot(object):
-
@pytest.fixture
def bot(self):
- b = RandomBot('http://example.com')
+ b = RandomBot("http://example.com")
b.publish = mock.Mock()
return b
@@ -154,11 +137,13 @@ def test_random_bot_selects_random_key(self, bot):
assert bot.get_next_key() in bot.VALID_KEYS
def test_random_bot_sends_random_key(self, bot):
- bot.VALID_KEYS = [Keys.DOWN, ]
+ bot.VALID_KEYS = [
+ Keys.DOWN,
+ ]
bot.publish.assert_not_called()
bot.send_next_key()
bot.publish.assert_called_once_with(
- {'type': 'move', 'player_id': '', 'move': 'down'}
+ {"type": "move", "player_id": "", "move": "down"}
)
def test_skips_experiment_if_overrecruited(self, bot, overrecruited_response):
@@ -174,16 +159,15 @@ def test_runs_experiment_if_not_overrecruited(self, bot, working_response):
class TestAdvantageSeekingBot(object):
-
@pytest.fixture
def bot(self):
- return AdvantageSeekingBot('http://example.com')
+ return AdvantageSeekingBot("http://example.com")
@pytest.fixture
def bot_in_maze(self, bot, grid_state):
bot.grid = {}
bot.participant_id = 1
- bot.handle_state({'grid': grid_state, u'remaining_time': 60})
+ bot.handle_state({"grid": grid_state, "remaining_time": 60})
bot.state = bot.observe_state()
return bot
@@ -202,22 +186,14 @@ def test_advantage_seeking_bot_understands_distances(self, bot_in_maze):
assert bot_in_maze.food_positions == [(1, 1), (4, 2), (4, 4)]
assert bot_in_maze.player_positions == {1: [5, 5], 2: [2, 2], 3: [5, 7]}
assert bot_in_maze.distances() == {
- 1: {
- 0: None,
- 1: 10,
- 2: 2
- }, 2: {
- 0: 2,
- 1: None,
- 2: None
- }, 3: {
- 0: None,
- 1: 12,
- 2: 4
- }
+ 1: {0: None, 1: 10, 2: 2},
+ 2: {0: 2, 1: None, 2: None},
+ 3: {0: None, 1: 12, 2: 4},
}
- def test_advantage_seeking_bot_goes_for_closest_food_not_already_a_target(self, bot_in_maze):
+ def test_advantage_seeking_bot_goes_for_closest_food_not_already_a_target(
+ self, bot_in_maze
+ ):
bot_in_maze.player_id = 1
assert bot_in_maze.get_next_key() == Keys.UP
assert bot_in_maze.target_coordinates == (4, 4)
@@ -235,30 +211,31 @@ def test_bot_does_not_get_stuck_if_end_of_game_message_is_missed(self, bot_in_ma
bot_in_maze.on_grid = True
bot_in_maze._quorum_reached = True
bot_in_maze.END_BUFFER_SECONDS = 1
- HPBGUB = 'dlgr.griduniverse.bots.HighPerformanceBaseGridUniverseBot'
+ HPBGUB = "dlgr.griduniverse.bots.HighPerformanceBaseGridUniverseBot"
- with mock.patch(HPBGUB + '.wait_for_grid') as wait_for_grid:
+ with mock.patch(HPBGUB + ".wait_for_grid") as wait_for_grid:
wait_for_grid.return_value = True
- bot_in_maze.grid['remaining_time'] = 2
+ bot_in_maze.grid["remaining_time"] = 2
before_participate = time.time()
- with mock.patch(HPBGUB + '.send_next_key') as send_next_key:
+ with mock.patch(HPBGUB + ".send_next_key") as send_next_key:
send_next_key.return_value = None
bot_in_maze.participate()
after_participate = time.time()
- assert after_participate - before_participate < 6 # 2s left, 1s grace, ~1s overhead
+ assert (
+ after_participate - before_participate < 6
+ ) # 2s left, 1s grace, ~1s overhead
class TestFoodSeekingBot(object):
-
@pytest.fixture
def bot(self):
- return FoodSeekingBot('http://example.com')
+ return FoodSeekingBot("http://example.com")
@pytest.fixture
def bot_in_maze(self, bot, grid_state):
bot.grid = {}
bot.participant_id = 1
- bot.handle_state({'grid': grid_state, u'remaining_time': 60})
+ bot.handle_state({"grid": grid_state, "remaining_time": 60})
bot.state = bot.observe_state()
return bot
diff --git a/test/test_griduniverse.py b/test/test_griduniverse.py
index 0a9e4051..a096a76d 100755
--- a/test/test_griduniverse.py
+++ b/test/test_griduniverse.py
@@ -6,32 +6,115 @@
import mock
import pytest
import time
+import uuid
+from dlgr.griduniverse.experiment import Player
-class TestDependenciesLoaded(object):
+class TestDependenciesLoaded(object):
def test_tablib_importable(self):
import tablib
+
assert tablib is not None
-@pytest.mark.usefixtures('env')
-class TestExperimentClass(object):
+class TestItem(object):
+ @pytest.fixture
+ def item_config(self):
+ return {
+ "item_id": 9,
+ "calories": 5,
+ "crossable": True,
+ "interactive": False,
+ "maturation_speed": 1,
+ "maturation_threshold": 0.0,
+ "n_uses": 1,
+ "name": "Food",
+ "plantable": False,
+ "planting_cost": 1,
+ "portable": True,
+ "spawn_rate": 0.1,
+ "sprite": "#8a9b0f,#7a6b54",
+ }
+
+ @property
+ def subject(self):
+ from dlgr.griduniverse.experiment import Item
+
+ return Item
+
+ def test_initialized_with_some_default_values(self, item_config):
+ item = self.subject(item_config)
+
+ assert isinstance(item.creation_timestamp, float)
+ assert isinstance(item.id, uuid.UUID)
+ assert item.position == (0, 0)
+
+ def test_instance_specific_values_can_be_specified(self, item_config):
+ item = self.subject(
+ item_config, id=42, position=(2, 4), creation_timestamp=21.2
+ )
+
+ assert item.id == 42
+ assert item.position == (2, 4)
+ assert item.creation_timestamp == 21.2
+
+ def test_repr(self, item_config):
+ item = self.subject(
+ item_config, id=42, position=(2, 4), creation_timestamp=21.2
+ )
+
+ assert (
+ item.__repr__()
+ == "Item(name='Food', item_id=9, id=42, position=(2, 4), creation_timestamp=21.2)"
+ )
+ def test_inherits_shared_type_properties_from_config(self, item_config):
+ item = self.subject(item_config)
+ assert item.item_id == item_config["item_id"]
+ assert item.name == "Food"
+ assert item.calories == 5
+
+ def test_type_properties_stored_by_reference(self, item_config):
+ item = self.subject(item_config)
+
+ assert item.calories == 5
+ # Update shared type definition:
+ item_config["calories"] = 6
+ # Change seen in instance:
+ assert item.calories == 6
+
+ def test_type_properties_cannot_by_shadowed(self, item_config):
+ from dataclasses import FrozenInstanceError
+
+ item = self.subject(item_config)
+
+ assert item.calories == 5
+
+ with pytest.raises(FrozenInstanceError):
+ item.calories = 6
+
+
+@pytest.mark.usefixtures("env")
+class TestExperimentClass(object):
def test_initialization(self, exp):
from dallinger.experiment import Experiment
+
assert isinstance(exp, Experiment)
def test_recruiter_property_is_some_subclass_of_recruiter(self, exp):
from dallinger.recruiters import Recruiter
+
assert isinstance(exp.recruiter, Recruiter)
def test_new_experiment_has_a_grid(self, exp):
from dlgr.griduniverse.experiment import Gridworld
+
assert isinstance(exp.grid, Gridworld)
def test_create_network_builds_default_network_type(self, exp):
from dallinger.networks import FullyConnected
+
net = exp.create_network()
assert isinstance(net, FullyConnected)
@@ -52,7 +135,7 @@ def test_bonus(self, participants, exp):
# With no experiment state, bonus returns 0
assert exp.bonus(participants[0]) == 0.0
- with mock.patch('dlgr.griduniverse.experiment.Griduniverse.environment') as env:
+ with mock.patch("dlgr.griduniverse.experiment.Griduniverse.environment") as env:
state_mock = mock.Mock()
env.state.return_value = state_mock
# State contents is JSON grid state
@@ -60,145 +143,128 @@ def test_bonus(self, participants, exp):
assert exp.bonus(participants[0]) == 100.0
-@pytest.mark.usefixtures('env', 'fake_gsleep')
+@pytest.mark.usefixtures("env", "fake_gsleep")
class TestGameLoops(object):
-
@pytest.fixture
def loop_exp_3x(self, exp):
- exp.grid = mock.Mock()
- exp.grid.num_food = 10
- exp.grid.round = 0
- exp.grid.seasonal_growth_rate = 1
- exp.grid.food_growth_rate = 1.0
- exp.grid.rows = exp.grid.columns = 25
- exp.grid.players = {'1': mock.Mock()}
- exp.grid.players['1'].score = 10
- exp.grid.contagion = 1
- exp.grid.tax = 1.0
- exp.grid.food_locations = []
- exp.grid.frequency_dependence = 1
- exp.grid.frequency_dependent_payoff_rate = 0
exp.grid.start_timestamp = time.time()
exp.socket_session = mock.Mock()
exp.publish = mock.Mock()
- exp.grid.serialize.return_value = {}
def count_down(counter):
for c in counter:
return False
return True
+
end_counter = (i for i in range(3))
start_counter = (i for i in range(3))
- # Each of these will loop three times before while condition is met
- type(exp.grid).game_started = mock.PropertyMock(
- side_effect=lambda: count_down(start_counter)
- )
- type(exp.grid).game_over = mock.PropertyMock(
- side_effect=lambda: count_down(end_counter)
- )
- yield exp
+
+ with mock.patch(
+ "dlgr.griduniverse.experiment.Gridworld.game_started",
+ new_callable=mock.PropertyMock,
+ ) as started:
+ with mock.patch(
+ "dlgr.griduniverse.experiment.Gridworld.game_over",
+ new_callable=mock.PropertyMock,
+ ) as over:
+ started.side_effect = lambda: count_down(start_counter)
+ over.side_effect = lambda: count_down(end_counter)
+
+ yield exp
def test_loop_builds_labrynth(self, loop_exp_3x):
exp = loop_exp_3x
- exp.game_loop()
- # labryinth built once
- assert exp.grid.build_labyrinth.call_count == 1
+ exp.grid.walls_density = 0.01
- def test_loop_spawns_food(self, loop_exp_3x):
- exp = loop_exp_3x
exp.game_loop()
- # Spawn food called once for each num_food
- assert exp.grid.spawn_food.call_count == exp.grid.num_food
-
- def test_loop_spawns_food_during_timed_events(self, loop_exp_3x):
- # Spawn food called twice for each num_food, once at start and again
- # on timed events to replenish empty list
- exp = loop_exp_3x
- # Ensure one timed events round
- exp.grid.start_timestamp -= 2
+ state = exp.grid.serialize()
+ assert len(state["walls"]) > 1
+ def test_loop_spawns_items(self, loop_exp_3x):
+ exp = loop_exp_3x
exp.game_loop()
- assert exp.grid.spawn_food.call_count == exp.grid.num_food * 2
+
+ state = exp.grid.serialize()
+ assert len(state["items"]) == sum(
+ [i["item_count"] for i in exp.item_config.values()]
+ )
def test_loop_serialized_and_saves(self, loop_exp_3x):
# Grid serialized and added to DB session once per loop
exp = loop_exp_3x
exp.game_loop()
- assert exp.grid.serialize.call_count == 3
+
assert exp.socket_session.add.call_count == 3
# Session commited once per loop and again at end
assert exp.socket_session.commit.call_count == 4
def test_loop_resets_state(self, loop_exp_3x):
- # Wall and food state unset, food count reset during loop
+ # Wall and item state unset, item count reset during loop
exp = loop_exp_3x
exp.game_loop()
assert exp.grid.walls_updated is False
- assert exp.grid.food_updated is False
+ assert exp.grid.items_updated is False
def test_loop_taxes_points(self, loop_exp_3x):
# Player is taxed one point during the timed event round
exp = loop_exp_3x
+ exp.grid.tax = 1.0
+ exp.grid.players = {"1": Player(id="1", score=10.0)}
# Ensure one timed events round
exp.grid.start_timestamp -= 2
exp.game_loop()
- assert exp.grid.players['1'].score == 9.0
+ assert exp.grid.players["1"].score == 9.0
def test_loop_computes_payoffs(self, loop_exp_3x):
# Payoffs computed once per loop before checking round completion
exp = loop_exp_3x
+ exp.grid.dollars_per_point = 0.5
+ exp.grid.players = {"1": Player(id="1", score=10.0)}
+
exp.game_loop()
- assert exp.grid.compute_payoffs.call_count == 3
- assert exp.grid.check_round_completion.call_count == 3
+
+ assert exp.grid.players["1"].payoff == 5.0
def test_loop_publishes_stop_event(self, loop_exp_3x):
# publish called with stop event at end of round
exp = loop_exp_3x
exp.game_loop()
- exp.publish.assert_called_once_with({'type': 'stop'})
+ exp.publish.assert_called_once_with({"type": "stop"})
def test_send_state_thread(self, loop_exp_3x):
- # State thread will loop 4 times before the loop is broken
exp = loop_exp_3x
- exp.grid.serialize.return_value = {
- 'grid': 'serialized',
- 'walls': [],
- 'food': [],
- 'players': [{'id': '1'}],
- }
-
exp.send_state_thread()
- # Grid serialized once per loop
- assert exp.grid.serialize.call_count == 4
- # publish called with grid state message once per loop
+
+ # State thread will loop 4 times before the loop is broken,
+ # and publish called with grid state message once per loop
assert exp.publish.call_count == 4
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestPlayerConnects(object):
-
def test_handle_connect_creates_node(self, exp, a):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
assert participant.id in exp.node_by_player_id
def test_handle_connect_adds_player_to_grid(self, exp, a):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
assert participant.id in exp.grid.players
def test_handle_connect_is_noop_for_spectators(self, exp):
- exp.handle_connect({'player_id': 'spectator'})
+ exp.handle_connect({"player_id": "spectator"})
assert exp.node_by_player_id == {}
def test_colors_distributed_evenly(self, exp, participants):
exp.grid.num_players = 9
exp.networks()[0].max_size = 10
players = [
- exp.handle_connect({'player_id': participant.id}) or exp.grid.players[participant.id]
+ exp.handle_connect({"player_id": participant.id})
+ or exp.grid.players[participant.id]
for participant in participants[:9]
]
colors = collections.Counter([player.color_idx for player in players])
@@ -209,55 +275,54 @@ def test_colors_distributed_almost_evenly_if_on_edge(self, exp, participants):
exp.grid.num_players = 9
exp.networks()[0].max_size = 10
players = [
- exp.handle_connect({'player_id': participant.id}) or exp.grid.players[participant.id]
+ exp.handle_connect({"player_id": participant.id})
+ or exp.grid.players[participant.id]
for participant in participants[:9]
]
colors = collections.Counter([player.color_idx for player in players])
assert colors == {0: 5, 1: 4}
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestRecordPlayerActivity(object):
-
def test_records_player_events(self, exp, a):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
exp.send(
- 'griduniverse_ctrl:'
+ "griduniverse_ctrl:"
'{{"type":"move","player_id":{},"move":"left"}}'.format(participant.id)
)
time.sleep(10)
data = exp.retrieve_data()
# Get the last recorded event
- event_detail = json.loads(data.infos.df['details'].values[-1])
- assert event_detail['player_id'] == participant.id
- assert event_detail['move'] == 'left'
+ event_detail = json.loads(data.infos.df["details"].values[-1])
+ assert event_detail["player_id"] == participant.id
+ assert event_detail["move"] == "left"
def test_scores_and_payoffs_averaged(self, exp, a):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
exp.send(
- 'griduniverse_ctrl:'
+ "griduniverse_ctrl:"
'{{"type":"move","player_id":{},"move":"left"}}'.format(participant.id)
)
time.sleep(10)
data = exp.retrieve_data()
results = json.loads(exp.analyze(data))
- assert results[u'average_score'] >= 0.0
- assert results[u'average_payoff'] >= 0.0
+ assert results["average_score"] >= 0.0
+ assert results["average_payoff"] >= 0.0
def test_record_event_with_participant(self, exp, a):
# Adds event to player node
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
exp.socket_session.add = mock.Mock()
exp.socket_session.commit = mock.Mock()
- exp.record_event({'data': ['some data']},
- player_id=participant.id)
+ exp.record_event({"data": ["some data"]}, player_id=participant.id)
exp.socket_session.add.assert_called_once()
exp.socket_session.commit.assert_called_once()
info = exp.socket_session.add.call_args[0][0]
- assert info.details['data'] == ['some data']
+ assert info.details["data"] == ["some data"]
assert info.origin.id == exp.node_by_player_id[participant.id]
def test_record_event_without_participant(self, exp):
@@ -265,11 +330,11 @@ def test_record_event_without_participant(self, exp):
node = exp.environment
exp.socket_session.add = mock.Mock()
exp.socket_session.commit = mock.Mock()
- exp.record_event({'data': ['some data']})
+ exp.record_event({"data": ["some data"]})
exp.socket_session.add.assert_called_once()
exp.socket_session.commit.assert_called_once()
info = exp.socket_session.add.call_args[0][0]
- assert info.details['data'] == ['some data']
+ assert info.details["data"] == ["some data"]
assert info.origin.id == node.id
def test_record_event_with_failed_node(self, exp, a):
@@ -278,51 +343,52 @@ def test_record_event_with_failed_node(self, exp, a):
node.failed = True
exp.socket_session.add = mock.Mock()
exp.socket_session.commit = mock.Mock()
- with mock.patch('dlgr.griduniverse.experiment.logger.info') as logger:
- exp.record_event({'data': ['some data']})
+ with mock.patch("dlgr.griduniverse.experiment.logger.info") as logger:
+ exp.record_event({"data": ["some data"]})
assert exp.socket_session.add.call_count == 0
assert exp.socket_session.commit.call_count == 0
logger.assert_called_once()
assert logger.call_args.startswith(
- 'Tried to record an event after node#{} failure:'.format(
- node.id
- )
+ "Tried to record an event after node#{} failure:".format(node.id)
)
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestChat(object):
-
def test_appends_to_chat_history(self, exp, a):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
exp.send(
- 'griduniverse_ctrl:'
- '{{"type":"chat","player_id":{},"contents":"hello!"}}'.format(participant.id)
+ "griduniverse_ctrl:"
+ '{{"type":"chat","player_id":{},"contents":"hello!"}}'.format(
+ participant.id
+ )
)
history = exp.grid.chat_message_history
assert len(history) == 1
- assert 'hello!' in history[0]
+ assert "hello!" in history[0]
def test_republishes_non_broadcasts(self, exp, a, pubsub):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
exp.send(
- 'griduniverse_ctrl:'
- '{{"type":"chat","player_id":{},"contents":"hello!"}}'.format(participant.id)
+ "griduniverse_ctrl:"
+ '{{"type":"chat","player_id":{},"contents":"hello!"}}'.format(
+ participant.id
+ )
)
pubsub.publish.assert_called()
def test_does_not_republish_broadcasts(self, exp, a, pubsub):
participant = a.participant()
- exp.handle_connect({'player_id': participant.id})
+ exp.handle_connect({"player_id": participant.id})
exp.send(
- 'griduniverse_ctrl:'
+ "griduniverse_ctrl:"
'{{"type":"chat","player_id":{},"contents":"hello!","broadcast":"true"}}'.format(
participant.id
)
@@ -331,22 +397,18 @@ def test_does_not_republish_broadcasts(self, exp, a, pubsub):
pubsub.publish.assert_not_called()
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestDonation(object):
-
def test_group_donations_distributed_evenly_across_team(self, exp, a):
donor = a.participant()
teammate = a.participant()
- exp.handle_connect({'player_id': donor.id})
- exp.handle_connect({'player_id': teammate.id})
+ exp.handle_connect({"player_id": donor.id})
+ exp.handle_connect({"player_id": teammate.id})
donor_player = exp.grid.players[1]
teammate_player = exp.grid.players[2]
# put them on the same team:
exp.handle_change_color(
- {
- 'player_id': teammate_player.id,
- 'color': donor_player.color
- }
+ {"player_id": teammate_player.id, "color": donor_player.color}
)
# make donation active
exp.grid.donation_group = True
@@ -355,9 +417,9 @@ def test_group_donations_distributed_evenly_across_team(self, exp, a):
exp.handle_donation(
{
- 'donor_id': donor_player.id,
- 'recipient_id': 'group:{}'.format(donor_player.color_idx),
- 'amount': 2
+ "donor_id": donor_player.id,
+ "recipient_id": "group:{}".format(donor_player.color_idx),
+ "amount": 2,
}
)
assert donor_player.score == 1
@@ -366,8 +428,8 @@ def test_group_donations_distributed_evenly_across_team(self, exp, a):
def test_public_donations_distributed_evenly_across_players(self, exp, a):
donor = a.participant()
opponent = a.participant()
- exp.handle_connect({'player_id': donor.id})
- exp.handle_connect({'player_id': opponent.id})
+ exp.handle_connect({"player_id": donor.id})
+ exp.handle_connect({"player_id": opponent.id})
donor_player = exp.grid.players[1]
opponent_player = exp.grid.players[2]
exp.grid.donation_public = True
@@ -375,11 +437,7 @@ def test_public_donations_distributed_evenly_across_players(self, exp, a):
donor_player.score = 2
exp.handle_donation(
- {
- 'donor_id': donor_player.id,
- 'recipient_id': 'all',
- 'amount': 2
- }
+ {"donor_id": donor_player.id, "recipient_id": "all", "amount": 2}
)
assert donor_player.score == 1
diff --git a/test/test_gridworld.py b/test/test_gridworld.py
index 7a74a1ec..aca6823b 100644
--- a/test/test_gridworld.py
+++ b/test/test_gridworld.py
@@ -2,68 +2,65 @@
import pytest
-@pytest.mark.usefixtures('env')
-class TestFoodSupply(object):
-
- def test_spawn_food_at_position(self, gridworld):
+@pytest.mark.usefixtures("env")
+class TestItemSpawning(object):
+ def test_spawn_item_at_position(self, gridworld):
# reset food state
- gridworld.food_updated = False
- assert len(gridworld.food_locations.keys()) == 0
- assert gridworld.food_locations.get((0, 0)) is None
+ gridworld.items_updated = False
+ assert len(gridworld.item_locations.keys()) == 0
+ assert gridworld.item_locations.get((0, 0)) is None
# Spawn food at specific location
- gridworld.spawn_food(position=(0, 0))
- assert gridworld.food_updated is True
- assert gridworld.food_locations.get((0, 0)) is not None
+ gridworld.spawn_item(position=(0, 0))
+ assert gridworld.items_updated is True
+ assert gridworld.item_locations.get((0, 0)) is not None
- def test_spawn_food_at_random(self, gridworld):
+ def test_spawn_item_at_random(self, gridworld):
# reset food state
- gridworld.food_updated = False
- assert len(gridworld.food_locations.keys()) == 0
+ gridworld.items_updated = False
+ assert len(gridworld.item_locations.keys()) == 0
# Spawn food at random location with no arguments
- gridworld.spawn_food()
- assert gridworld.food_updated is True
- assert len(gridworld.food_locations.keys()) == 1
+ gridworld.spawn_item()
+ assert gridworld.items_updated is True
+ assert len(gridworld.item_locations.keys()) == 1
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestSerialize(object):
-
def test_serializes_players(self, gridworld):
player = mock.Mock()
- player.serialize.return_value = 'Serialized Player'
+ player.serialize.return_value = "Serialized Player"
gridworld.players = {1: player}
values = gridworld.serialize()
- assert values['players'] == ['Serialized Player']
- assert values['round'] == 0
- assert values['donation_active'] == gridworld.donation_active
- assert values['rows'] == gridworld.rows
- assert values['columns'] == gridworld.columns
+ assert values["players"] == ["Serialized Player"]
+ assert values["round"] == 0
+ assert values["donation_active"] == gridworld.donation_active
+ assert values["rows"] == gridworld.rows
+ assert values["columns"] == gridworld.columns
- def test_serializes_food_and_walls(self, gridworld):
+ def test_serializes_items_and_walls(self, gridworld):
values = gridworld.serialize()
- assert values.get('walls') == []
- assert values.get('food') == []
+ assert values.get("walls") == []
+ assert values.get("items") == []
wall = mock.Mock()
- food = mock.Mock()
- wall.serialize.return_value = 'Serialized Wall'
- food.serialize.return_value = 'Serialized Food'
+ items = mock.Mock()
+ wall.serialize.return_value = "Serialized Wall"
+ items.serialize.return_value = "Serialized Item"
gridworld.wall_locations[(1, 1)] = wall
- gridworld.food_locations[(2, 2)] = food
+ gridworld.item_locations[(2, 2)] = items
values = gridworld.serialize()
- assert values.get('walls') == ['Serialized Wall']
- assert values.get('food') == ['Serialized Food']
+ assert values.get("walls") == ["Serialized Wall"]
+ assert values.get("items") == ["Serialized Item"]
def test_food_and_wall_serialization_disabled(self, gridworld):
- gridworld.wall_locations[(1, 1)] = 'ignored'
- gridworld.food_locations[(2, 2)] = 'ignored'
- values = gridworld.serialize(include_walls=False, include_food=False)
- assert values.get('walls') is None
- assert values.get('food') is None
+ gridworld.wall_locations[(1, 1)] = "ignored"
+ gridworld.item_locations[(2, 2)] = "ignored"
+ values = gridworld.serialize(include_walls=False, include_items=False)
+ assert values.get("walls") is None
+ assert values.get("food") is None
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestRoundState(object):
-
def test_check_round_not_started(self, gridworld):
# Game hasn't started
gridworld._start_if_ready()
@@ -87,7 +84,7 @@ def test_check_round_change(self, gridworld):
gridworld.num_rounds = 2
gridworld._start_if_ready()
# When elapsed_round_time surpasses the time_per_round, the round is over
- with mock.patch('time.time') as mod_time:
+ with mock.patch("time.time") as mod_time:
start_time = gridworld.start_timestamp
mod_time.return_value = start_time + gridworld.time_per_round
assert gridworld.remaining_round_time == 0
@@ -104,7 +101,7 @@ def test_game_end(self, gridworld):
gridworld.players = {1: mock.Mock()}
gridworld.num_rounds = 1
gridworld._start_if_ready()
- with mock.patch('time.time') as mod_time:
+ with mock.patch("time.time") as mod_time:
# Finish final round
start_time = gridworld.start_timestamp
mod_time.return_value = start_time + gridworld.time_per_round
@@ -113,70 +110,12 @@ def test_game_end(self, gridworld):
assert gridworld.game_over is True
-@pytest.mark.usefixtures('env')
+@pytest.mark.usefixtures("env")
class TestInstructions(object):
-
def test_instructions(self, gridworld):
- # There are a number of parameters that influence the game instructions
- # new parameters that influence game play should be included here
- boolean_params = (
- 'build_walls', 'others_visible', 'mutable_colors',
- 'player_overlap', 'motion_auto',
- 'respawn_food', 'food_planting',
- 'show_chatroom', 'pseudonyms'
- )
- numeric_params = (
- 'window_columns', 'window_rows', 'walls_density',
- 'num_rounds', 'num_players', 'num_colors', 'contagion',
- 'visibility', 'motion_cost', 'motion_tremble_rate',
- 'food_maturation_threshold', 'donation_amount',
- 'dollars_per_point'
- )
- dependent_numeric_params = (
- 'wall_building_cost', 'food_planting_cost'
+ # Just test something basic
+ html = gridworld.instructions()
+ assert (
+ f"game duration is {gridworld.time_per_round} seconds"
+ in html
)
- dependent_boolean_params = (
- 'frequency_dependence', 'alternate_consumption_donation'
- )
-
- instructions = gridworld.instructions()
- for param in boolean_params:
- # invert the parameter value
- setattr(gridworld, param, not getattr(gridworld, param, False))
- assert gridworld.instructions() != instructions, (
- '{} = {} did not change instructions'.format(
- param, getattr(gridworld, param))
- )
- instructions = gridworld.instructions()
-
- for param in numeric_params:
- old_value = getattr(gridworld, param, 0)
- # Add one or subtract 20 depending on original value
- new_value = old_value + 1 - max((old_value - 21), 0)
- setattr(gridworld, param, new_value)
- assert gridworld.instructions() != instructions, (
- '{} = {} did not change instructions'.format(
- param, getattr(gridworld, param))
- )
- instructions = gridworld.instructions()
-
- # These are parameters that only have an effect if a numeric value above
- # has been set
- for param in dependent_boolean_params:
- # invert the parameter value
- setattr(gridworld, param, not getattr(gridworld, param, False))
- assert gridworld.instructions() != instructions, (
- '{} = {} did not change instructions'.format(
- param, getattr(gridworld, param))
- )
- instructions = gridworld.instructions()
-
- # These are generally costs for things enabled above
- for param in dependent_numeric_params:
- # increment the cost
- setattr(gridworld, param, getattr(gridworld, param, 0) + 1)
- assert gridworld.instructions() != instructions, (
- '{} = {} did not change instructions'.format(
- param, getattr(gridworld, param))
- )
- instructions = gridworld.instructions()
diff --git a/test/test_labyrinth.py b/test/test_labyrinth.py
index 1c51db11..43f1cd76 100644
--- a/test/test_labyrinth.py
+++ b/test/test_labyrinth.py
@@ -3,14 +3,15 @@
class TestLabyrinth(object):
-
@pytest.fixture
def labyrinth(self, **kw):
from dlgr.griduniverse.maze import labyrinth
+
return labyrinth
def test_creates_walls(self, labyrinth):
from dlgr.griduniverse.maze import Wall
+
walls = labyrinth()
assert isinstance(walls[0], Wall)
@@ -38,15 +39,15 @@ def test_reducing_density_and_contiguity_reduces_wall_count_by_sum(self, labyrin
class TestMazePrune(object):
-
@pytest.fixture
def prune(self, **kw):
from dlgr.griduniverse import maze
+
return maze._prune
@pytest.fixture
def Pos(self):
- Positioned = namedtuple('Positioned', ['position'])
+ Positioned = namedtuple("Positioned", ["position"])
return Positioned
def test_prune_removes_isolated_walls_only_when_contiguity_is_1(self, Pos, prune):
@@ -77,48 +78,90 @@ def test_prune_removes_isolated_walls_only_when_contiguity_is_1(self, Pos, prune
"""
positions = [
- [0, 7], [0, 8],
- [1, 7], [1, 8],
- [2, 0], [2, 3], [2, 6], [2, 7],
- [3, 1], [3, 5], [3, 6], [3, 9],
- [4, 0], [4, 2], [4, 4], [4, 5],
- [5, 3], [5, 4], [5, 7],
- [6, 1], [6, 2], [6, 3],
- [7, 1], [7, 2], [7, 3],
- [8, 0]
+ [0, 7],
+ [0, 8],
+ [1, 7],
+ [1, 8],
+ [2, 0],
+ [2, 3],
+ [2, 6],
+ [2, 7],
+ [3, 1],
+ [3, 5],
+ [3, 6],
+ [3, 9],
+ [4, 0],
+ [4, 2],
+ [4, 4],
+ [4, 5],
+ [5, 3],
+ [5, 4],
+ [5, 7],
+ [6, 1],
+ [6, 2],
+ [6, 3],
+ [7, 1],
+ [7, 2],
+ [7, 3],
+ [8, 0],
]
walls = [Pos(pos) for pos in positions]
- pruned = [
- w.position for w in prune(walls, density=0.5, contiguity=1.0)
- ]
+ pruned = [w.position for w in prune(walls, density=0.5, contiguity=1.0)]
singles_removed = [
- [0, 7], [0, 8],
- [1, 7], [1, 8],
- [2, 6], [2, 7],
- [3, 5], [3, 6],
- [4, 4], [4, 5],
- [5, 3], [5, 4],
- [6, 1], [6, 2], [6, 3],
- [7, 1], [7, 2], [7, 3]
+ [0, 7],
+ [0, 8],
+ [1, 7],
+ [1, 8],
+ [2, 6],
+ [2, 7],
+ [3, 5],
+ [3, 6],
+ [4, 4],
+ [4, 5],
+ [5, 3],
+ [5, 4],
+ [6, 1],
+ [6, 2],
+ [6, 3],
+ [7, 1],
+ [7, 2],
+ [7, 3],
]
assert pruned == singles_removed
- def test_prune_removes_some_contiguous_walls_with_contiguity_less_than_1(self, Pos, prune):
+ def test_prune_removes_some_contiguous_walls_with_contiguity_less_than_1(
+ self, Pos, prune
+ ):
positions = [
- [0, 7], [0, 8],
- [1, 7], [1, 8],
- [2, 0], [2, 3], [2, 6], [2, 7],
- [3, 1], [3, 5], [3, 6], [3, 9],
- [4, 0], [4, 2], [4, 4], [4, 5],
- [5, 3], [5, 4], [5, 7],
- [6, 1], [6, 2], [6, 3],
- [7, 1], [7, 2], [7, 3],
- [8, 0]
+ [0, 7],
+ [0, 8],
+ [1, 7],
+ [1, 8],
+ [2, 0],
+ [2, 3],
+ [2, 6],
+ [2, 7],
+ [3, 1],
+ [3, 5],
+ [3, 6],
+ [3, 9],
+ [4, 0],
+ [4, 2],
+ [4, 4],
+ [4, 5],
+ [5, 3],
+ [5, 4],
+ [5, 7],
+ [6, 1],
+ [6, 2],
+ [6, 3],
+ [7, 1],
+ [7, 2],
+ [7, 3],
+ [8, 0],
]
num_with_neighbors = 18 # 18 of 26 walls have neighbors in this configuration
walls = [Pos(pos) for pos in positions]
- pruned = [
- w.position for w in prune(walls, density=0.5, contiguity=0.5)
- ]
+ pruned = [w.position for w in prune(walls, density=0.5, contiguity=0.5)]
assert len(pruned) < num_with_neighbors
diff --git a/test/test_player.py b/test/test_player.py
index d2686707..6ac5b0c4 100644
--- a/test/test_player.py
+++ b/test/test_player.py
@@ -3,9 +3,9 @@
class TestInstantiation(object):
-
def test_has_sensible_defaults(self):
from dlgr.griduniverse.experiment import Gridworld
+
player = Player()
assert player.position == [0, 0]
assert not player.motion_auto
@@ -15,8 +15,8 @@ def test_has_sensible_defaults(self):
def test_has_a_persona(self):
player = Player()
- assert hasattr(player, 'name')
- assert player.gender in ('F', 'M')
+ assert hasattr(player, "name")
+ assert player.gender in ("F", "M")
def test_can_assign_color_by_name(self):
player = Player(color_name="BLUE")
@@ -24,7 +24,6 @@ def test_can_assign_color_by_name(self):
class TestIsAwareOfOtherPlayersOnGrid(object):
-
def test_knows_if_its_adjacent_to_another_player(self):
player = Player()
assert player.is_neighbor(Player(position=[0, 1]))
@@ -39,8 +38,8 @@ def test_has_no_neighbors_by_default(self):
assert player.neighbors() == []
def test_can_find_neighbors_via_the_grid(self, gridworld):
- player1 = gridworld.spawn_player('1')
- player2 = gridworld.spawn_player('2')
+ player1 = gridworld.spawn_player("1")
+ player2 = gridworld.spawn_player("2")
player1.position = [0, 0]
player2.position = [0, 1]
@@ -57,76 +56,81 @@ class TestMovement(object):
def test_needs_a_grid_for_context(self):
player = Player()
with pytest.raises(AttributeError):
- player.move('right')
+ player.move("right")
- def test_can_move_into_empty_location_immediately_if_no_speed_limit(self, gridworld):
- player = gridworld.spawn_player('1')
+ def test_can_move_into_empty_location_immediately_if_no_speed_limit(
+ self, gridworld
+ ):
+ player = gridworld.spawn_player("1")
player.position = [0, 0]
player.motion_speed_limit = 0
- player.move('right')
+ player.move("right")
assert player.position == [0, 1]
def test_successful_move_returns_direction_in_message(self, gridworld):
- player = gridworld.spawn_player('1')
+ player = gridworld.spawn_player("1")
player.position = [0, 0]
player.motion_speed_limit = 0
- message = player.move('right')
+ message = player.move("right")
- assert message['direction'] == 'right'
+ assert message["direction"] == "right"
def test_can_add_wall_on_move(self, gridworld):
- player = gridworld.spawn_player('1')
+ player = gridworld.spawn_player("1")
player.position = [0, 0]
player.add_wall = [0, 0]
player.motion_speed_limit = 0
- message = player.move('right')
+ message = player.move("right")
- assert message['direction'] == 'right'
- assert message['wall'] == {'type': 'wall_built', 'wall': [0, 0]}
+ assert message["direction"] == "right"
+ assert message["wall"] == {"type": "wall_built", "wall": [0, 0]}
assert gridworld.has_wall([0, 0])
def test_cannot_move_immediately_if_speed_limit_enforced(self, gridworld):
from dlgr.griduniverse.experiment import IllegalMove
- player = gridworld.spawn_player('1')
+
+ player = gridworld.spawn_player("1")
player.position = [0, 0]
with pytest.raises(IllegalMove) as ex_info:
- player.move('right')
- assert ex_info.match('wait time has not passed')
+ player.move("right")
+ assert ex_info.match("wait time has not passed")
assert player.position == [0, 0]
def test_cannot_move_if_motion_has_cost_exceeding_score(self, gridworld):
from dlgr.griduniverse.experiment import IllegalMove
- player = gridworld.spawn_player('1')
+
+ player = gridworld.spawn_player("1")
player.motion_speed_limit = 0
player.motion_cost = 1
player.position = [0, 0]
with pytest.raises(IllegalMove) as ex_info:
- player.move('right')
- assert ex_info.match('Not enough points')
+ player.move("right")
+ assert ex_info.match("Not enough points")
assert player.position == [0, 0]
def test_cannot_move_if_space_is_occupied(self, gridworld):
from dlgr.griduniverse.experiment import IllegalMove
- player = gridworld.spawn_player('1')
- occupier = gridworld.spawn_player('2')
+
+ player = gridworld.spawn_player("1")
+ occupier = gridworld.spawn_player("2")
occupier.position = [0, 1]
player.motion_speed_limit = 0
player.position = [0, 0]
with pytest.raises(IllegalMove) as ex_info:
- player.move('right')
- assert ex_info.match('not open')
+ player.move("right")
+ assert ex_info.match("not open")
assert player.position == [0, 0]
def test_tremble_sends_player_in_another_direction(self):
player = Player()
- assert player.tremble('up') in ('down', 'left', 'right')
+ assert player.tremble("up") in ("down", "left", "right")
diff --git a/test/test_replay_state.py b/test/test_replay_state.py
index 433e912f..3520b0a7 100644
--- a/test/test_replay_state.py
+++ b/test/test_replay_state.py
@@ -5,22 +5,22 @@
class TestReplayState(object):
-
@pytest.fixture
def experiment(self, db_session):
from dlgr.griduniverse.experiment import Griduniverse
+
yield Griduniverse()
@pytest.fixture
def scrubber(self, experiment, db_session):
with experiment.restore_state_from_replay(
- 'griduniverse-test',
+ "griduniverse-test",
session=db_session,
- zip_path='test/griduniverse_bots.zip',
+ zip_path="test/griduniverse_bots.zip",
) as scrubber:
yield scrubber
def test_forward_scrub_updates_state(self, scrubber, experiment):
target = datetime(2018, 4, 5, 10, 32, 0, 0)
scrubber(target)
- assert len(experiment.grid.food_locations) > 0
+ assert len(experiment.grid.item_locations) > 0
diff --git a/tox.ini b/tox.ini
index b638534a..4c355d49 100644
--- a/tox.ini
+++ b/tox.ini
@@ -24,6 +24,7 @@ deps =
-r {toxinidir}/dev-requirements.txt
commands =
flake8 dlgr/griduniverse
+ black dlgr/griduniverse
[testenv:docs]
install_command=pip install {opts} {packages}