diff --git a/.buildinfo b/.buildinfo new file mode 100644 index 00000000..a6ae2ffa --- /dev/null +++ b/.buildinfo @@ -0,0 +1,4 @@ +# Sphinx build info version 1 +# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done. +config: 038b64fa838090890154e37420263738 +tags: 645f666f9bcd5a90fca523b33c5a78b7 diff --git a/.nojekyll b/.nojekyll new file mode 100644 index 00000000..e69de29b diff --git a/API/core.html b/API/core.html new file mode 100644 index 00000000..cfd354c6 --- /dev/null +++ b/API/core.html @@ -0,0 +1,1863 @@ + + + + + + + + + Simulation Core Classes — WOMBAT v0.9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + + + +
+
+ + + + + + + +
+
+ + + +
+
+
+ +
+

Simulation Core Classes

+ +
+
+ +
+

Contents

+
+ +
+
+
+
+ +
+ +
+

Simulation Core Classes#

+

There are a variety of components that enable a simulation to be run, from the +environmen to the management of repairs to the servicing equipment. The below will show +how each of the APIs are powered to enable the full flexibility of modeling.

+ +
+

Environment#

+
+
+class wombat.core.environment.WombatEnvironment(data_dir: pathlib.Path | str, weather_file: str, workday_start: int, workday_end: int, simulation_name: Optional[str] = None, start_year: Optional[int] = None, end_year: Optional[int] = None, port_distance: Optional[Union[int, float]] = None, non_operational_start: Optional[Union[str, datetime.datetime]] = None, non_operational_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed_start: Optional[Union[str, datetime.datetime]] = None, reduced_speed_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed: float = 0.0, random_seed: Optional[int] = None, random_generator: Optional[numpy.random._generator.Generator] = None)#
+

The primary mechanism for powering an O&M simulation. This object has insight +into all other simulation objects, and controls the timing, date/time stamps, and +weather conditions.

+
+
Parameters
+
    +
  • data_dir (pathlib.Path | str) – Directory where the inputs are stored and where to save outputs.

  • +
  • weather_file (str) – Name of the weather file. Should be contained within data_dir/weather/, with +columns “datetime”, “windspeed”, and, optionally, “waveheight”. The datetime +column should adhere to the following format: “MM/DD/YY HH:MM”, in 24-hour time.

  • +
  • workday_start (int) – Starting time for the repair crew, in 24 hour local time. This can be overridden +by an ServiceEquipmentData object that operates outside of the “typical” +working hours.

  • +
  • workday_end (int) – Ending time for the repair crew, in 24 hour local time. This can be overridden +by an ServiceEquipmentData object that operates outside of the “typical” +working hours.

  • +
  • simulation_name (str | None, optional) – Name of the simulation; will be used for naming the log file, by default None. +If None, then the current time will be used. Will always save to +data_dir/outputs/logs/simulation_name.log.

    +
  • +
  • start_year (int | None, optional) – Custom starting year for the weather profile, by default None. If None or +less than the first year of the weather profile, this will be ignored.

  • +
  • end_year (int | None, optional) – Custom ending year for the weather profile, by default None. If None or +greater than the last year of the weather profile, this will be ignored.

  • +
  • port_distance (int | float) – The simulation-wide daily travel distance for servicing equipment. This +should be used as a base setting when multiple or all servicing equipment +will be operating out of the same base location, but can be individually +modified.

  • +
  • non_operational_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level, +an undefined or later starting date will be overridden for all servicing +equipment and any modeled port, by default None.

  • +
  • non_operational_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level, +an undefined or earlier ending date will be overridden for all servicing +equipment and any modeled port, by default None.

  • +
  • reduced_speed_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level, +an undefined or later starting date will be overridden for all servicing +equipment and any modeled port, by default None.

  • +
  • reduced_speed_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level, +an undefined or earlier ending date will be overridden for all servicing +equipment and any modeled port, by default None.

  • +
  • reduced_speed (float) – The maximum operating speed during the annualized reduced speed operations. +When defined at the environment level, an undefined or faster value will be +overridden for all servicing equipment and any modeled port, by default 0.0.

  • +
  • random_seed (int | None) – The random seed to be passed to a universal NumPy default_rng object to +generate Weibull random generators, by default None.

  • +
  • random_generator (np.random._generator.Generator | None) – An optional numpy random generator that can be provided to seed a simulation +with the same generator each time, in place of the random seed. If a +random_seed is also provided, this will override the random seed, +by default None.

  • +
+
+
Raises
+

FileNotFoundError – Raised if data_dir cannot be found.

+
+
+
+
+cleanup_log_files() None#
+

Convenience method to clear the output log files in case a large +batch of simulations is being run and there are space limitations.

+
+
… warning:: This shuts down the loggers, so no more logging will be able

to be performed.

+
+
+
+ +
+
+property current_time: str#
+

Timestamp for the current time as a datetime.datetime.strftime.

+
+ +
+
+date_ix(date: datetime.datetime | datetime.date) int#
+

The first index of a future date. This corresponds to the number of hours +until this dates from the very beginning of the simulation.

+
+
Parameters
+

date (datetime.datetime | datetime.date) – A date within the environment’s simulation range.

+
+
Returns
+

Index of the weather profile corresponds to the first hour of date.

+
+
Return type
+

int

+
+
+
+ +
+
+get_random_seconds(low: int = 0, high: int = 10) float#
+

Generate a random number of seconds to wait, between low and +high.

+
+
Parameters
+
    +
  • low (int, optional) – Minimum number of seconds to wait, by default 0.

  • +
  • high (int, optional) – Maximum number of seconds to wait, by default 10.

  • +
+
+
Returns
+

Number of seconds to wait.

+
+
Return type
+

float

+
+
+
+ +
+
+hour_in_shift(hour: int, workday_start: int = - 1, workday_end: int = - 1) bool#
+

Checks whether an hour is within the working hours.

+
+
Parameters
+
    +
  • hour (int) – Hour of the day.

  • +
  • workday_start (int) – A valid hour in 24 hour time, by default -1. This should only be provided +from an ServiceEquipmentData object. workday_end must also be +provided in order to be used.

  • +
  • workday_end (int) – A valid hour in 24 hour time, by default -1. This should only be provided +from an ServiceEquipmentData object. workday_start must also be +provided in order to be used.

  • +
+
+
Returns
+

True if hour is during working hours, False otherwise.

+
+
Return type
+

bool

+
+
+
+ +
+
+hours_to_next_shift(workday_start: int = - 1) float#
+

Time until the next work shift starts, in hours.

+
+
Parameters
+

workday_start (int) – A valid hour in 24 hour time, by default -1. This should only be provided +from an ServiceEquipmentData object.

+
+
Returns
+

Hours until the next shift starts.

+
+
Return type
+

float

+
+
+
+ +
+
+is_workshift(workday_start: int = - 1, workday_end: int = - 1) bool#
+

Check if the current simulation time is within the windfarm’s working hours.

+
+
Parameters
+
    +
  • workday_start (int) – A valid hour in 24 hour time, by default -1. This should only be provided +from an ServiceEquipmentData object. workday_end must also be +provided in order to be used.

  • +
  • workday_end (int) – A valid hour in 24 hour time, by default -1. This should only be provided +from an ServiceEquipmentData object. workday_start must also be +provided in order to be used.

  • +
+
+
Returns
+

True if it’s valid working hours, False otherwise.

+
+
Return type
+

bool

+
+
+
+ +
+
+load_events_log_dataframe() pandas.core.frame.DataFrame#
+

Imports the logging file created in run and returns it as a formatted +pandas.DataFrame.

+
+
Returns
+

The formatted logging data from a simulation.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+load_operations_log_dataframe() pandas.core.frame.DataFrame#
+

Imports the logging file created in run and returns it as a formatted +pandas.DataFrame.

+
+
Returns
+

The formatted logging data from a simulation.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+log_action(*, agent: str, action: str, reason: str, additional: str = '', system_id: str = '', system_name: str = '', part_id: str = '', part_name: str = '', system_ol: float | int = 0, part_ol: float | int = 0, duration: float = 0, distance_km: float = 0, request_id: str = 'na', location: str = 'na', materials_cost: int | float = 0, hourly_labor_cost: int | float = 0, salary_labor_cost: int | float = 0, equipment_cost: int | float = 0) None#
+

Formats the logging messages into the expected format for logging.

+
+
Parameters
+
    +
  • agent (str) – Agent performing the action.

  • +
  • action (str) – Action that was taken.

  • +
  • reason (str) – Reason an action was taken.

  • +
  • additional (str) – Any additional information that needs to be logged.

  • +
  • system_id (str) – Turbine ID, System.id, by default “”.

  • +
  • system_name (str) – Turbine name, System.name, by default “”.

  • +
  • part_id (str) – Subassembly, component, or cable ID, _.id, by default “”.

  • +
  • part_name (str) – Subassembly, component, or cable name, _.name, by default “”.

  • +
  • system_ol (float | int) – Turbine operating level, System.operating_level. Use an empty string +for n/a, by default 0.

  • +
  • part_ol (float | int) – Subassembly, component, or cable operating level, _.operating_level. Use +an empty string for n/a, by default 0.

  • +
  • request_id (str) – The RepairManager assigned request_id found in +RepairRequest.request_id, by default “na”.

  • +
  • location (str) – The location of where the event ocurred: should be one of site, port, +enroute, or system, by default “na”.

  • +
  • duration (float) – Length of time the action lasted, by default 0.

  • +
  • distance (float) – Distance traveled, in km, if applicable, by default 0.

  • +
  • materials_cost (Union[int, float], optional) – Total cost of materials for action, in USD, by default 0.

  • +
  • hourly_labor_cost (Union[int, float], optional) – Total cost of hourly labor for action, in USD, by default 0.

  • +
  • salary_labor_cost (Union[int, float], optional) – Total cost of salaried labor for action, in USD, by default 0.

  • +
  • equipment_cost (Union[int, float], optional) – Total cost of equipment for action, in USD, by default 0.

  • +
+
+
+
+ +
+
+power_production_potential_to_csv(windfarm: wombat.windfarm.windfarm.Windfarm, operations: Optional[pandas.core.frame.DataFrame] = None, return_df: bool = True) tuple[pandas.core.frame.DataFrame, pandas.core.frame.DataFrame]#
+

Creates the power production DataFrame and optionally returns it.

+
+
Parameters
+
    +
  • windfarm (wombat.windfarm.Windfarm) – The simulation’s windfarm object.

  • +
  • operations (Optional[pd.DataFrame], optional) – The operations log DataFrame if readily available, by default None. If +None, then it will be created through +load_operations_log_dataframe().

  • +
  • return_df (bool, optional) – Indicator to return the power production for further usage, by default True.

  • +
+
+
Returns
+

The power potential and production timeseries data.

+
+
Return type
+

Tuple[pd.DataFrame, pd.DataFrame]

+
+
+
+ +
+
+run(until: Optional[Union[int, float, simpy.events.Event]] = None)#
+

Extends the simpy.Environment.run method to change the default behavior +if no argument is passed to until, which will now run a simulation until the +end of the weather profile is reached.

+
+
Parameters
+

until (Optional[Union[int, float, Event]], optional) – When to stop the simulation, by default None. See documentation on +simpy.Environment.run for more details.

+
+
+
+ +
+
+property simulation_time: datetime.datetime#
+

Current time within the simulation (“datetime” column within weather).

+
+ +
+
+weather_forecast(hours: int | float) tuple[polars.series.series.Series, polars.series.series.Series, polars.series.series.Series, polars.series.series.Series]#
+

Returns the datetime, wind, wave, and hour data for the next hours hours, +starting from the current hour’s weather.

+
+
Parameters
+

hours (Union[int, float]) – Number of hours to look ahead, rounds up to the nearest hour.

+
+
Returns
+

Each of the relevant columns (datetime, wind, wave, hour) from the weather +profile.

+
+
Return type
+

tuple[pl.Series, pl.Series, pl.Series, pl.Series]

+
+
+
+ +
+
+property weather_now: polars.dataframe.frame.DataFrame#
+

The current weather.

+
+
Returns
+

A length 1 slice from the weather profile at the current int() rounded +hour, in simulation time.

+
+
Return type
+

pl.DataFrame

+
+
+
+ +
+ +
+
+

Repair Management#

+
+
+class wombat.core.repair_management.RepairManager(env: wombat.core.environment.WombatEnvironment, capacity: float = inf)#
+

Provides a class to manage repair and maintenance tasks.

+
+
Parameters
+
    +
  • FilterStore (simpy.resources.store.FilterStore) – The simpy class on which RepairManager is based to manage the repair and +maintenance tasks.

  • +
  • env (wombat.core.WombatEnvironment) – The simulation environment.

  • +
  • capacity (float) – The maximum number of tasks that can be submitted to the manager, by default +np.inf.

  • +
+
+
Variables
+
    +
  • env (wombat.core.WombatEnvironment) – The simulation environment.

  • +
  • windfarm (wombat.windfarm.Windfarm) – The simulated windfarm. This is only used for getting the operational capacity.

  • +
  • _current_id (int) – The logged and auto-incrememented integer base for the ID generated for each +submitted repair request.

  • +
  • downtime_based_equipment (StrategyMap) – The mapping between downtime-based servicing equipment and their capabilities.

  • +
  • request_based_equipment (StrategyMap) – The mapping between request-based servicing equipment and their capabilities.

  • +
+
+
+
+
+enable_requests_for_system(system: System | Cable, tow: bool = False) None#
+

Reenables service equipment operations on the provided system.

+
+
Parameters
+
    +
  • system_id (System | Cable) – The System or Cable that can be operated on again.

  • +
  • tow (bool, optional) – Set to True if this is for a tow-to-port request.

  • +
+
+
+
+ +
+
+get_all_requests_for_system(agent: str, system_id: str) list[wombat.core.data_classes.RepairRequest] | None | collections.abc.Generator#
+

Gets all repair requests for a specific system_id.

+
+
Parameters
+
    +
  • agent (str) – The name of the entity requesting all of a system’s repair requests.

  • +
  • system_id (Optional[str], optional) – ID of the turbine or OSS; should correspond to System.id. +the first repair requested.

  • +
+
+
Returns
+

All repair requests for a given system. If no matching requests are found, +or there aren’t any items in the queue yet, then None is returned.

+
+
Return type
+

Optional[list[RepairRequest]]

+
+
+
+ +
+
+get_request_by_severity(equipment_capability: list[str] | set[str], severity_level: Optional[int] = None) simpy.resources.store.FilterStoreGet | None#
+

Gets the next repair request by severity_level.

+
+
Parameters
+
    +
  • equipment_capability (list[str]) – The capability of the equipment requesting possible repairs to make.

  • +
  • severity_level (int) – Severity level of focus, default None.

  • +
+
+
Returns
+

Repair request meeting the required criteria.

+
+
Return type
+

Optional[FilterStoreGet]

+
+
+
+ +
+
+get_request_by_system(equipment_capability: list[str], system_id: Optional[str] = None) simpy.resources.store.FilterStoreGet | None#
+

Gets all repair requests for a certain turbine with given a sequence of +equipment_capability as long as it isn’t registered as unable to be +serviced.

+
+
Parameters
+
    +
  • equipment_capability (list[str]) – The capability of the servicing equipment requesting repairs to process.

  • +
  • system_id (Optional[str], optional) – ID of the turbine or OSS; should correspond to System.id, by default +None. If None, then it will simply sort the list by System.id and return +the first repair requested.

  • +
+
+
Returns
+

The first repair request for the focal system or None, if self.items is +empty, or there is no matching system.

+
+
Return type
+

Optional[FilterStoreGet]

+
+
+
+ +
+
+interrupt_system(system: System | Cable) None#
+

Sets the turbine status to be in servicing, and interrupts all the processes +to turn off operations.

+
+
Parameters
+

system_id (str) – The system to disable repairs.

+
+
+
+ +
+
+invalidate_system(system: System | Cable | str, tow: bool = False) None#
+

Disables the ability for servicing equipment to service a specific system, +sets the turbine status to be in servicing, and interrupts all the processes +to turn off operations.

+
+
Parameters
+
    +
  • system (System | Cable | str) – The system, cable, or str id of one to disable repairs.

  • +
  • tow (bool, optional) – Set to True if this is for a tow-to-port request.

  • +
+
+
+
+ +
+
+purge_subassembly_requests(system_id: str, subassembly_id: str, exclude: list[str] = []) list[wombat.core.data_classes.RepairRequest] | None#
+

Yields all the requests for a system/subassembly combination. This is +intended to be used to remove erroneous requests after a subassembly has been +replaced.

+
+
Parameters
+
    +
  • system_id (str) – Either the System.id or Cable.id.

  • +
  • subassembly_id (str) – Either the Subassembly.id or the Cable.id repeated for cables.

  • +
  • exclude (list[str]) – A list of request_id to exclude from the purge. This is a specific use +case for the combined cable system/subassembly, but can be to exclude +certain requests from the purge.

  • +
+
+
Yields
+

Optional[list[RepairRequest]] – All requests made to the repair manager for the provided system/subassembly +combination. Returns None if self.items is empty or the loop terminates +without finding what it is looking for.

+
+
+
+ +
+
+register_repair(repair: wombat.core.data_classes.RepairRequest) collections.abc.Generator#
+

Registers the repair as complete with the repair managiner.

+
+
Parameters
+
    +
  • repair (RepairRequest) – The repair that has been completed.

  • +
  • port (bool, optional) – If True, indicates that a port handled the repair, otherwise that a managed +servicing equipment handled the repair, by default False.

  • +
+
+
Yields
+

Generator – The completed_requests.put() that registers completion.

+
+
+
+ +
+
+register_request(request: wombat.core.data_classes.RepairRequest) wombat.core.data_classes.RepairRequest#
+

The method to submit requests to the repair mananger and adds a unique +identifier for logging.

+
+
Parameters
+

request (RepairRequest) – The request that needs to be tracked and logged.

+
+
Returns
+

The same request as passed into the method, but with a unique identifier +used for logging.

+
+
Return type
+

RepairRequest

+
+
+
+ +
+
+property request_map: dict[str, int]#
+

Creates an updated mapping between the servicing equipment capabilities and +the number of requests that fall into each capability category (nonzero values +only).

+
+ +
+
+submit_request(request: wombat.core.data_classes.RepairRequest) None#
+

The method to submit requests to the repair mananger and adds a unique +identifier for logging.

+
+
Parameters
+

request (RepairRequest) – The request that needs to be tracked and logged.

+
+
Returns
+

The same request as passed into the method, but with a unique identifier +used for logging.

+
+
Return type
+

RepairRequest

+
+
+
+ +
+ +
+
+

Servicing Equipment#

+

The servicing equipment module provides a small number of utility functions specific +to the operations of servicing equipment and the ServiceEquipment class that provides +the repair and transportation logic for scheduled, unscheduled, and unscheduled towing +servicing equipment.

+
+
+wombat.core.service_equipment.calculate_delay_from_forecast(forecast: numpy.ndarray, hours_required: int | float) tuple[bool, int]#
+

Calculates the delay from the binary weather forecast for if that hour is all +clear for operations.

+
+
Parameters
+
    +
  • forecast (np.ndarray) – Truth array to indicate if that hour satisfies the weather limit requirements.

  • +
  • hours_required (np.ndarray) – The minimum clear weather window required, in hours.

  • +
+
+
Returns
+

Indicator if a window is found (True) or not (False), and the number +of hours the event needs to be delayed in order to start.

+
+
Return type
+

tuple[bool, int]

+
+
+
+ +
+
+wombat.core.service_equipment.consecutive_groups(data: numpy.ndarray, step_size: int = 1) list[numpy.ndarray]#
+

Generates the subgroups of an array where the difference between two sequential +elements is equal to the step_size. The intent is to find the length of delays +in a weather forecast.

+
+
Parameters
+
    +
  • data (np.ndarray) – An array of integers.

  • +
  • step_size (int, optional) – The step size to be considered a consecutive number, by default 1.

  • +
+
+
Returns
+

A list of arrays of the consecutive elements of data.

+
+
Return type
+

list[np.ndarray]

+
+
+
+ +
+
+wombat.core.service_equipment.reset_system_operations(system: wombat.windfarm.system.system.System, subassembly_resets: list[str]) None#
+

Completely resets the failure and maintenance events for a given system +and its subassemblies, and puts each Subassembly.operating_level back to 100%.

+
+

Note

+

This is only intended to be used in conjunction with a tow-to-port +repair where a turbine will be completely serviced.

+
+
+
Parameters
+
    +
  • system (System) – The turbine to be reset.

  • +
  • subassembly_resets (list[str]) – The `subassembly_id`s to reset to good as new, if not assuming all +subassemblies.

  • +
+
+
+
+ +
+
+wombat.core.service_equipment.validate_end_points(start: str, end: str, no_intrasite: bool = False) None#
+

Checks the starting and ending locations for traveling and towing.

+
+
Parameters
+
    +
  • start (str) – The starting location; should be on of: “site”, “system”, or “port”.

  • +
  • end (str) – The ending location; should be on of: “site”, “system”, or “port”.

  • +
  • no_intrasite (bool) – A flag to disable intrasite travel, so that start and end cannot +both be “system”, by default False.

  • +
+
+
Raises
+
    +
  • ValueError – Raised if the starting location is invalid.

  • +
  • ValueError – Raised if the ending location is invalid

  • +
  • ValueError – Raised if “system” is provided to both start and end, but + no_intrasite is set to True.

  • +
+
+
+
+ +
+
+class wombat.core.service_equipment.ServiceEquipment(env: wombat.core.environment.WombatEnvironment, windfarm: wombat.windfarm.windfarm.Windfarm, repair_manager: wombat.core.repair_management.RepairManager, equipment_data_file: str | pathlib.Path | dict)#
+

Provides a servicing equipment object that can handle various maintenance and +repair tasks.

+
+
Parameters
+
    +
  • env (WombatEnvironment) – The simulation environment.

  • +
  • windfarm (Windfarm) – The Windfarm object.

  • +
  • repair_manager (RepairManager) – The RepairManager object.

  • +
  • equipment_data_file (str) – The equipment settings file name with extension.

  • +
+
+
Variables
+
    +
  • env (WombatEnvironment) – The simulation environment instance.

  • +
  • windfarm (Windfarm) – The simulation windfarm instance.

  • +
  • manager (RepairManager) – The simulation repair manager instance.

  • +
  • settings (ScheduledServiceEquipmentData | UnscheduledServiceEquipmentData) – The servicing equipment’s configuration settings, as provided by the user.

  • +
  • onsite (bool) – Indicates if the servicing equipment is at the site (True), or not +(False).

  • +
  • enroute (bool) – Indicates if the servicing equipment is on its way to the site (True), +or not (False).

  • +
  • at_port (bool) – Indicates if the servicing equipment is at the port, or similar location for +land-based, (True), or not (False).

  • +
  • at_system (bool) – Indications if the servicing equipment is at a cable, substation, or turbine +while on the site (True), or not (False).

  • +
  • current_system (str | None) – Either the System.id if at_system, or None if not.

  • +
  • transferring_crew (bool) – Indications if the servicing equipment is at a cable, substation, or turbine +and transferring the crew to or from that system (True), or not +(False).

  • +
+
+
+
+
+crew_transfer(system: wombat.windfarm.system.system.System | wombat.windfarm.system.cable.Cable, subassembly: wombat.windfarm.system.subassembly.Subassembly, request: wombat.core.data_classes.RepairRequest, to_system: bool) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

The process of transfering the crew from the equipment to the System +for servicing using an uninterrupted weather window to ensure safe transfer.

+
+
Parameters
+
    +
  • system (System | Cable) – The System where the crew needs to be transferred to.

  • +
  • subassembly (Subassembly) – The Subassembly that is being worked on.

  • +
  • request (RepairRequest) – The repair to be processed.

  • +
  • to_system (bool) – True if the crew is being transferred to the system, or False if the crew +is being transferred off the system.

  • +
+
+
Returns
+

None is returned when this is run recursively to not repeat the crew +transfer process.

+
+
Return type
+

None

+
+
Yields
+

Generator[Timeout | Process, None, None] – Yields a timeout event for the crew transfer once an uninterrupted weather +window can be found.

+
+
+
+ +
+
+enable_string_operations(cable: wombat.windfarm.system.cable.Cable) None#
+

Traverses the upstream cable and turbine connections and resets the +System.cable_failure and Cable.downstream_failure until it hits +another cable failure, then the loop exits.

+
+
Parameters
+

subassembly (Cable) – The Cable or System

+
+
+
+ +
+
+find_interrupted_weather_window(hours_required: int | float) tuple[pandas.core.indexes.datetimes.DatetimeIndex, numpy.ndarray, bool]#
+

Assesses at which points in the repair window, the wind (and wave) +constraints for safe operation are met.

+

The initial search looks for a weather window of length hours_required, and +adds 24 hours to the window for the proceeding 9 days. If no satisfactory window +is found, then the calling process must make another call to this function, but +likely there is something wrong with the constraints or weather conditions if +no window is found within 10 days.

+
+
Parameters
+

hours_required (int | float) – The number of hours required to complete the repair.

+
+
Returns
+

The pandas DatetimeIndex, and a corresponding boolean array for what points +in the time window are safe to complete a maintenance task or repair.

+
+
Return type
+

tuple[DatetimeIndex, np.ndarray, bool]

+
+
+
+ +
+
+find_uninterrupted_weather_window(hours_required: int | float) tuple[int | float, bool]#
+

Finds the delay required before starting on a process that won’t be able to +be interrupted by a weather delay.

+

TODO: WEATHER FORECAST NEEDS TO BE DONE FOR math.floor(self.now), not the +ceiling or there will be a whole lot of rounding up errors on process times.

+
+
Parameters
+

hours_required (int | float) – The number of uninterrupted of hours that a process requires for completion.

+
+
Returns
+

The number of hours in weather delays before a process can be completed, and +an indicator for if the process has to be delayed until the next shift for +a safe transfer.

+
+
Return type
+

tuple[int | float, bool]

+
+
+
+ +
+
+finish_setup_with_environment_variables() None#
+

A post-initialization step that will override unset parameters with those +from the the environemt that may have already been set.

+
+ +
+
+get_next_request()#
+

Gets the next request by the rig’s method for processing repairs.

+
+
Returns
+

The next RepairRequest to be processed.

+
+
Return type
+

simpy.resources.store.FilterStoreGet

+
+
+
+ +
+
+get_speed(tow: bool = False) float#
+

Determines the appropriate speed that the servicing equipment should be +traveling at for towing or traveling, and if the timeframe is during a reduced +speed scenario.

+
+
Parameters
+

tow (bool, optional) – True indicates the servicing equipment should be using the towing speed, +and if False, then the traveling speed should be used, by default False.

+
+
Returns
+

The maximum speed the servicing equipment should be traveling/towing at.

+
+
Return type
+

float

+
+
+
+ +
+
+hours_to_next_operational_date(start_search_date: datetime.datetime | datetime.date | pandas._libs.tslibs.timestamps.Timestamp, exclusion_days: int = 0) float#
+

Calculates the number of hours until the first date that is not a part of +the non_operational_dates given a starting datetime and for search criteria. +Optionally, exclusion_days can be used to account for a mobilization period +so that mobilization can occur during the non operational period.

+
+
Parameters
+
    +
  • start_search_date (datetime.datetime | datetime.date | pd.Timestamp) – The first date to be considered in the search.

  • +
  • exclusion_days (int, optional) – The number of days to subtract from the next available datetime that +represents a non operational action that can occur during the non +operational period, such as mobilization, by default 0.

  • +
+
+
Returns
+

The total number of hours until the next operational date.

+
+
Return type
+

float

+
+
+
+ +
+
+in_situ_repair(request: wombat.core.data_classes.RepairRequest, time_processed: int | float = 0, prior_operation_level: float = - 1.0, initial: bool = False) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

Processes the repair including any weather and shift delays.

+
+
Parameters
+
    +
  • request (RepairRequest) – The Maintenance or Failure receiving attention.

  • +
  • time_processed (int | float, optional) – Time that has already been processed, by default 0.

  • +
  • prior_operation_level (float, optional) – The operating level of the System just before the repair has begun, by +default -1.0.

  • +
  • initial (bool, optional) – True for first step in a potentially-recursive logic, otherwise False. When +True, the repair manager will turn off the system being worked on, but if +done multiple times, the simulation will error out.

  • +
+
+
Yields
+

Generator[Timeout | Process, None, None] – Timeouts for the repair process.

+
+
+
+ +
+
+initialize_cost_calculators(which: str) None#
+

Creates the cost calculators for each of the subclasses that will need to +calculate hourly costs.

+
+
Parameters
+

which (str) – One of “port” or “equipment” to to indicate how to access equipment costs

+
+
+
+ +
+
+mobilize() collections.abc.Generator[simpy.events.Timeout, None, None]#
+

Mobilizes the ServiceEquipment object.

+

NOTE: weather delays are not accounted for at this stage.

+
+
Yields
+

Generator[Timeout, None, None] – A Timeout event for the number of hours the ServiceEquipment requires for +mobilizing to the windfarm site.

+
+
+
+ +
+
+mobilize_scheduled() collections.abc.Generator[simpy.events.Timeout, None, None]#
+

Mobilizes the ServiceEquipment object by waiting for the next operational +period, less the mobilization time, then logging the mobiliztion cost.

+

NOTE: weather delays are not accounted for in this process.

+
+
Yields
+

Generator[Timeout, None, None] – A Timeout event for the number of hours between when the function is called +and when the next operational period starts.

+
+
+
+ +
+
+mooring_connection(system: wombat.windfarm.system.system.System, request: wombat.core.data_classes.RepairRequest, which: str) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

The process of either umooring a floating turbine to prepare for towing it to +port, or reconnecting it after its repairs have been completed.

+
+
Parameters
+
    +
  • system (System) – The System that needs unmooring/reconnecting.

  • +
  • request (RepairRequest) – The repair to be processed.

  • +
  • which (bool) – “unmoor” for unmooring the turbine, “reconnect” for reconnecting the +turbine.

  • +
+
+
Returns
+

None is returned when this is run recursively to not repeat the process.

+
+
Return type
+

None

+
+
Yields
+

Generator[Timeout | Process, None, None] – Yields a timeout event for the unmooring/reconnection once an uninterrupted +weather window can be found.

+
+
+
+ +
+
+process_repair(hours: int | float, request_details: wombat.core.data_classes.Maintenance | wombat.core.data_classes.Failure, **kwargs) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

The logging and timeout process for performing a repair or doing maintenance.

+
+
Parameters
+
    +
  • hours (int | float) – The lenght, in hours, of the repair or maintenance task.

  • +
  • request_details (Maintenance | Failure) – The deatils of the request, this is only being checked for the type.

  • +
  • kwargs (dict) – Additional parameters to be passed to WombatEnvironment.log_action.

  • +
+
+
Yields
+

Generator[Timeout | Process, None, None] – A Timeout is yielded of length hours.

+
+
+
+ +
+
+register_repair_with_subassembly(subassembly: wombat.windfarm.system.subassembly.Subassembly | wombat.windfarm.system.cable.Cable, repair: wombat.core.data_classes.RepairRequest, starting_operating_level: float) None#
+

Goes into the repaired subassembly, component, or cable and returns its +operating_level back to good as new for the specific repair. For fatal cable +failures, all upstream turbines are turned back on unless there is another fatal +cable failure preventing any more from operating.

+
+
Parameters
+
    +
  • subassembly (Subassembly | Cable) – The subassembly or cable that was repaired.

  • +
  • repair (RepairRequest) – The request for repair that was submitted.

  • +
  • starting_operating_level (float) – The operating level before a repair was started.

  • +
+
+
+
+ +
+
+run_scheduled_in_situ() collections.abc.Generator[simpy.events.Process, None, None]#
+

Runs the simulation of in situ repairs for scheduled servicing equipment +that have the onsite designation or don’t require mobilization.

+
+
Yields
+

Generator[Process, None, None] – The simulation.

+
+
+
+ +
+
+run_tow_to_port(request: wombat.core.data_classes.RepairRequest) collections.abc.Generator[simpy.events.Process, None, None]#
+

Runs the tow to port logic, so a turbine can be repaired at port.

+
+
Parameters
+

request (RepairRequest) – The request the triggered the tow-to-port strategy.

+
+
Yields
+

Generator[Process, None, None] – The series of events that simulate the complete towing logic.

+
+
Raises
+

ValueError – Raised if the equipment is not currently at port

+
+
+
+ +
+
+run_tow_to_site(request: wombat.core.data_classes.RepairRequest, subassembly_resets: list[str] = []) collections.abc.Generator[simpy.events.Process, None, None]#
+

Runs the tow to site logic for after a turbine has had its repairs completed +at port.

+
+
Parameters
+
    +
  • request (RepairRequest) – The request the triggered the tow-to-port strategy.

  • +
  • subassembly_resets (list[str]) – The `subassembly_id`s to reset to good as new. Defaults to [].

  • +
+
+
Yields
+

Generator[Process, None, None] – The series of events that simulate the complete towing logic.

+
+
Raises
+

ValueError – Raised if the equipment is not currently at port

+
+
+
+ +
+
+run_unscheduled_in_situ() collections.abc.Generator[simpy.events.Process, None, None]#
+

Runs an in situ repair simulation for unscheduled servicing equipment, or +those that have to be mobilized before performing repairs and maintenance.

+
+
Yields
+

Generator[Process, None, None] – The simulation

+
+
+
+ +
+
+tow(start: str, end: str, set_current: Optional[str] = None, **kwargs) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

The process for towing a turbine to/from port.

+
+
Parameters
+
    +
  • start (str) – The starting location; one of “site” or “port”.

  • +
  • end (str) – The ending location; one of “site” or “port”.

  • +
  • set_current (str | None, optional) – The System.id if the turbine is being returned to site, by default None

  • +
+
+
Yields
+

Generator[Timeout | Process, None, None] – The series of SimPy events that will be processed for the actions to occur.

+
+
+
+ +
+
+travel(start: str, end: str, set_current: Optional[str] = None, hours: Optional[float] = None, distance: Optional[float] = None, **kwargs) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

The process for traveling between port and site, or two systems onsite.

+

NOTE: This does not currently take the weather conditions into account.

+
+
Parameters
+
    +
  • start (str) – The starting location, one of “site”, “port”, or “system”.

  • +
  • end (str) – The starting location, one of “site”, “port”, or “system”.

  • +
  • set_current (str, optional) – Where to set current_system to be if traveling to site or a different +system onsite, by default None.

  • +
  • hours (float, optional) – The number hours required for traveling between start and end. +If provided, no internal travel time will be calculated.

  • +
  • distance (float, optional) – The distance, in km, to be traveled. Only used if hours is provided

  • +
+
+
Yields
+

Generator[Timeout | Process, None, None] – The timeout event for traveling.

+
+
+
+ +
+
+wait_until_next_operational_period(*, less_mobilization_hours: int = 0) collections.abc.Generator[simpy.events.Timeout, None, None]#
+

Delays the crew and equipment until the start of the next operational +period.

+

TODO: Need a custom error if weather doesn’t align with the equipment dates.

+
+
Parameters
+

less_mobilization_hours (int) – The number of hours required for mobilization that will be subtracted from +the waiting period to account for mobilization, by default 0.

+
+
Yields
+

Generator[Timeout, None, None] – A Timeout event for the number of hours between when the function is called +and when the next operational period starts.

+
+
+
+ +
+
+wait_until_next_shift(**kwargs) collections.abc.Generator[simpy.events.Timeout, None, None]#
+

Delays the process until the start of the next shift.

+
+
Yields
+

Generator[Timeout, None, None] – Delay until the start of the next shift.

+
+
+
+ +
+
+weather_delay(hours: int | float, **kwargs) collections.abc.Generator[simpy.events.Event, Any, Any]#
+

Processes a weather delay of length hours hours. If hours = 0, then +a Timeout is still processed, but not logging is done (this is to increase +consistency and do better typing validation across the codebase).

+
+
Parameters
+

hours (int | float) – The lenght, in hours, of the weather delay.

+
+
Yields
+

Generator[Event, Any, Any] – If the delay is more than 0 hours, then a Timeout is yielded of length +hours.

+
+
+
+ +
+ +
+
+

Port#

+

Creates the Port class that provies the tow-to-port repair capabilities for +offshore floating wind farms. The Port will control a series of tugboats enabled +through the “TOW” capability that get automatically dispatched once a tow-to-port repair +is submitted and a tugboat is available (ServiceEquipment.at_port). The Port also +controls any mooring repairs through the “AHV” capability, which operates similarly to +the tow-to-port except that it will not be released until the repair is completed, and +operates on a strict shift scheduling basis.

+
+
+class wombat.core.port.Port(env: wombat.core.environment.WombatEnvironment, windfarm: wombat.windfarm.windfarm.Windfarm, repair_manager: wombat.core.repair_management.RepairManager, config: dict | str | pathlib.Path)#
+

The offshore wind base port that operates tugboats and performs tow-to-port +repairs.

+
+

Note

+

The operating costs for the port are incorporated into the FixedCosts +functionality in the high-levl cost bucket: +operations_management_administration or the more granula cost bucket: +marine_management

+
+
+
Parameters
+
    +
  • env (WombatEnvironment) – The simulation environment instance.

  • +
  • windfarm (Windfarm) – The simulation windfarm instance.

  • +
  • repair_manager (RepairManager) – The simulation repair manager instance.

  • +
  • config (dict | str | Path) – A path to a YAML object or dictionary encoding the port’s configuration +settings. This will be loaded into a PortConfig object during +initialization.

  • +
+
+
Variables
+
    +
  • env (WombatEnvironment) – The simulation environment instance.

  • +
  • windfarm (Windfarm) – The simulation windfarm instance.

  • +
  • manager (RepairManager) – The simulation repair manager instance.

  • +
  • settings (PortConfig) – The port’s configuration settings, as provided by the user.

  • +
  • requests_serviced (set[str]) – The set of requests that have already been serviced to ensure there are no +duplications of labor when splitting out the repair requests to be processed.

  • +
  • turbine_manager (simpy.Resource) – A SimPy Resource object that limits the number of turbines that can be towed +to port, so as not to overload the quayside waters, which is controlled by +settings.max_operations.

  • +
  • crew_manager (simpy.Resource) – A SimPy Resource object that limts the number of repairs that can be +occurring at any given time, which is controlled by settings.n_crews.

  • +
  • service_equipment_manager (simpy.FilterStore) – A SimPy FilterStore object that acts as a coordination system for the +registered tugboats to tow turbines between port and site. In order to tow +in either direction they must be filtered by ServiceEquipment.at_port. This +is generated from the tugboat definitions in settings.tugboats.

  • +
  • active_repairs (dict[str, dict[str, simpy.events.Event]]) – A nested dictionary of turbines, and its associated request IDs with a SimPy +Event. The use of events allows them to automatically succeed at the end of +repairs, and once all repairs are processed on a turbine, the tow-to-site +process can commence.

  • +
+
+
+
+
+get_all_requests_for_system(system_id: str) None | collections.abc.Generator[simpy.resources.store.FilterStoreGet, None, None]#
+

Gets all repair requests for a specific system_id.

+
+
Parameters
+

system_id (Optional[str], optional) – ID of the turbine or OSS; should correspond to System.id. +the first repair requested.

+
+
Returns
+

All repair requests for a given system. If no matching requests are found, +or there aren’t any items in the queue yet, then None is returned.

+
+
Return type
+

Optional[Generator[FilterStoreGet]]

+
+
+
+ +
+
+hours_to_next_operational_date(start_search_date: datetime.datetime | datetime.date | pandas._libs.tslibs.timestamps.Timestamp, exclusion_days: int = 0) float#
+

Calculates the number of hours until the first date that is not a part of +the non_operational_dates given a starting datetime and for search criteria. +Optionally, exclusion_days can be used to account for a mobilization period +so that mobilization can occur during the non operational period.

+
+
Parameters
+
    +
  • start_search_date (datetime.datetime | datetime.date | pd.Timestamp) – The first date to be considered in the search.

  • +
  • exclusion_days (int, optional) – The number of days to subtract from the next available datetime that +represents a non operational action that can occur during the non +operational period, such as mobilization, by default 0.

  • +
+
+
Returns
+

The total number of hours until the next operational date.

+
+
Return type
+

float

+
+
+
+ +
+
+initialize_cost_calculators(which: str) None#
+

Creates the cost calculators for each of the subclasses that will need to +calculate hourly costs.

+
+
Parameters
+

which (str) – One of “port” or “equipment” to to indicate how to access equipment costs

+
+
+
+ +
+
+process_repair(hours: int | float, request_details: wombat.core.data_classes.Maintenance | wombat.core.data_classes.Failure, **kwargs) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

The logging and timeout process for performing a repair or doing maintenance.

+
+
Parameters
+
    +
  • hours (int | float) – The lenght, in hours, of the repair or maintenance task.

  • +
  • request_details (Maintenance | Failure) – The deatils of the request, this is only being checked for the type.

  • +
  • kwargs (dict) – Additional parameters to be passed to WombatEnvironment.log_action.

  • +
+
+
Yields
+

Generator[Timeout | Process, None, None] – A Timeout is yielded of length hours.

+
+
+
+ +
+
+repair_single(request: wombat.core.data_classes.RepairRequest) collections.abc.Generator[simpy.events.Timeout | simpy.events.Process, None, None]#
+

Simulation logic to process a single repair request.

+
+
Parameters
+

request (RepairRequest) – The submitted repair or maintenance request.

+
+
+
+ +
+
+run_repairs(system_id: str) collections.abc.Generator | None#
+

Method that transfers the requests from the repair manager and initiates the +repair sequence.

+
+
Parameters
+

system_id (str) – The System.id that is has been towed to port.

+
+
+
+ +
+
+run_tow_to_port(request: wombat.core.data_classes.RepairRequest) collections.abc.Generator[simpy.events.Process, None, None]#
+

The method to initiate a tow-to-port repair sequence.

+

The process follows the following following routine:

+
    +
  1. Request a tugboat from the tugboat resource manager and wait

  2. +
  3. +
    Runs ServiceEquipment.tow_to_port, which encapsulates the traveling to

    site, unmooring, and return tow with a turbine

    +
    +
    +
  4. +
  5. Transfers the the turbine’s repair log to the port, and gets all available +crews to work on repairs immediately

  6. +
  7. Requests a tugboat to return the turbine to site

  8. +
  9. Runs ServiceEquipment.tow_to_site(), which encapsulates the tow back to +site, reconnection, resetting the operating status, and returning back to +port

  10. +
+
+
Parameters
+

request (RepairRequest) – The request that initiated the process. This is primarily used for logging +purposes.

+
+
Yields
+

Generator[Process, None, None] – The series of events constituting the tow-to-port repairs

+
+
+
+ +
+
+run_unscheduled_in_situ(request: wombat.core.data_classes.RepairRequest, initial: bool = False) collections.abc.Generator[simpy.events.Process, None, None]#
+

Runs the in-situ repair processes for port-based servicing equipment such as +tugboats that will always return back to port, but are not necessarily a feature +of the windfarm itself, such as a crew transfer vessel.

+
+
Parameters
+

request (RepairRequest) – The request that triggered the non tow-to-port, but port-based servicing +equipment repair.

+
+
Yields
+

Generator[Process, None, None] – The travel and repair processes.

+
+
+
+ +
+
+transfer_requests_from_manager(system_id: str) None | list[wombat.core.data_classes.RepairRequest] | collections.abc.Generator#
+

Gets all of a given system’s repair requests from the simulation’s repair +manager, removes them from that queue, and puts them in the port’s queue.

+
+
Parameters
+

system_id (str) – The System.id attribute from the system that will be repaired at port.

+
+
Returns
+

The list of repair requests that need to be completed at port.

+
+
Return type
+

None | list[RepairRequest]

+
+
+
+ +
+
+wait_until_next_shift(**kwargs) collections.abc.Generator[simpy.events.Timeout, None, None]#
+

Delays the process until the start of the next shift.

+
+
Yields
+

Generator[Timeout, None, None] – Delay until the start of the next shift.

+
+
+
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/API/index.html b/API/index.html new file mode 100644 index 00000000..42763aae --- /dev/null +++ b/API/index.html @@ -0,0 +1,538 @@ + + + + + + + + + WOMBAT API — WOMBAT v0.9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + + + +
+
+ + + + + + + +
+
+ + +
+
+ Contents +
+ +
+
+
+
+ +
+

WOMBAT API

+ +
+
+ +
+

Contents

+
+ +
+
+
+
+ +
+ +
+

WOMBAT API#

+

The WOMBAT framework relies on a set of base data classes powered by the attrs +library and a series of simulation classes and methods to perform all the operations.

+

To make a it easier for users, there is also a simulation interface provided.

+
+

Package Hierarchy#

+
+
+

Class Hierarchy#

+ + +
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/API/simulation_api.html b/API/simulation_api.html new file mode 100644 index 00000000..436a4682 --- /dev/null +++ b/API/simulation_api.html @@ -0,0 +1,1298 @@ + + + + + + + + + Simulation API — WOMBAT v0.9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + + + +
+
+ + + + + + + +
+
+ + + +
+
+
+ +
+

Simulation API

+ +
+ +
+
+
+ +
+ +
+

Simulation API#

+ +
+

Configuration#

+
+
+class wombat.core.simulation_api.Configuration(name: str, layout: str, service_equipment: Sequence | str | int | float, weather: str | pd.DataFrame, workday_start, workday_end, inflation_rate, project_capacity, fixed_costs: dict | str | Path = None, port: dict | str | Path = None, start_year: int = None, end_year: int = None, port_distance: int | float = None, non_operational_start: str | datetime.datetime | None = None, non_operational_end: str | datetime.datetime | None = None, reduced_speed_start: str | datetime.datetime | None = None, reduced_speed_end: str | datetime.datetime | None = None, reduced_speed: float = 0.0, random_seed: int | None = None, random_generator: np.random._generator.Generator | None = None)#
+

The Simulation configuration data class that provides all the necessary +definitions.

+
+
Parameters
+
    +
  • name (str) – Name of the simulation. Used for logging files.

  • +
  • layout (str) – The windfarm layout file. See wombat.Windfarm for more details.

  • +
  • service_equipment (str | list[str]) – The equpiment that will be used in the simulation. See +wombat.core.ServiceEquipment for more details.

  • +
  • weather (str) – The weather profile to be used. See wombat.simulation.WombatEnvironment +for more details.

  • +
  • workday_start (int) – Starting hour for a typical work shift. Can be overridden by +equipment-specific settings.

  • +
  • workday_end (int) – Ending hour for a typical work shift. Can be overridden by +equipment-specific settings.

  • +
  • inflation_rate (float) – The annual inflation rate to be used for post-processing.

  • +
  • fixed_costs (str) – The file name for the fixed costs assumptions.

  • +
  • project_capacity (int | float) – The total capacity of the wind plant, in MW.

  • +
  • port (dict | str | Path) – The port configuration file or dictionary that will be used to setup a +tow-to-port repair strategy, default None.

  • +
  • port_distance (int | float) – The simulation-wide daily travel distance for servicing equipment. This should +be used as a base setting when multiple or all servicing equipment will be +operating out of the same base location, but can be individually modified.

  • +
  • start_year (int) – Start year of the simulation. The exact date will be determined by +the first valid date of this year in weather.

  • +
  • end_year (int) – Final year of the simulation. The exact date will be determined by +the last valid date of this year in weather.

  • +
  • non_operational_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level, an +undefined or later starting date will be overridden for all servicing equipment +and any modeled port, by default None.

  • +
  • non_operational_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level, an +undefined or earlier ending date will be overridden for all servicing equipment +and any modeled port, by default None.

  • +
  • reduced_speed_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level, an +undefined or later starting date will be overridden for all servicing equipment +and any modeled port, by default None.

  • +
  • reduced_speed_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level, an +undefined or earlier ending date will be overridden for all servicing equipment +and any modeled port, by default None.

  • +
  • reduced_speed (float) – The maximum operating speed during the annualized reduced speed operations. +When defined at the environment level, an undefined or faster value will be +overridden for all servicing equipment and any modeled port, by default 0.0.

  • +
  • random_seed (int | None) – The random seed to be passed to a universal NumPy default_rng object to +generate Weibull random generators, by default None.

  • +
  • random_generator (np.random._generator.Generator | None) – An optional numpy random generator that can be provided to seed a simulation +with the same generator each time, in place of the random seed. If a +random_seed is also provided, this will override the random seed, +by default None.

  • +
+
+
+
+ +
+
+

Simulation Interface#

+
+
+class wombat.core.simulation_api.Simulation(library_path: str | pathlib.Path, config: wombat.core.simulation_api.Configuration, random_seed: Optional[int] = None, random_generator: Optional[numpy.random._generator.Generator] = None)#
+

The primary API to interact with the simulation methodologies.

+
+
Parameters
+
    +
  • library_path (str) – The path to the main data library.

  • +
  • config (Configuration | dict | str) –

    +
    +
    One of the following:
      +
    • A pre-loaded Configuration object

    • +
    • A dictionary ready to be converted to a Configuration object

    • +
    • The name of the configuration file to be loaded, that will be located at: +library_path / config / config

    • +
    +
    +
    +
  • +
  • random_seed (int | None) – The random seed to be passed to a universal NumPy default_rng object to +generate Weibull random generators, by default None.

  • +
  • random_generator (np.random._generator.Generator | None) – An optional numpy random generator that can be provided to seed a simulation +with the same generator each time, in place of the random seed. If a +random_seed is also provided, this will override the random seed, +by default None.

  • +
+
+
+
+
+classmethod from_config(library_path: str | pathlib.Path, config: str | pathlib.Path | dict | wombat.core.simulation_api.Configuration)#
+

Creates the Simulation object only the configuration contents as either a +full file path to the configuration file, a dictionary of the configuration +contents, or pre-loaded Configuration object.

+
+
Parameters
+
    +
  • library_path (str | Path) – The simulation’s data library. If a filename is provided for +config, this is the data library from where it will be imported. +This will also be used to feed into the returned Simulation.library_path.

  • +
  • config (str | Path | dict | Configuration) – The simulation configuration, see Configuration for more details on the +contents. The following is a description of the acceptable contents:

    +
      +
    • str : the full file path of the configuration yaml file.

    • +
    • dict : a dictionary with the requried configuration settings.

    • +
    • Configuration : a pre-created Configuration object.

    • +
    +
  • +
+
+
Raises
+

TypeError – Raised if config is not one of the three acceptable input types.

+
+
Returns
+

A ready-to-run Simulation object.

+
+
Return type
+

Simulation

+
+
+
+ +
+
+run(until: Optional[Union[int, float, simpy.events.Event]] = None, create_metrics: bool = True, save_metrics_inputs: bool = True)#
+

Calls WombatEnvironment.run() and gathers the results for +post-processing. See wombat.simulation.WombatEnvironment.run or +simpy.Environment.run for more details.

+
+
Parameters
+
    +
  • until (Optional[int | float | Event], optional) – When to stop the simulation, by default None. See documentation on +simpy.Environment.run for more details.

  • +
  • create_metrics (bool, optional) – If True, the metrics object will be created, and not, if False, by default +True.

  • +
  • save_metrics_inputs (bool, optional) – If True, the metrics inputs data will be saved to a yaml file, with file +references to any larger data structures that can be reloaded later. If +False, the data will not be saved, by default True.

  • +
+
+
+
+ +
+
+save_metrics_inputs() None#
+

Saves the inputs for the Metrics initialization with either a direct +copy of the data or a file reference that can be loaded later.

+
+ +
+ +
+
+

Metrics Computation#

+

For example usage of the Metrics class and its associated methods, please see the examples documentation page

+
+
+class wombat.core.post_processor.Metrics(data_dir: str | pathlib.Path, events: str | pandas.core.frame.DataFrame, operations: str | pandas.core.frame.DataFrame, potential: str | pandas.core.frame.DataFrame, production: str | pandas.core.frame.DataFrame, inflation_rate: float, project_capacity: float, turbine_capacities: list[float], substation_id: str | list[str], turbine_id: str | list[str], substation_turbine_map: dict[str, dict[str, list[str]]], service_equipment_names: str | list[str], fixed_costs: Optional[str] = None)#
+

The metric computation class for storing logs and compiling results.

+
+
+classmethod from_simulation_outputs(fpath: pathlib.Path | str, fname: str) wombat.core.post_processor.Metrics#
+

Creates the Metrics class from the saved outputs of a simulation for ease of +revisiting the calculated metrics.

+
+
Parameters
+
    +
  • fpath (Path | str) – The full path to the file where the data was saved.

  • +
  • fname (Path | str) – The filename for where the data was saved, which should be a direct +dictionary mapping for the Metrics initialization.

  • +
+
+
Returns
+

The class object.

+
+
Return type
+

Metrics

+
+
+
+ +
+
+time_based_availability(frequency: str, by: str) pandas.core.frame.DataFrame#
+

Calculates the time-based availabiliy over a project’s lifetime as a single +value, annual average, or monthly average for the whole windfarm or by turbine.

+
+

Note

+

This currently assumes that if there are multiple substations, that +the turbines are all connected to multiple.

+
+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by (str) – One of “windfarm” or “turbine”.

  • +
+
+
Returns
+

The time-based availability at the desired aggregation level.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+production_based_availability(frequency: str, by: str) pandas.core.frame.DataFrame#
+

Calculates the production-based availabiliy over a project’s lifetime as a +single value, annual average, or monthly average for the whole windfarm or by +turbine.

+
+

Note

+

This currently assumes that if there are multiple substations, that +the turbines are all connected to multiple.

+
+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by (str) – One of “windfarm” or “turbine”.

  • +
+
+
Returns
+

The production-based availability at the desired aggregation level.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+capacity_factor(which: str, frequency: str, by: str) pandas.core.frame.DataFrame#
+

Calculates the capacity factor over a project’s lifetime as a single value, +annual average, or monthly average for the whole windfarm or by turbine.

+
+

Note

+

This currently assumes that if there are multiple substations, that +the turbines are all connected to multiple.

+
+
+
Parameters
+
    +
  • which (str) – One of “net” or “gross”.

  • +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by (str) – One of “windfarm” or “turbine”.

  • +
+
+
Returns
+

The capacity factor at the desired aggregation level.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+task_completion_rate(which: str, frequency: str) float | pandas.core.frame.DataFrame#
+

Calculates the task completion rate (including tasks that are canceled after +a replacement event) over a project’s lifetime as a single value, annual +average, or monthly average for the whole windfarm or by turbine.

+
+
Parameters
+
    +
  • which (str) – One of “scheduled”, “unscheduled”, or “both”.

  • +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
+
+
Returns
+

The task completion rate at the desired aggregation level.

+
+
Return type
+

float | pd.DataFrame

+
+
+
+ +
+
+equipment_costs(frequency: str, by_equipment: bool = False) pandas.core.frame.DataFrame#
+

Calculates the equipment costs for the simulation at a project, annual, or +monthly level with (or without) respect to equipment utilized in the simulation. +This excludes any port fees that might apply, which are included in: +port_fees.

+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by_equipment (bool, optional) – Indicates whether the values are with resepect to the equipment utilized +(True) or not (False), by default False.

  • +
+
+
Returns
+

+
Returns pandas DataFrame with columns:
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • then any equipment names as they appear in the logs

  • +
+
+
+

+
+
Return type
+

pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_equipment is not one of True or False.

  • +
+
+
+
+ +
+
+service_equipment_utilization(frequency: str) pandas.core.frame.DataFrame#
+

Calculates the utilization rate for each of the service equipment in the +simulation as the ratio of total number of days each of the servicing +equipment is in operation over the total number of days it’s present in the +simulation. This number excludes mobilization time and the time between +visits for scheduled servicing equipment strategies.

+
+

Note

+

For tugboats in a tow-to-port scenario, this ratio will be near +100% because they are considered to be operating on an as-needed basis per +the port contracting assumptions

+
+
+
Parameters
+

frequency (str) – One of “project” or “annual”.

+
+
Returns
+

The utilization rate of each of the simulation SerivceEquipment.

+
+
Return type
+

pd.DataFrame

+
+
Raises
+

ValueError – If frequency is not one of “project” or “annual”.

+
+
+
+ +
+
+vessel_crew_hours_at_sea(frequency: str, by_equipment: bool = False, vessel_crew_assumption: dict[str, float] = {}) pandas.core.frame.DataFrame#
+

Calculates the total number of crew hours at sea that occurred during a +simulation at a project, annual, or monthly level that can be broken out by +servicing equipment. This includes time mobilizing, delayed at sea, servicing, +towing, and traveling.

+
+

Note

+

This metric is intended to be used for offshore wind simulations.

+
+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by_equipment (bool, optional) – Indicates whether the values are with resepect to each tugboat (True) or not +(False), by default False.

  • +
  • vessel_crew_assumption (dict[str, float], optional) – Dictionary of vessel names (ServiceEquipment.settings.name) and number +of crew members aboard to trannsform the results from vessel hours at sea +to crew hours at sea.

  • +
+
+
Returns
+

Returns a pandas DataFrame with columns:

+
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • Total Crew Hours at Sea

  • +
  • {ServiceEquipment.settings.name} (if broken out)

  • +
+

+
+
Return type
+

pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_equipment is not one of True or False.

  • +
  • ValueError – If vessel_crew_assumption is not a dictionary.

  • +
+
+
+
+ +
+
+number_of_tows(frequency: str, by_tug: bool = False, by_direction: bool = False) float | pandas.core.frame.DataFrame#
+

Calculates the total number of tows that occurred during a simulation at a +project, annual, or monthly level that can be broken out by tugboat.

+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by_tug (bool, optional) – Indicates whether the values are with resepect to each tugboat (True) or not +(False), by default False.

  • +
  • by_direction (bool, optional) – Indicates whether the values are with respect to the direction a turbine is +towed (True) or not (False), by default False.

  • +
+
+
Returns
+

Returns either a float for whole project-level costs or a pandas +DataFrame with columns:

+
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • total_tows

  • +
  • total_tows_to_port (if broken out)

  • +
  • total_tows_to_site (if broken out)

  • +
  • {ServiceEquipment.settings.name}_total_tows (if broken out)

  • +
  • {ServiceEquipment.settings.name}_to_port (if broken out)

  • +
  • {ServiceEquipment.settings.name}_to_site (if broken out)

  • +
+

+
+
Return type
+

float | pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_tug is not one of True or False.

  • +
  • ValueError – If by_direction is not one of True or False.

  • +
+
+
+
+ +
+
+labor_costs(frequency: str, by_type: bool = False) float | pandas.core.frame.DataFrame#
+

Calculates the labor costs for the simulation at a project, annual, or +monthly level that can be broken out by hourly and salary labor costs.

+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by_type (bool, optional) – Indicates whether the values are with resepect to the labor types +(True) or not (False), by default False.

  • +
+
+
Returns
+

Returns either a float for whole project-level costs or a pandas +DataFrame with columns:

+
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • total_labor_cost

  • +
  • hourly_labor_cost (if broken out)

  • +
  • salary_labor_cost (if broken out)

  • +
+

+
+
Return type
+

float | pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_type is not one of True or False.

  • +
+
+
+
+ +
+
+equipment_labor_cost_breakdowns(frequency: str, by_category: bool = False, by_equipment: bool = False) pandas.core.frame.DataFrame#
+

Calculates the producitivty cost and time breakdowns for the simulation at a +project, annual, or monthly level that can be broken out to include the +equipment and labor components, as well as be broken down by servicing +equipment.

+
+

Note

+

Doesn’t produce a value if there’s no cost associated with a “reason”.

+
+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by_category (bool, optional) – Indicates whether to include the equipment and labor categories (True) or +not (False), by default False.

  • +
  • by_equipment (bool, optional) – Indicates whether the values are with resepect to the equipment utilized +(True) or not (False), by default False.

  • +
+
+
Returns
+

+
Returns pandas DataFrame with columns:
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • reason

  • +
  • hourly_labor_cost (if by_category == True)

  • +
  • salary_labor_cost (if by_category == True)

  • +
  • total_labor_cost (if by_category == True)

  • +
  • equipment_cost (if by_category == True)

  • +
  • total_cost (if broken out)

  • +
  • total_hours

  • +
+
+
+

+
+
Return type
+

pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_category is not one of True or False.

  • +
+
+
+
+ +
+
+emissions(emissions_factors: dict, maneuvering_factor: float = 0.1, port_engine_on_factor: float = 0.25) pandas.core.frame.DataFrame#
+

Calculates the emissions, typically in tons, per hour of operations for +transiting, maneuvering (calculated as a % of transiting), idling at the site +(repairs, crew transfer, weather delays), and idling at port (weather delays), +excluding waiting overnight between shifts.

+
+
Parameters
+
    +
  • emissions_factors (dict) – Dictionary of emissions per hour for “transit”, “maneuver”, “idle at site”, +and “idle at port” for each of the servicing equipment in the simulation.

  • +
  • maneuvering_factor (float, optional) – The proportion of transit time that can be attributed to +maneuvering/positioning, by default 0.1.

  • +
  • port_engine_on_factor (float, optional) – The proportion of idling at port time that can be attributed to having the +engine on and producing emissions, by default 0.25.

  • +
+
+
Returns
+

DataFrame of “duration” (hours), “distance_km”, and “emissions” (tons) for +each servicing equipment in the simulation for each emissions category.

+
+
Return type
+

pd.DataFrame

+
+
Raises
+
    +
  • KeyError – Raised if any of the servicing equipment are missing from the + emissions_factors dictionary.

  • +
  • KeyError – Raised if any of the emissions categories are missing from each servcing + equipment definition in emissions_factors.

  • +
+
+
+
+ +
+
+component_costs(frequency: str, by_category: bool = False, by_action: bool = False) pandas.core.frame.DataFrame#
+

Calculates the component costs for the simulation at a project, annual, or +monthly level that can be broken out by cost categories. This will not sum to +the total cost because it is does not include times where there is no work being +done, but costs are being accrued.

+
+

Note

+

It should be noted that the costs will include costs accrued from both +weather delays and shift-to-shift delays. In the future these will be +disentangled.

+
+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by_category (bool, optional) – Indicates whether the values are with resepect to the various cost +categories (True) or not (False), by default False.

  • +
  • by_action (bool, optional) – Indicates whether component costs are going to be further broken out by the +action being performed–repair, maintenance, and delay–(True) or not +(False), by default False.

  • +
+
+
Returns
+

Returns either a float for whole project-level costs or a pandas +DataFrame with columns:

+
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • component

  • +
  • action (if broken out)

  • +
  • materials_cost (if broken out)

  • +
  • total_labor_cost (if broken out)

  • +
  • equipment_cost (if broken out)

  • +
  • total_cost

  • +
+

+
+
Return type
+

float | pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_category is not one of True or False.

  • +
  • ValueError – If by_action is not one of True or False.

  • +
+
+
+
+ +
+
+port_fees(frequency: str) pandas.core.frame.DataFrame#
+

Calculates the port fees for the simulation at a project, annual, or monthly +level. This excludes any equipment or labor costs, which are included in: +equipment_costs.

+
+
Parameters
+

frequency (str) – One of “project” or “annual”, “monthly”, “.

+
+
Returns
+

The broken out by time port fees with

+
+
Return type
+

pd.DataFrame

+
+
Raises
+

ValueError – If frequency not one of “project” or “annual”.

+
+
+
+ +
+
+project_fixed_costs(frequency: str, resolution: str) pandas.core.frame.DataFrame#
+

Calculates the fixed costs of a project at the project and annual frequencies +at a given cost breakdown resolution.

+
+
Parameters
+
    +
  • frequency (str) – One of “project” or “annual”, “monthly”, “.

  • +
  • resolution (st) – One of “low”, “medium”, or “high”, where the values correspond to:

    +
      +
    • low: FixedCosts.resolution["low"], corresponding to itemized costs.

    • +
    • medium: FixedCosts.resolution["medium"], corresponding to the +overarching cost categories.

    • +
    • high: FixedCosts.resolution["high"], corresponding to a lump sum.

    • +
    +

    These values can also be seen through the FixedCosts.hierarchy

    +
  • +
+
+
Returns
+

The project’s fixed costs as a sum or annualized with high, medium, and low +resolution as desired.

+
+
Return type
+

pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency not one of “project” or “annual”.

  • +
  • ValueError – If resolution must be one of “low”, “medium”, or “high”.

  • +
+
+
+
+ +
+
+opex(frequency: str, by_category: bool = False) pandas.core.frame.DataFrame#
+

Calculates the project’s OpEx for the simulation at a project, annual, or +monthly level.

+
+
Parameters
+
    +
  • frequency (str) – One of project, annual, monthly, or month-year.

  • +
  • by_category (bool, optional) – Indicates whether the values are with resepect to the various cost +categories (True) or not (False), by default False.

  • +
+
+
Returns
+

The project’s OpEx broken out at the desired time and category resolution.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+process_times() pandas.core.frame.DataFrame#
+

Calculates the time, in hours, to complete a repair/maintenance request, on +both a request to completion basis, and the actual time to complete the repair.

+
+
Returns
+

    +
  • category (index): repair/maintenance category

  • +
  • time_to_completion: total number of hours from the time of request to the +time of completion

  • +
  • process_time: total number of hours it took for the equipment to complete

  • +
  • the request.

  • +
  • downtime: total number of hours where the operations were below 100%.

  • +
  • N: total number of processes in the category.

  • +
+

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+
+power_production(frequency: str, by: str = 'windfarm', units: str = 'gwh') float | pandas.core.frame.DataFrame#
+

Calculates the power production for the simulation at a project, annual, or +monthly level that can be broken out by turbine.

+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • by (str) – One of “windfarm” or “turbine”.

  • +
  • units (str) – One of “gwh”, “mwh”, or “kwh”.

  • +
+
+
Returns
+

Returns either a float for whole project-level costs or a pandas +DataFrame with columns:

+
    +
  • year (if appropriate for frequency)

  • +
  • month (if appropriate for frequency)

  • +
  • total_power_production

  • +
  • <turbine_id>_power_production (if broken out)

  • +
+

+
+
Return type
+

float | pd.DataFrame

+
+
Raises
+
    +
  • ValueError – If frequency is not one of “project”, “annual”, “monthly”, or + “month-year”.

  • +
  • ValueError – If by_turbine is not one of True or False.

  • +
+
+
+
+ +
+
+npv(frequency: str, discount_rate: float = 0.025, offtake_price: float = 80) pandas.core.frame.DataFrame#
+

Calculates the net present value of the windfarm at a project, annual, or +monthly resolution given a base discount rate and offtake price.

+
+

Note

+

This function will be improved over time to incorporate more of the +financial parameter at play, such as PPAs.

+
+
+
Parameters
+
    +
  • frequency (str) – One of “project”, “annual”, “monthly”, or “month-year”.

  • +
  • discount_rate (float, optional) – The rate of return that could be earned on alternative investments, by +default 0.025.

  • +
  • offtake_price (float, optional) – Price of energy, per MWh, by default 80.

  • +
+
+
Returns
+

The project net prsent value at the desired time resolution.

+
+
Return type
+

pd.DataFrame

+
+
+
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/API/types.html b/API/types.html new file mode 100644 index 00000000..fb7dc052 --- /dev/null +++ b/API/types.html @@ -0,0 +1,1574 @@ + + + + + + + + + Data Classes — WOMBAT v0.9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + + + +
+
+ + + + + + + +
+
+ + + +
+
+
+ + +
+ +
+ +
+

Data Classes#

+

The WOMBAT architecture relies heavily on a base set of data classes to process most of +the model’s inputs. This enables a rigid, yet robust data model to properly define a +simulation.

+ +
+

What is this FromDictMixin I keep seeing in the code diagrams?#

+

The FromDictMixin class provides a standard method for providing dictionary definitions +to attrs dataclasses without worrying about overloading the definition. Plus, you get +the convenience of writing cls.from_dict(data_dict) instead of cls(**data_dict), and +hoping for the best.

+
+
+class wombat.core.data_classes.FromDictMixin#
+

A Mixin class to allow for kwargs overloading when a data class doesn’t +have a specific parameter definied. This allows passing of larger dictionaries +to a data class without throwing an error.

+
+
Raises
+

AttributeError – Raised if the required class inputs are not provided.

+
+
+
+
+classmethod from_dict(data: dict)#
+

Map a data dictionary to an attrs-defined class.

+

TODO: Add an error to ensure that either none or all the parameters are passed

+
+
Parameters
+

data (dict) – The data dictionary to be mapped.

+
+
Returns
+

cls – The attrs-defined class.

+
+
Return type
+

Any

+
+
+
+ +
+ +
+
+

Scheduled and Unscheduled Maintenance#

+
+

Maintenance Tasks#

+
+
+class wombat.core.data_classes.Maintenance(time, materials, frequency, service_equipment: collections.abc.Sequence | str | int | float, system_value, description='routine maintenance', operation_reduction=0.0, level=0)#
+

Data class to store maintenance data used in subassembly and cable modeling.

+
+
Parameters
+
    +
  • time (float) – Amount of time required to perform maintenance, in hours.

  • +
  • materials (float) – Cost of materials required to perform maintenance, in USD.

  • +
  • frequency (float) – Optimal number of days between performing maintenance, in days.

  • +
  • service_equipment (list[str] | str) – Any combination of th following Equipment.capability options.

    +
      +
    • RMT: remote (no actual equipment BUT no special implementation)

    • +
    • DRN: drone

    • +
    • CTV: crew transfer vessel/vehicle

    • +
    • SCN: small crane (i.e., field support vessel)

    • +
    • LCN: large crane (i.e., heavy lift vessel)

    • +
    • CAB: cabling vessel/vehicle

    • +
    • DSV: diving support vessel

    • +
    • TOW: tugboat or towing equipment

    • +
    • AHV: anchor handling vessel (tugboat that doesn’t trigger tow-to-port)

    • +
    +
  • +
  • system_value (Union[int, float]) – Turbine replacement value. Used if the materials cost is a proportional cost.

  • +
  • description (str) – A short text description to be used for logging.

  • +
  • operation_reduction (float) – Performance reduction caused by the failure, between (0, 1]. Defaults to 0.

    +
    +

    Warning

    +

    As of v0.7, availability is very sensitive to the usage of this +parameter, and so it should be used carefully.

    +
    +
  • +
  • level (int, optional) – Severity level of the maintenance. Defaults to 0.

  • +
+
+
+
+
+assign_id(request_id: str) None#
+

Assign a unique identifier to the request.

+
+
Parameters
+

request_id (str) – The wombat.core.RepairManager generated identifier.

+
+
+
+ +
+
+request_id: str#
+
+ +
+ +
+
+

Failures#

+
+
+class wombat.core.data_classes.Failure(scale, shape, time, materials, operation_reduction, level, service_equipment: collections.abc.Sequence | str | int | float, system_value, rng: numpy.random._generator.Generator, replacement=False, description='failure')#
+

Data class to store failure data used in subassembly and cable modeling.

+
+
Parameters
+
    +
  • scale (float) – Weibull scale parameter for a failure classification.

  • +
  • shape (float) – Weibull shape parameter for a failure classification.

  • +
  • time (float) – Amount of time required to complete the repair, in hours.

  • +
  • materials (float) – Cost of the materials required to complete the repair, in $USD.

  • +
  • operation_reduction (float) – Performance reduction caused by the failure, between (0, 1].

    +
    +

    Warning

    +

    As of v0.7, availability is very sensitive to the usage of this +parameter, and so it should be used carefully.

    +
    +
  • +
  • level (int, optional) – Level of severity, will be generated in the ComponentData.create_severities +method.

  • +
  • service_equipment (list[str] | str) – Any combination of the following Equipment.capability options:

    +
      +
    • RMT: remote (no actual equipment BUT no special implementation)

    • +
    • DRN: drone

    • +
    • CTV: crew transfer vessel/vehicle

    • +
    • SCN: small crane (i.e., field support vessel)

    • +
    • LCN: large crane (i.e., heavy lift vessel)

    • +
    • CAB: cabling vessel/vehicle

    • +
    • DSV: diving support vessel

    • +
    • TOW: tugboat or towing equipment

    • +
    • AHV: anchor handling vessel (tugboat that doesn’t trigger tow-to-port)

    • +
    +
  • +
  • system_value (Union[int, float]) – Turbine replacement value. Used if the materials cost is a proportional cost.

  • +
  • replacement (bool) – True if triggering the failure requires a subassembly replacement, False, if +only a repair is necessary. Defaults to False

  • +
  • description (str) – A short text description to be used for logging.

  • +
  • rng (np.random._generator.Generator) –

    +
    +

    Note

    +

    Do not provide this, it comes from +wombat.core.environment.WombatEnvironment

    +
    +

    The shared random generator used for the Weibull sampling. This is fed through +the simulation environment to ensure consistent seeding between simulations.

    +
  • +
+
+
+
+
+assign_id(request_id: str) None#
+

Assign a unique identifier to the request.

+
+
Parameters
+

request_id (str) – The wombat.core.RepairManager generated identifier.

+
+
+
+ +
+
+hours_to_next_failure() float | None#
+

Sample the next time to failure in a Weibull distribution. If the scale +and shape parameters are set to 0, then the model will return None to +cause the subassembly to timeout to the end of the simulation.

+
+
Returns
+

Returns None for a non-modelled failure, or the time until the next +simulated failure.

+
+
Return type
+

float | None

+
+
+
+ +
+
+request_id: str#
+
+ +
+ +
+
+

Repair Requests#

+
+
+class wombat.core.data_classes.RepairRequest(system_id, system_name, subassembly_id, subassembly_name, severity_level, details: wombat.core.data_classes.Failure | wombat.core.data_classes.Maintenance, *, cable=False, upstream_turbines: list[str] = _Nothing.NOTHING, upstream_cables: list[str] = _Nothing.NOTHING)#
+

Repair/Maintenance request data class.

+
+
Parameters
+
    +
  • system_id (str) – System.id.

  • +
  • system_name (str) – System.name.

  • +
  • subassembly_id (str) – Subassembly.id.

  • +
  • subassembly_name (str) – Subassembly.name.

  • +
  • severity_level (int) – Maintenance.level or Failure.level.

  • +
  • details (Failure | Maintenance) – The actual data class.

  • +
  • cable (bool) – Indicator that the request is for a cable, by default False.

  • +
  • upstream_turbines (list[str]) – The cable’s upstream turbines, by default []. No need to use this if +cable == False.

  • +
  • upstream_cables (list[str]) – The cable’s upstream cables, by default []. No need to use this if +cable == False.

  • +
+
+
+
+
+assign_id(request_id: str) None#
+

Assign a unique identifier to the request.

+
+
Parameters
+

request_id (str) – The wombat.core.RepairManager generated identifier.

+
+
+
+ +
+
+request_id: str#
+
+ +
+ +
+
+
+

Servicing Equipment and Crews#

+
+

Service Equipment#

+
+
+class wombat.core.data_classes.ServiceEquipmentData(data_dict: dict, *, strategy: Optional[str] = None)#
+

Helps to determine the type ServiceEquipment that should be used, based on the +repair strategy for its operation. See +ScheduledServiceEquipmentData or +UnscheduledServiceEquipmentData for more details on each +classifcation.

+
+
Parameters
+
    +
  • data_dict (dict) – The dictionary that will be used to create the appropriate ServiceEquipmentData. +This should contain a field called ‘strategy’ with either “scheduled” or +“unscheduled” as a value if strategy is not provided as a keyword argument.

  • +
  • strategy (str, optional) – Should be one of “scheduled”, “requests”, “downtime”. If nothing is provided, +the equipment configuration will be checked.

  • +
+
+
Raises
+

ValueError – Raised if strategy is not one of “scheduled” or “unscheduled”.

+
+
+

Examples

+

The below workflow is how a new data +ScheduledServiceEquipmentData object could be created via +a generic/routinized creation method, and is how the +ServiceEquipment’s __init__ method creates the +settings data.

+
>>> from wombat.core.data_classes import  ServiceEquipmentData
+>>>
+>>> data_dict = {
+>>>     "name": "Crew Transfer Vessel 1",
+>>>     "equipment_rate": 1750,
+>>>     "start_month": 1,
+>>>     "start_day": 1,
+>>>     "end_month": 12,
+>>>     "end_day": 31,
+>>>     "start_year": 2002,
+>>>     "end_year": 2014,
+>>>     "onsite": True,
+>>>     "capability": "CTV",
+>>>     "max_severity": 10,
+>>>     "mobilization_cost": 0,
+>>>     "mobilization_days": 0,
+>>>     "speed": 37.04,
+>>>     "max_windspeed_transport": 99,
+>>>     "max_windspeed_repair": 99,
+>>>     "max_waveheight_transport": 1.5,
+>>>     "max_waveheight_repair": 1.5,
+>>>     "strategy": scheduled,
+>>>     "crew_transfer_time": 0.25,
+>>>     "n_crews": 1,
+>>>     "crew": {
+>>>         "day_rate": 0,
+>>>         "n_day_rate": 0,
+>>>         "hourly_rate": 0,
+>>>         "n_hourly_rate": 0,
+>>>     },
+>>> }
+>>> equipment = ServiceEquipmentData(data_dict).determine_type()
+>>> type(equipment)
+
+
+
+
+determine_type() wombat.core.data_classes.ScheduledServiceEquipmentData | wombat.core.data_classes.UnscheduledServiceEquipmentData#
+

Generate the appropriate ServiceEquipmentData variation.

+
+
Returns
+

The appropriate xxServiceEquipmentData schema depending on the strategy +the ServiceEquipment will use.

+
+
Return type
+

Union[ScheduledServiceEquipmentData, UnscheduledServiceEquipmentData]

+
+
+
+ +
+ +
+
+

ServiceCrew#

+
+
+class wombat.core.data_classes.ServiceCrew(n_day_rate, day_rate, n_hourly_rate, hourly_rate)#
+

An internal data class for the indivdual crew units that are on the servicing +equipment.

+
+
Parameters
+
    +
  • n_day_rate (int) – Number of salaried workers.

  • +
  • day_rate (float) – Day rate for salaried workers, in USD.

  • +
  • n_hourly_rate (int) – Number of hourly/subcontractor workers.

  • +
  • hourly_rate (float) – Hourly labor rate for subcontractors, in USD.

  • +
+
+
+
+ +
+
+

Scheduled Service Equipment#

+
+
+class wombat.core.data_classes.ScheduledServiceEquipmentData(name, equipment_rate, n_crews, crew: dict, capability: collections.abc.Sequence | str | int | float, speed, max_windspeed_transport, max_windspeed_repair, mobilization_cost=0, mobilization_days=0, max_waveheight_transport=1000.0, max_waveheight_repair=1000.0, workday_start=- 1, workday_end=- 1, crew_transfer_time=0.0, speed_reduction_factor=0.0, port_distance=0.0, onsite=False, method='severity', start_month=- 1, start_day=- 1, start_year=- 1, end_month=- 1, end_day=- 1, end_year=- 1, strategy: str = 'scheduled', non_operational_start: Optional[Union[str, datetime.datetime]] = None, non_operational_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed_start: Optional[Union[str, datetime.datetime]] = None, reduced_speed_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed=0)#
+

The data class specification for servicing equipment that will use a +pre-scheduled basis for returning to site.

+
+
Parameters
+
    +
  • name (str) – Name of the piece of servicing equipment.

  • +
  • equipment_rate (float) – Day rate for the equipment/vessel, in USD.

  • +
  • n_crews (int) – Number of crew units for the equipment.

    +
    +

    Note

    +

    The input to this does not matter yet, as multi-crew functionality +is not yet implemented.

    +
    +
  • +
  • crew (ServiceCrew) – The crew details, see ServiceCrew for more information. Dictionary +of labor costs with the following: n_day_rate, day_rate, +n_hourly_rate, and hourly_rate.

  • +
  • start_month (int) – The day to start operations for the rig and crew.

  • +
  • start_day (int) – The month to start operations for the rig and crew.

  • +
  • start_year (int) – The year to start operations for the rig and crew.

  • +
  • end_month (int) – The month to end operations for the rig and crew.

  • +
  • end_day (int) – The day to end operations for the rig and crew.

  • +
  • end_year (int) – The year to end operations for the rig and crew.

    +
    +

    Note

    +

    if the rig comes annually, then the enter the year for the last year +that the rig and crew will be available.

    +
    +
  • +
  • capability (str) – The type of capabilities the equipment contains. Must be one of:

    +
      +
    • RMT: remote (no actual equipment BUT no special implementation)

    • +
    • DRN: drone

    • +
    • CTV: crew transfer vessel/vehicle

    • +
    • SCN: small crane (i.e., field support vessel)

    • +
    • LCN: large crane (i.e., heavy lift vessel)

    • +
    • CAB: cabling vessel/vehicle

    • +
    • DSV: diving support vessel

    • +
    +

    Please note that “TOW” is unavailable for scheduled servicing equipment

    +
  • +
  • mobilization_cost (float) – Cost to mobilize the rig and crew.

  • +
  • mobilization_days (int) – Number of days it takes to mobilize the equipment.

  • +
  • speed (float) – Maximum transit speed, km/hr.

  • +
  • speed_reduction_factor (flaot) – Reduction factor for traveling in inclement weather, default 0. When 0, travel +is stopped when either max_windspeed_transport or max_waveheight_transport +is reached, and when 1, speed is used.

  • +
  • max_windspeed_transport (float) – Maximum windspeed for safe transport, m/s.

  • +
  • max_windspeed_repair (float) – Maximum windspeed for safe operations, m/s.

  • +
  • max_waveheight_transport (float) – Maximum waveheight for safe transport, m, default 1000 (land-based).

  • +
  • max_waveheight_repair (float) – Maximum waveheight for safe operations, m, default 1000 (land-based).

  • +
  • workday_start (int) – The starting hour of a workshift, in 24 hour time.

  • +
  • workday_end (int) – The ending hour of a workshift, in 24 hour time.

  • +
  • crew_transfer_time (float) – The number of hours it takes to transfer the crew from the equipment to the +system, e.g. how long does it take to transfer the crew from the CTV to the +turbine, default 0.

  • +
  • onsite (bool) – Indicator for if the servicing equipment and crew are based onsite.

    +
    +

    Note

    +

    If based onsite, be sure that the start and end dates represent the +first and last day/month of the year, respectively, and the start and end +years represent the fist and last year in the weather file.

    +
    +
  • +
  • method (str) – Determines if the equipment will do all maximum severity repairs first or do all +the repairs at one turbine before going to the next, by default severity. Must +be one of “severity” or “turbine”.

  • +
  • port_distance (int | float) – The distance, in km, the equipment must travel to go between port and site, by +default 0.

  • +
  • non_operational_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level, an +undefined or later starting date will be overridden, by default None.

  • +
  • non_operational_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level, an +undefined or earlier ending date will be overridden, by default None.

  • +
  • reduced_speed_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level, an +undefined or later starting date will be overridden, by default None.

  • +
  • reduced_speed_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level, an +undefined or earlier ending date will be overridden, by default None.

  • +
  • reduced_speed (float) – The maximum operating speed during the annualized reduced speed operations. +When defined at the environment level, an undefined or faster value will be +overridden, by default 0.0.

  • +
+
+
+
+
+non_operational_dates: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+
+non_operational_dates_set: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+
+non_stop_shift: bool#
+
+ +
+
+operating_dates_set: set#
+
+ +
+
+reduced_speed_dates: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+ +
+
+

Unscheduled Service Equipment#

+
+
+class wombat.core.data_classes.UnscheduledServiceEquipmentData(name, equipment_rate, n_crews, crew: dict, capability: collections.abc.Sequence | str | int | float, speed, max_windspeed_transport, max_windspeed_repair, mobilization_cost=0, mobilization_days=0, max_waveheight_transport=1000.0, max_waveheight_repair=1000.0, workday_start=- 1, workday_end=- 1, crew_transfer_time=0.0, speed_reduction_factor=0.0, port_distance=0.0, onsite=False, method='severity', strategy: str | None = 'unscheduled', strategy_threshold=- 1, charter_days=- 1, tow_speed=1, unmoor_hours=0, reconnection_hours=0, non_operational_start: Optional[Union[str, datetime.datetime]] = None, non_operational_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed_start: Optional[Union[str, datetime.datetime]] = None, reduced_speed_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed=0)#
+

The data class specification for servicing equipment that will use either a +basis of windfarm downtime or total number of requests serviceable by the equipment.

+
+
Parameters
+
    +
  • name (str) – Name of the piece of servicing equipment.

  • +
  • equipment_rate (float) – Day rate for the equipment/vessel, in USD.

  • +
  • n_crews (int) – Number of crew units for the equipment.

    +
  • +
  • crew (ServiceCrew) – The crew details, see ServiceCrew for more information. Dictionary +of labor costs with the following: n_day_rate, day_rate, +n_hourly_rate, and hourly_rate.

  • +
  • charter_days (int) – The number of days the servicing equipment can be chartered for.

  • +
  • capability (str) – The type of capabilities the equipment contains. Must be one of: +- RMT: remote (no actual equipment BUT no special implementation) +- DRN: drone +- CTV: crew transfer vessel/vehicle +- SCN: small crane (i.e., field support vessel) +- LCN: large crane (i.e., heavy lift vessel) +- CAB: cabling vessel/vehicle +- DSV: diving support vessel +- TOW: tugboat or towing equipment +- AHV: anchor handling vessel (tugboat that doesn’t trigger tow-to-port)

  • +
  • speed (float) – Maximum transit speed, km/hr.

  • +
  • tow_speed (float) – The maximum transit speed when towing, km/hr.

    +
    +

    Note

    +

    This is only required for when the servicing equipment is tugboat +enabled for a tow-to-port scenario (capability = “TOW”)

    +
    +
  • +
  • speed_reduction_factor (flaot) – Reduction factor for traveling in inclement weather, default 0. When 0, travel +is stopped when either max_windspeed_transport or max_waveheight_transport +is reached, and when 1, speed is used.

  • +
  • max_windspeed_transport (float) – Maximum windspeed for safe transport, m/s.

  • +
  • max_windspeed_repair (float) – Maximum windspeed for safe operations, m/s.

  • +
  • max_waveheight_transport (float) – Maximum waveheight for safe transport, m, default 1000 (land-based).

  • +
  • max_waveheight_repair (float) – Maximum waveheight for safe operations, m, default 1000 (land-based).

  • +
  • mobilization_cost (float) – Cost to mobilize the rig and crew, default 0.

  • +
  • mobilization_days (int) – Number of days it takes to mobilize the equipment, default 0.

  • +
  • strategy (str) – For any unscheduled maintenance servicing equipment, this determines the +strategy for dispatching. Should be on of “downtime” or “requests”.

  • +
  • strategy_threshold (str) – For downtime-based scenarios, this is based on the operating level, and should +be in the range (0, 1). For reqest-based scenarios, this is the maximum number +of requests that are allowed to build up for any given type of unscheduled +servicing equipment, should be an integer >= 1.

  • +
  • workday_start (int) – The starting hour of a workshift, in 24 hour time.

  • +
  • workday_end (int) – The ending hour of a workshift, in 24 hour time.

  • +
  • crew_transfer_time (float) – The number of hours it takes to transfer the crew from the equipment to the +system, e.g. how long does it take to transfer the crew from the CTV to the +turbine, default 0.

  • +
  • onsite (bool) – Indicator for if the rig and crew are based onsite.

    +
    +

    Note

    +

    if the rig and crew are onsite be sure that the start and end dates +represent the first and last day/month of the year, respectively, and the +start and end years represent the fist and last year in the weather file.

    +
    +
  • +
  • method (str) – Determines if the ship will do all maximum severity repairs first or do all +the repairs at one turbine before going to the next, by default severity. +Should by one of “severity” or “turbine”.

  • +
  • unmoor_hours (int | float) – The number of hours required to unmoor a floating offshore wind turbine in order +to tow it to port, by default 0.

    +
    +

    Note

    +

    Required for the tugboat/towing capability, otherwise unused.

    +
    +
  • +
  • reconnection_hours (int | float) – The number of hours required to reconnect a floating offshore wind turbine after +being towed back to site, by default 0.

    +
    +

    Note

    +

    Required for the tugboat/towing capability, otherwise unused.

    +
    +
  • +
  • port_distance (int | float) – The distance, in km, the equipment must travel to go between port and site, by +default 0.

  • +
  • non_operational_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level or the +port level, if a tugboat, an undefined or later starting date will be +overridden, by default None.

  • +
  • non_operational_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the environment level or the +port level, if a tugboat, an undefined or earlier ending date will be +overridden, by default None.

  • +
  • reduced_speed_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level or the +port level, if a tugboat, an undefined or later starting date will be +overridden, by default None.

  • +
  • reduced_speed_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the environment level or the +port level, if a tugboat, an undefined or earlier ending date will be +overridden, by default None.

  • +
  • reduced_speed (float) – The maximum operating speed during the annualized reduced speed operations. +When defined at the environment level, an undefined or faster value will be +overridden, by default 0.0.

  • +
+
+
+
+
+non_operational_dates: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+
+non_operational_dates_set: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+
+non_stop_shift: bool#
+
+ +
+
+reduced_speed_dates: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+ +
+
+

Port Configuration#

+
+
+class wombat.core.data_classes.PortConfig(name, tugboats: collections.abc.Sequence | str | int | float, crew: dict, n_crews=1, max_operations=1, workday_start=- 1, workday_end=- 1, site_distance=0.0, annual_fee=0, non_operational_start: Optional[Union[str, datetime.datetime]] = None, non_operational_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed_start: Optional[Union[str, datetime.datetime]] = None, reduced_speed_end: Optional[Union[str, datetime.datetime]] = None, reduced_speed=0)#
+

Port configurations for offshore wind power plant scenarios.

+
+
Parameters
+
    +
  • name (str) – The name of the port, if multiple are used, then be sure this is unique.

  • +
  • tugboats (list[str]) – file, or list of files to create the port’s tugboats.

    +
    +

    Note

    +

    Each tugboat is considered to be a tugboat + supporting vessels as +the primary purpose to tow turbines between a repair port and site.

    +
    +
  • +
  • n_crews (int) – The number of service crews available to be working on repairs simultaneously; +each crew is able to service exactly one repair.

  • +
  • crew (ServiceCrew) – The crew details, see ServiceCrew for more information. Dictionary +of labor costs with the following: n_day_rate, day_rate, +n_hourly_rate, and hourly_rate.

  • +
  • max_operations (int) – Total number of turbines the port can handle simultaneously.

  • +
  • workday_start (int) – The starting hour of a workshift, in 24 hour time.

  • +
  • workday_end (int) – The ending hour of a workshift, in 24 hour time.

  • +
  • site_distance (int | float) – Distance, in km, a tugboat has to travel to get between site and port.

  • +
  • annual_fee (int | float) – The annualized fee for access to the repair port that will be distributed +monthly in the simulation and accounted for on the first of the month from the +start of the simulation to the end of the simulation.

    +
    +

    Note

    +

    Don’t include this cost in both this category and either the +FixedCosts.operations_management_administration bucket or +FixedCosts.marine_management category.

    +
    +
  • +
  • non_operational_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the port level, an undefined or +later starting date will be overridden by the environment, and any associated +tubboats will have this value overridden using the same logic, by default None.

  • +
  • non_operational_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of prohibited operations. When defined at the port level, an undefined +or earlier ending date will be overridden by the environment, and any associated +tubboats will have this value overridden using the same logic, by default None.

  • +
  • reduced_speed_start (str | datetime.datetime | None) – The starting month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the port level, an undefined +or later starting date will be overridden by the environment, and any associated +tubboats will have this value overridden using the same logic, by default None.

  • +
  • reduced_speed_end (str | datetime.datetime | None) – The ending month and day, e.g., MM/DD, M/D, MM-DD, etc. for an annualized +period of reduced speed operations. When defined at the port level, an undefined +or earlier ending date will be overridden by the environment, and any associated +tubboats will have this value overridden using the same logic, by default None.

  • +
  • reduced_speed (float) – The maximum operating speed during the annualized reduced speed operations. +When defined at the port level, an undefined or faster value will be overridden +by the environment, and any associated tubboats will have this value overridden +using the same logic, by default 0.0.

  • +
+
+
+
+
+non_operational_dates: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+
+reduced_speed_dates: pandas.core.indexes.datetimes.DatetimeIndex#
+
+ +
+ +
+
+
+

Wind Farm Support#

+
+

Subassembly Model#

+
+
+class wombat.core.data_classes.SubassemblyData(name, maintenance: list[wombat.core.data_classes.Maintenance | dict[str, float | str]], failures: list[wombat.core.data_classes.Failure | dict[str, float | str]] | dict[int, wombat.core.data_classes.Failure | dict[str, float | str]], system_value, rng: numpy.random._generator.Generator)#
+

Data storage and validation class for the subassemblies.

+
+
Parameters
+
    +
  • name (str) – Name of the component/subassembly.

  • +
  • maintenance (list[dict[str, float | str]]) – List of the maintenance classification dictionaries. This will be converted +to a list of Maintenance objects in the post initialization hook.

  • +
  • failures (dict[int, dict[str, float | str]]) – Dictionary of failure classifications in a numerical (ordinal) categorization +order. This will be converted to a dictionary of Failure objects in the +post initialization hook.

  • +
  • system_value (int | float) – Turbine’s cost of replacement. Used in case percentages of turbine cost are used +in place of an absolute cost.

  • +
+
+
+
+ +
+
+

Wind Farm Map#

+
+
+class wombat.core.data_classes.WindFarmMap(substation_map: dict[str, wombat.core.data_classes.SubstationMap], export_cables: list[tuple[str, str]])#
+

A list of the upstream connections for a turbine and its downstream connector.

+
+
Parameters
+
    +
  • substation_map (list[str]) – A dictionary mapping of each substation and its SubstationMap.

  • +
  • export_cables (list[tuple[str, str]]) – A list of the export cable connections.

  • +
+
+
+
+
+get_upstream_connections(substation: str, string_start: str, node: str, return_cables: bool = True) list[str] | tuple[list[str], list[str]]#
+

Retrieve the upstream turbines (and optionally cables) within the wind farm +graph.

+
+
Parameters
+
    +
  • substation (str) – The substation’s System.id.

  • +
  • string_start (str) – The System.id of the first turbine in the string.

  • +
  • node (str) – The System.id of the ending node for a cable connection.

  • +
  • return_cables (bool) – Indicates if the Cable.id should be generated for each of the turbines, +by default True.

  • +
+
+
Returns
+

A list of System.id for all of the upstream turbines of node if +cables=False, otherwise the upstream turbine and the Cable.id lists +are returned.

+
+
Return type
+

list[str] | tuple[list[str], list[str]]

+
+
+
+ +
+
+get_upstream_connections_from_substation(substation: str, return_cables: bool = True, by_string: bool = True) list[str] | tuple[list[str], list[str]] | list[list[str]] | tuple[list[list[str]], list[list[str]]]#
+

Retrieve the upstream turbines (and optionally, cables) connected to a +py:attr:substation in the wind farm graph.

+
+
Parameters
+
    +
  • substation (str) – The py:attr:System.id for the substation.

  • +
  • return_cables (bool, optional) – Indicates if the Cable.id should be generated for each of the turbines, +by default True

  • +
  • by_string (bool, optional) – Indicates if the list of turbines (and cables) should be a nested list for +each string (py:obj:True), or as 1-D list (py:obj:False), by default +True.

  • +
+
+
Returns
+

A list of System.id for all of the upstream turbines of node if +return_cables=False, otherwise the upstream turbine and the Cable.id +lists are returned. These are bifurcated in lists of lists for each string +if by_string=True

+
+
Return type
+

list[str] | tuple[list[str], list[str]]

+
+
+
+ +
+ +
+
+

Substation Map#

+
+
+class wombat.core.data_classes.SubstationMap(string_starts: list[str], string_map: dict[str, wombat.core.data_classes.String], downstream: str)#
+

A mapping of every String connected to a substation, excluding export +connections to other substations.

+
+
Parameters
+
    +
  • string_starts (list[str]) – A list of every first turbine’s System.id in a string connected to the +substation.

  • +
  • string_map (dict[str, String]) – A dictionary mapping each string starting turbine to its String data.

  • +
  • downstream (str) – The System.id of where the export cable leads. This should be the same +System.id as the substation for an interconnection point, or another +connecting substation.

  • +
+
+
+
+ +
+
+

String#

+
+
+class wombat.core.data_classes.String(start: str, upstream_map: dict[str, wombat.core.data_classes.SubString])#
+

All of the connection information for a complete string in a wind farm.

+
+
Parameters
+
    +
  • start (str) – The substation’s ID (System.id)

  • +
  • upstream_map (dict[str, SubString]) – The dictionary of each turbine ID in the string and it’s upstream SubString.

  • +
+
+
+
+ +
+
+

Sub String#

+
+
+class wombat.core.data_classes.SubString(downstream: str, upstream: list[str])#
+

A list of the upstream connections for a turbine and its downstream connector.

+
+
Parameters
+
    +
  • downstream (str) – The downstream turbine/substation connection id.

  • +
  • upstream (list[str]) – A list of the upstream turbine connections.

  • +
+
+
+
+ +
+
+
+

Miscellaneous#

+
+

Fixed Cost Model#

+
+
+class wombat.core.data_classes.FixedCosts(operations: float = 0, operations_management_administration: float = 0, project_management_administration: float = 0, marine_management: float = 0, weather_forecasting: float = 0, condition_monitoring: float = 0, operating_facilities: float = 0, environmental_health_safety_monitoring: float = 0, insurance: float = 0, brokers_fee: float = 0, operations_all_risk: float = 0, business_interruption: float = 0, third_party_liability: float = 0, storm_coverage: float = 0, annual_leases_fees: float = 0, submerge_land_lease_costs: float = 0, transmission_charges_rights: float = 0, onshore_electrical_maintenance: float = 0, labor: float = 0)#
+

Fixed costs for operating a windfarm. All values are assumed to be in $/kW/yr.

+
+
Parameters
+
    +
  • operations (float) – Non-maintenance costs of operating the project. If a value is provided for this +attribute, then it will zero out all other values, otherwise it will be set to +the sum of the remaining values.

  • +
  • operations_management_administration (float) – Activities necessary to forecast, dispatch, sell, and manage the production of +power from the plant. Includes both the onsite and offsite personnel, software, +and equipment to coordinate high voltage equipment, switching, port activities, +and marine activities.

    +
    +

    Note

    +

    This should only be used when not breaking down the cost into the +following categories: project_management_administration, +operation_management_administration, marine_management, and/or +weather_forecasting

    +
    +
  • +
  • project_management_administration (float) – Financial reporting, public relations, procurement, parts and stock management, +H&SE management, training, subcontracts, and general administration.

  • +
  • marine_management (float) – Coordination of port equipment, vessels, and personnel to carry out inspections +and maintenance of generation and transmission equipment.

  • +
  • weather_forecasting (float) – Daily forecast of metocean conditions used to plan maintenance visits and +estimate project power production.

  • +
  • condition_monitoring (float) – Monitoring of SCADA data from wind turbine components to optimize performance +and identify component faults.

  • +
  • operating_facilities (float) – Co-located offices, parts store, quayside facilities, helipad, refueling +facilities, hanger (if necesssary), etc.

  • +
  • environmental_health_safety_monitoring (float) – Coordination and monitoring to ensure compliance with HSE requirements during +operations.

  • +
  • insurance (float) – Insurance policies during operational period including All Risk Property, +Buisness Interuption, Third Party Liability, and Brokers Fee, and Storm +Coverage.

    +
    +

    Note

    +

    This should only be used when not breaking down the cost into the +following categories: brokers_fee, operations_all_risk, +business_interruption, third_party_liability, and/or +storm_coverage

    +
    +
  • +
  • brokers_fee (float) – Fees for arranging the insurance package.

  • +
  • operations_all_risk (float) – All Risk Property (physical damage). Sudden and unforseen physical loss or +physical damage to teh plant/assets during the operational phase of a project.

  • +
  • business_interruption (float) – Sudden and unforseen loss or physical damage to the plant/assets during the +operational phase of a project causing an interruption.

  • +
  • third_party_liability (float) – Liability imposed by law, and/or Express Contractual Liability, for bodily +injury or property damage.

  • +
  • storm_coverage (float) – Coverage from huricane and tropical storm events (tyipcally for Atlantic Coast +projects).

  • +
  • annual_leases_fees (float) – Ongoing payments, including but not limited to: payments to regulatory body for +permission to operate at project site (terms defined within lease); payments to +Transmission Systems Operators or Transmission Asseet Owners for rights to +transport generated power.

    +
    +

    Note

    +

    This should only be used when not breaking down the cost into the +following categories: submerge_land_lease_costs and/or +transmission_charges_rights

    +
    +
  • +
  • submerge_land_lease_costs (float) – Payments to submerged land owners for rights to build project during operations.

  • +
  • transmission_charges_rights (float) – Any payments to Transmissions Systems Operators or Transmission Asset Owners for +rights to transport generated power.

  • +
  • onshore_electrical_maintenance (float) – Inspections of cables, transformer, switch gears, power compensation equipment, +etc. and infrequent repairs

    +
    +

    Warning

    +

    This should only be used if not modeling these as processes within +the model. Currently, onshore modeling is not included.

    +
    +
  • +
  • labor (float) – The costs associated with labor, if not being modeled through the simulated +processes.

  • +
+
+
+
+ +
+
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/API/utilities.html b/API/utilities.html new file mode 100644 index 00000000..00c5cd3a --- /dev/null +++ b/API/utilities.html @@ -0,0 +1,830 @@ + + + + + + + + + Helpers and Plotting — WOMBAT v0.9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + + + +
+
+ + + + + + + +
+
+ + + +
+
+
+ +
+

Helpers and Plotting

+ +
+ +
+
+
+ +
+ +
+

Helpers and Plotting#

+
+

Plotting#

+

Provides expoerimental plotting routines to help with simulation diagnostics.

+
+
+wombat.utilities.plot.plot_farm_availability(sim: wombat.core.simulation_api.Simulation, which: str = 'energy', individual_turbines: bool = False, farm_95_CI: bool = False, figure_kwargs: Optional[dict] = None, plot_kwargs: Optional[dict] = None, legend_kwargs: Optional[dict] = None, tick_fontsize: int = 12, label_fontsize: int = 16, return_fig: bool = False) tuple[matplotlib.figure.Figure | matplotlib.axes._axes.Axes] | None#
+

Plots a line chart of the monthly availability at the wind farm level.

+
+
Parameters
+
    +
  • sim (Simulation) – A Simulation object that has been run.

  • +
  • which (str) – One of “time” or “energy”, to indicate the basis for the availability +calculation, by default “energy”.

  • +
  • individual_turbines (bool, optional) – Indicates if faint gray lines should be added in the background for the +availability of each turbine, by default False.

  • +
  • farm_95_CI (bool, optional) – Indicates if the 95% CI area fill should be added in the background.

  • +
  • figure_kwargs (dict, optional) – Custom parameters for plt.figure(), by default figsize=(15, 7) and +dpi=300.

  • +
  • plot_kwargs (dict, optional) – Custom parameters to be passed to ax.plot(), by default a label consisting +of the simulation name and project-level availability.

  • +
  • legend_kwargs (dict, optional) – Custom parameters to be passed to ax.legend(), by default fontsize=14.

  • +
  • tick_fontsize (int, optional) – The x- and y-axis tick label fontsize, by default 12.

  • +
  • label_fontsize (int, optional) – The x- and y-axis label fontsize, by default 16.

  • +
  • return_fig (bool, optional) – If True, return the figure and Axes object, otherwise don’t, by default +False.

  • +
+
+
Returns
+

See return_fig for details. +_description_

+
+
Return type
+

tuple[plt.Figure, plt.Axes] | None

+
+
+
+ +
+
+wombat.utilities.plot.plot_farm_layout(windfarm: wombat.windfarm.windfarm.Windfarm, figure_kwargs: Optional[dict] = None, plot_kwargs: Optional[dict] = None, return_fig: bool = False) None | tuple[matplotlib.pyplot.figure, matplotlib.pyplot.axes]#
+

Plot the graph representation of the windfarm as represented through WOMBAT.

+
+
Args:
+
figure_kwargsdict, optional

Customized keyword arguments for matplotlib figure instantiation that +will passed as plt.figure(**figure_kwargs). Defaults to {}.

+
+
plot_kwargsdict, optional

Customized parameters for networkx.draw() that can will passed as +nx.draw(**figure_kwargs). Defaults to with_labels=True, +horizontalalignment=right, verticalalignment=bottom, +font_weight=bold, font_size=10, and node_color=#E37225.

+
+
return_figbool, optional

Whether or not to return the figure and axes objects for further editing +and/or saving. Defaults to False.

+
+
+
+
+
+
Returns
+

None | tuple[plt.figure, plt.axes]

+
+
Return type
+

_description_

+
+
+
+ +
+
+wombat.utilities.plot.plot_operational_levels(sim: wombat.core.simulation_api.Simulation, figure_kwargs: Optional[dict] = None, cbar_label_fontsize: int = 14, return_fig: bool = False)#
+

Plots an hourly view of the operational levels of the wind farm and individual +turbines as a heatmap.

+
+
Parameters
+
    +
  • sim (Simulation) – A Simulation object that has been run.

  • +
  • figure_kwargs (dict, optional) – Custom settings for plt.figure(), by default figsize=(15, 10) +and dpi=300.

  • +
  • cbar_label_fontsize (int, optional) – The default fontsize used in the color bar legend for the axis label, by default +14.

  • +
  • return_fig (bool, optional) – If True, return the figure and Axes object, otherwise don’t, by default +False.

  • +
+
+
Returns
+

See return_fig for details.

+
+
Return type
+

tuple[plt.Figure, plt.Axes] | None

+
+
+
+ +
+
+

Logging functions#

+

General logging methods.

+
+
+wombat.utilities.logging.format_events_log_message(simulation_time: datetime.datetime, env_time: float, system_id: str, system_name: str, part_id: str, part_name: str, system_ol: float | str, part_ol: float | str, agent: str, action: str, reason: str, additional: str, duration: float, request_id: str, location: str = 'na', materials_cost: int | float = 0, hourly_labor_cost: int | float = 0, salary_labor_cost: int | float = 0, equipment_cost: int | float = 0) str#
+

Formats the logging messages into the expected format for logging.

+
+
Parameters
+
    +
  • simulation_time (datetime64) – Timestamp within the simulation time.

  • +
  • env_time (float) – Environment simulation time (Environment.now).

  • +
  • system_id (str) – Turbine ID, System.id.

  • +
  • system_name (str) – Turbine name, System.name.

  • +
  • part_id (str) – Subassembly, component, or cable ID, _.id.

  • +
  • part_name (str) – Subassembly, component, or cable name, _.name.

  • +
  • system_ol (int | float) – System operating level, System.operating_level. Use an empty string for n/a.

  • +
  • part_ol (int | float) – Subassembly, component, or cable operating level, _.operating_level. Use an +empty string for n/a.

  • +
  • agent (str) – Agent performin the action.

  • +
  • action (str) – Action that was taken.

  • +
  • reason (str) – Reason an action was taken.

  • +
  • additional (str) – Any additional information that needs to be logged.

  • +
  • duration (float) – Length of time the action lasted.

  • +
  • request_id (str) – The RepairRequest.request_id or “na”.

  • +
  • location (str) – The location of where the event ocurred: should be one of site, port, +enroute, or system, by default “na”.

  • +
  • materials_cost (int | float, optional) – Total cost of materials for action, in USD, by default 0.

  • +
  • hourly_labor_cost (int | float, optional) – Total cost of hourly labor for action, in USD, by default 0.

  • +
  • salary_labor_cost (int | float, optional) – Total cost of salaried labor for action, in USD, by default 0.

  • +
  • equipment_cost (int | float, optional) – Total cost of equipment for action, in USD, by default 0.

  • +
+
+
Returns
+

Formatted message for consistent logging.[summary]

+
+
Return type
+

str

+
+
+
+ +
+
+wombat.utilities.logging.setup_logger(logger_name: str, log_file: pathlib.Path, level: Any = 20, capacity: int = 500) None#
+

Creates the logging infrastructure for a given logging category.

+

TODO: Figure out how to type check logging.INFO; Callable?

+
+
Parameters
+
    +
  • logger_name (str) – Name to assign to the logger.

  • +
  • log_file (Path) – File name and path for where the log data should be saved.

  • +
  • level (Any, optional) – Logging level, by default logging.INFO.

  • +
+
+
+
+ +
+
+

Time Calculations#

+

General methods for time-based calculations.

+
+
+wombat.utilities.time.calculate_cost(duration: int | float, rate: float, n_rate: int = 1, daily_rate: bool = False) float#
+

Calculates the equipment cost, or labor cost for either salaried or hourly +employees.

+
+
Parameters
+
    +
  • duration (int | float) – Length of time, in hours.

  • +
  • rate (float) – The labor or equipment rate, in $USD/hour.

  • +
  • n_rate (int) – Total number of of the rate to be applied, more than one if``rate`` is broken +down by number of individual laborers (if rate is a labor rate), by default 1.

  • +
  • daily_rate (bool, optional) – Indicator for if the rate is a daily rate (True), or hourly rate (False), by +default False.

  • +
+
+
Returns
+

The total cost of the labor performed.

+
+
Return type
+

float

+
+
+
+ +
+
+wombat.utilities.time.check_working_hours(env_start: int, env_end: int, workday_start: int, workday_end: int) tuple[int, int]#
+

Checks the working hours of a port or servicing equipment, and overrides a +default (-1) to the environment’s settings, otherwise returns back the input hours.

+
+
Parameters
+
    +
  • env_start (int) – The starting hour for the environment’s shift

  • +
  • env_end (int) – The ending hour for the environment’s shift

  • +
  • workday_start (int) – The starting hour to be checked.

  • +
  • workday_end (int) – The ending hour to be checked.

  • +
+
+
Returns
+

The starting and ending hour to be applied back to the port or servicing +equipment.

+
+
Return type
+

tuple[int, int]

+
+
+
+ +
+
+wombat.utilities.time.convert_dt_to_hours(diff: datetime.timedelta) float#
+

Convert a datetime.timedelta object to number of hours at the seconds +resolution.

+
+
Parameters
+

diff (datetime.timedelta) – The difference between two datetime.datetime objects.

+
+
Returns
+

Number of hours between to datetime.datetime objects.

+
+
Return type
+

float

+
+
+
+ +
+
+wombat.utilities.time.hours_until_future_hour(dt: datetime.datetime, hour: int) float#
+

Number of hours until a future hour in the same day for hour <= 24, +otherwise, it is the number of hours until a time in the proceeding days.

+
+
Parameters
+
    +
  • dt (datetime.datetime) – Focal datetime.

  • +
  • hour (int) – Hour that is later in the day, in 24 hour time.

  • +
+
+
Returns
+

Number of hours between the two times.

+
+
Return type
+

float

+
+
+
+ +
+
+wombat.utilities.time.parse_date(value: str | None | datetime.datetime) datetime.datetime | None#
+

Thin wrapper for dateutil.parser.parse that converts string dates and returns +back None or the original value if it’s None or a datetime.datetime object, +respectively.

+
+
Parameters
+

value (str | None | datetime.datetime) – A month/date or month-date formatted string to be converted to a +datetime.datetime object, or datetime.datetime object, or None.

+
+
Returns
+

A converted datetime.datetime object or None.

+
+
Return type
+

datetime.datetime | None

+
+
+
+ +
+
+

Miscellaneous#

+

Provides various utility functions that don’t fit within a common theme.

+
+
+wombat.utilities.utilities.IEC_power_curve(windspeed_column: numpy.ndarray | pandas.core.series.Series, power_column: numpy.ndarray | pandas.core.series.Series, bin_width: float = 0.5, windspeed_start: float = 0.0, windspeed_end: float = 30.0) Callable#
+

Direct copyfrom OpenOA: +https://github.com/NREL/OpenOA/blob/main/operational_analysis/toolkits/power_curve/functions.py#L16-L57 +Use IEC 61400-12-1-2 method for creating wind-speed binned power curve.

+
+
Parameters
+
    +
  • windspeed_column (np.ndarray | pandas.Series) – The power curve’s windspeed values, in m/s.

  • +
  • power_column (np.ndarray | pandas.Series) – The power curve’s output power values, in kW.

  • +
  • bin_width (float) – Width of windspeed bin, default is 0.5 m/s according to standard, by default +0.5.

  • +
  • windspeed_start (float) – Left edge of first windspeed bin, where all proceeding values will be 0.0, +by default 0.0.

  • +
  • windspeed_end (float) – Right edge of last windspeed bin, where all following values will be 0.0, by +default 30.0.

  • +
+
+
Returns
+

Python function of the power curve, of type (Array[float] -> Array[float]), +that maps input windspeed value(s) to ouptut power value(s).

+
+
Return type
+

Callable

+
+
+
+ +
+
+wombat.utilities.utilities.create_variable_from_string(string: str) str#
+

Creates a valid Python variable style string from a passed string.

+
+
Parameters
+

string (str) – The string to convert into a Python-friendly variable name.

+
+
Returns
+

A Python-valid variable name.

+
+
Return type
+

str

+
+
+

Examples

+
>>> example_string = "*Electrical!*_ _System$*_"
+>>> print(create_variable_from_string(example_string))
+'electrical_system'
+
+
+
+ +
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/API/windfarm.html b/API/windfarm.html new file mode 100644 index 00000000..ee65ced4 --- /dev/null +++ b/API/windfarm.html @@ -0,0 +1,888 @@ + + + + + + + + + Wind Farm Classes — WOMBAT v0.9 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + +
+
+ + + + + + + + + + +
+ +
+ +
+ + + + +
+
+ + + + +
+
+ + + + + + + +
+
+ + + +
+
+
+ + +
+ +
+ +
+

Wind Farm Classes#

+

The wind farm classes define how the wind farm’s graph model are created, and power the +timing out of the failures and maintenance events within the simulation for the cables, +substations, and turbines.

+
+

Windfarm#

+

Creates the Windfarm class/model.

+
+
+class wombat.windfarm.windfarm.Windfarm(env: wombat.core.environment.WombatEnvironment, windfarm_layout: str, repair_manager: wombat.core.repair_management.RepairManager)#
+

The primary class for operating on objects within a windfarm. The substations, +cables, and turbines are created as a network object to be more appropriately +accessed and controlled.

+
+
+cable(cable_id: tuple[str, str] | str) wombat.windfarm.system.cable.Cable#
+

Convenience function to returns the desired Cable object for a cable in the +windfarm.

+
+
Parameters
+

cable_id (tuple[str, str] | str) – The cable’s unique identifier, of the form: (wombat.windfarm.System.id, +wombat.windfarm.System.id), for the (downstream node id, upstream node +id), or the Cable.id.

+
+
Returns
+

The Cable object.

+
+
Return type
+

Cable

+
+
+
+ +
+
+calculate_distance_matrix() None#
+

Calculates the geodesic distance, in km, between all of the windfarm’s nodes, +e.g., substations and turbines, and cables.

+
+ +
+
+property current_availability: float#
+

Calculates the product of all system operating_level variables across +the windfarm using the following forumation.

+
+\[\sum{ + OperatingLevel_{substation_{i}} * + \sum{OperatingLevel_{turbine_{j}} * Weight_{turbine_{j}}} +}\]
+

where the :math:{OperatingLevel} is the product of the operating level +of each subassembly on a given system (substation or turbine), and the +:math:{Weight} is the proportion of one turbine’s capacity relative to +the whole windfarm.

+
+ +
+
+property current_availability_wo_servicing: float#
+

Calculates the product of all system operating_level variables across +the windfarm using the following forumation, ignoring 0 operating level due to +ongoing servicing.

+
+\[\sum{ + OperatingLevel_{substation_{i}} * + \sum{OperatingLevel_{turbine_{j}} * Weight_{turbine_{j}}} +}\]
+

where the :math:{OperatingLevel} is the product of the operating level +of each subassembly on a given system (substation or turbine), and the +:math:{Weight} is the proportion of one turbine’s capacity relative to +the whole windfarm.

+
+ +
+
+finish_setup() None#
+

Final initialization hook for any substations, turbines, or cables.

+
+ +
+
+system(system_id: str) wombat.windfarm.system.system.System#
+

Convenience function to returns the desired System object for a turbine or +substation in the windfarm.

+
+
Parameters
+

system_id (str) – The system’s unique identifier, wombat.windfarm.System.id.

+
+
Returns
+

The System object.

+
+
Return type
+

System

+
+
+
+ +
+ +
+
+

System: Wind turbine or substation#

+

Creates the Turbine class.

+
+
+class wombat.windfarm.system.system.System(env: wombat.core.environment.WombatEnvironment, repair_manager: wombat.core.repair_management.RepairManager, t_id: str, name: str, subassemblies: dict, system: str)#
+

Can either be a turbine or substation, but is meant to be something that consists +of ‘Subassembly’ pieces.

+

See here +for more information.

+
+
+interrupt_all_subassembly_processes(origin: Optional[wombat.windfarm.system.subassembly.Subassembly] = None) None#
+

Interrupts the running processes in all of the system’s subassemblies.

+
+
Parameters
+

origin (Subassembly) – The subassembly that triggered the request, if the method call is coming +from a subassembly shutdown event.

+
+
+
+ +
+
+property operating_level: float#
+

The turbine’s operating level, based on subassembly and cable performance.

+
+
Returns
+

Operating level of the turbine.

+
+
Return type
+

float

+
+
+
+ +
+
+property operating_level_wo_servicing: float#
+

The turbine’s operating level, based on subassembly and cable performance, +without accounting for servicing status.

+
+
Returns
+

Operating level of the turbine.

+
+
Return type
+

float

+
+
+
+ +
+
+power(windspeed: list[float] | numpy.ndarray) numpy.ndarray#
+

Generates the power output for an iterable of windspeed values.

+
+
Parameters
+

windspeed (list[float] | np.ndarrays) – Windspeed values, in m/s.

+
+
Returns
+

Power production, in kW.

+
+
Return type
+

np.ndarray

+
+
+
+ +
+ +
+
+

Subassembly: The modeled componenents of a system#

+

Provides the Subassembly class.

+
+
+class wombat.windfarm.system.subassembly.Subassembly(system, env: wombat.core.environment.WombatEnvironment, s_id: str, subassembly_data: dict)#
+

A major system composes the turbine or substation objects.

+
+
+interrupt_all_subassembly_processes() None#
+

Thin wrapper for system.interrupt_all_subassembly_processes.

+
+ +
+
+interrupt_processes(origin: Optional[wombat.windfarm.system.subassembly.Subassembly] = None) None#
+

Interrupts all of the running processes within the subassembly except for the +process associated with failure that triggers the catastrophic failure.

+
+
Parameters
+

origin (Subassembly) – The subassembly that triggered the request, if the method call is coming +from a subassembly shutdown event. If provided, and it is the same as the +current subassembly, then a try/except flow is used to ensure the process +that initiated the shutdown is not interrupting itself.

+
+
+
+ +
+
+recreate_processes() None#
+

If a turbine is being reset after a tow-to-port repair or replacement, then +all processes are assumed to be reset to 0, and not pick back up where they left +off.

+
+ +
+
+run_single_failure(failure: wombat.core.data_classes.Failure) collections.abc.Generator#
+

Runs a process to trigger one type of failure repair request throughout the +simulation.

+
+
Parameters
+

failure (Failure) – A failure classification.

+
+
Yields
+

simpy.events. HOURS_IN_DAY – Time between failure events that need to request a repair.

+
+
+
+ +
+
+run_single_maintenance(maintenance: wombat.core.data_classes.Maintenance) collections.abc.Generator#
+

Runs a process to trigger one type of maintenance request throughout the +simulation.

+
+
Parameters
+

maintenance (Maintenance) – A maintenance category.

+
+
Yields
+

simpy.events. HOURS_IN_DAY – Time between maintenance requests.

+
+
+
+ +
+
+trigger_request(action: wombat.core.data_classes.Maintenance | wombat.core.data_classes.Failure)#
+

Triggers the actual repair or maintenance logic for a failure or maintenance +event, respectively.

+
+
Parameters
+

action (Maintenance | Failure) – The maintenance or failure event that triggers a RepairRequest.

+
+
+
+ +
+ +
+
+

Cable: Hybrid system and subassembly model#

+

“Defines the Cable class and cable simulations.

+
+
+class wombat.windfarm.system.cable.Cable(windfarm, env: wombat.core.environment.WombatEnvironment, connection_type: str, start_node: str, end_node: str, cable_data: dict, name: Optional[str] = None)#
+

The cable system/asset class.

+
+
Parameters
+
    +
  • windfarm (wombat.windfarm.Windfarm) – The Windfarm object.

  • +
  • env (WombatEnvironment) – The simulation environment.

  • +
  • cable_id (str) – The unique identifier for the cable.

  • +
  • connection_type (str) – The type of cable. Must be one of “array” or “export”.

  • +
  • start_node (str) – The starting point (system.id) (turbine or substation) of the cable segment.

  • +
  • cable_data (dict) – The dictionary defining the cable segment.

  • +
+
+
+
+
+finish_setup() None#
+

Creates the upstream_nodes and upstream_cables attributes for use by +the cable when triggering usptream failures and resetting them after the repair +is complete.

+
+ +
+
+interrupt_all_subassembly_processes() None#
+

Thin wrapper for interrupt_processes for consistent usage with system.

+
+ +
+
+interrupt_processes() None#
+

Interrupts all of the running processes within the subassembly except for the +process associated with failure that triggers the catastrophic failure.

+
+
Parameters
+

subassembly (Subassembly) – The subassembly that should have all processes interrupted.

+
+
+
+ +
+
+recreate_processes() None#
+

If a cable is being reset after a replacement, then all processes are +assumed to be reset to 0, and not pick back up where they left off.

+
+ +
+
+run_single_failure(failure: wombat.core.data_classes.Failure) collections.abc.Generator#
+

Runs a process to trigger one type of failure repair request throughout the +simulation.

+
+
Parameters
+

failure (Failure) – A failure classification.

+
+
Yields
+

simpy.events.Timeout – Time between failure events that need to request a repair.

+
+
+
+ +
+
+run_single_maintenance(maintenance: wombat.core.data_classes.Maintenance) collections.abc.Generator#
+

Runs a process to trigger one type of maintenance request throughout the +simulation.

+
+
Parameters
+

maintenance (Maintenance) – A maintenance category.

+
+
Yields
+

simpy.events.Timeout – Time between maintenance requests.

+
+
+
+ +
+
+set_string_details(start_node: str, substation: str)#
+

Sets the starting turbine for the string to be used for traversing the +correct upstream connections when resetting after a failure.

+
+
Parameters
+
    +
  • start_node (str) – The System.id for the starting turbine on a string.

  • +
  • substation (str) – The System.id for the string’s connecting substation.

  • +
+
+
+
+ +
+
+stop_all_upstream_processes(failure: wombat.core.data_classes.Failure | wombat.core.data_classes.Maintenance) None#
+

Stops all upstream turbines and cables from producing power by creating a +env.event() for each System.cable_failure and +Cable.downstream_failure, respectively. In the case of an export cable, each +string is traversed to stop the substation and upstream turbines and cables.

+
+
Parameters
+

failure (Failre) – The Failure that is causing a string shutdown.

+
+
+
+ +
+
+trigger_request(action: wombat.core.data_classes.Maintenance | wombat.core.data_classes.Failure)#
+

Triggers the actual repair or maintenance logic for a failure or maintenance +event, respectively.

+
+
Parameters
+

action (Maintenance | Failure) – The maintenance or failure event that triggers a RepairRequest.

+
+
+
+ +
+ +
+
+ + +
+ +
+ +
+
+ + +
+ + +
+
+ + + + + + + \ No newline at end of file diff --git a/_downloads/4cf89f146ec897dc843df0212e9fd862/operations_and_maintenance_model_FY20.pdf b/_downloads/4cf89f146ec897dc843df0212e9fd862/operations_and_maintenance_model_FY20.pdf new file mode 100644 index 00000000..0d411a2d Binary files /dev/null and b/_downloads/4cf89f146ec897dc843df0212e9fd862/operations_and_maintenance_model_FY20.pdf differ diff --git a/_downloads/82dc1ac1d7973496c3a853395d9ba8ba/code_comparison.pdf b/_downloads/82dc1ac1d7973496c3a853395d9ba8ba/code_comparison.pdf new file mode 100644 index 00000000..491ce19c Binary files /dev/null and b/_downloads/82dc1ac1d7973496c3a853395d9ba8ba/code_comparison.pdf differ diff --git a/_downloads/b58402184736ea5efcd8b535eb737d2c/WOMBAT_IEA_task_26_presentation_6_May_2021.pdf b/_downloads/b58402184736ea5efcd8b535eb737d2c/WOMBAT_IEA_task_26_presentation_6_May_2021.pdf new file mode 100644 index 00000000..154cee48 Binary files /dev/null and b/_downloads/b58402184736ea5efcd8b535eb737d2c/WOMBAT_IEA_task_26_presentation_6_May_2021.pdf differ diff --git a/_images/class_diagram.svg b/_images/class_diagram.svg new file mode 100644 index 00000000..62602233 --- /dev/null +++ b/_images/class_diagram.svg @@ -0,0 +1 @@ +FromDictMixinfrom_dict(cls, data)Failurelevel: intscale: floatshape: floattime: floatmaterials: floatdescription: stroperation_reduction: floatservice_equipment: list[str]system_value: floatweibullrequest_id: strassign_id(request_id: str)hours_to_next_failure() : floatMaintenancetime: floatmaterials: floatdescription: stroperation_reduction: floatservice_equipment: list[str]system_value: floatrequest_id: strassign_id(request_id: str)RepairRequestcable: booldetails: Failure | Maintenancerequest_id: strseverity_level: intsubassembly_id: strsubassembly_name: strsystem_id: strsystem_name: strupstream_turbines: list[str]assign_id(request_id: str)ServiceEquipmentDatadata_dict: dictstrategy: strdetermine_type()ServiceCrewday_rate: floathourly_rate: floatn_day_rate: intn_hourly_rate: intScheduledServiceEquipmentDataname: strcapability: list[str]equipment_rate: floatcrew: ServiceCrewn_crews: intcrew_transfer_time: floatstart_day: intstart_month: intend_day: intend_month: intstart_year: intend_year: intstrategy: stronsite: boolworkday_start: intworkday_end: intmethod: strmobilization_cost: floatmobilization_days: intspeed: floatmax_waveheight_repair: floatmax_waveheight_transport: floatmax_windspeed_repair: floatmax_windspeed_transport: floatoperating_dates: numpy.ndarraycreate_date_range()UnscheduledServiceEquipmentDataname: strcapability: list[str]equipment_rate: floatcrew: ServiceCrewn_crews: intcrew_transfer_time: floatcharter_days: intstrategy: strstrategy_threshold: int | floatworkday_start: intworkday_end: intmethod: strmobilization_cost: floatmobilization_days: intspeed: floattow_sped: floatspeed_reduction_factor: floatmax_waveheight_repair: floatmax_waveheight_transport: floatmax_windspeed_repair: floatmax_windspeed_transport: floatport_distance: floatunmoor_hours: floatreconnection_hours: floatonsite: boolPortConfigname: strtugboats: list[UnscheduledServiceEquipmentData]crew: ServiceCrewn_crews: intmax_operations: intworkday_start: intworkday_end: intsite_distance: floatannual_fee: floatSubassemblyDataname: strsystem_value: floatfailures: dict[int, Failure]maintenance: list[Maintenance]FixedCostsoperations: floatannual_lease_fees: floatbrokers_fee: floatbusiness_interruption: floatcondition_monitoring: floatenvironmental_health_safety_monitoring: floatinsurance: floatlabor: floatmarine_management: floatonshore_electrical_maintenance: floatoperating_facilities: floatoperations_all_risk: floatoperations_management_administration: floatproject_management_administration: floatstorm_coverage: floatsubmerge_land_lease_costs: floatthird_party_liability: floattransmission_charges_rights: floatweather_forecasting: floathierarchy: dictresolution: dictcost_category_validator(name: str, sub_name: list[str])Portenv: WombatEnvironmentmanager: RepairManagerwindfarm: Windfarmsettings: PortConfigturbine_manager: simpy.Resourcecrew_manager: simpy.Resourcetugboat_manager: simpy.Resourceactive_repairs: dict[str, dict[str, simpy.events.Event]]wait_until_next_shift()proces_repair(request: RepairRequest, time_processed: float, prior_operational_level: float)repair_single(request: RepairRequest)transfer_requests_from_manager(system_id: str)run_repairs(system_id: str)run_tow_to_port(request: RepairRequest)run_unscheduled_in_situ(request: RepairRequest)ServiceEquipmentenv: WombatEnvironmentmanager: RepairManagerwindfarm: Windfarmsettings: ScheduledServiceEquipmentData | UnscheduledServiceEquipmentDataat_port: boolat_system: boolonsite: boolenroute: booltransferring_crew: boolcurrent_system: boolcalculate_equipment_cost(duration: float)calculate_hourly_cost(duration: float)calculate_salary_cost(duration: float)find_interrupted_weather_window(hours_required: float)find_uninterrupted_weather_window(hours_required: float)run_scheduled()run_unscheduled()mobilize()mobilize_scheduled()weather_delay(hours: float)wait_until_next_shift()wait_until_next_operational_period()proces_repair(request: RepairRequest, time_processed: float, prior_operational_level: float)crew_transfer(system: System, subassembly: Subassembly, requests: RepairRequest, to_system: bool)repair(hours: float, request_details: Failure | Maintenance)travel(start: str, end: str, set_current: str)Windfarmenv: WombatEnvironmentwindfarm_layout: strrepair_manager: RepairManagercapacity: floatgraph: networkx.DiGraphsystem_list: list[str]substation_id: list[str]turbine_id: list[str]distance_matrix: numpy.ndarraycurrent_availability: floatcurrent_availability_wo_servicing: floatcalculate_distance_matrix()_log_operations()system(system_id: str) : Systemcable(cable_id: str | tuple) : CableSystemenv: WombatEnvironmentid: strname: stroperating_level: floatoperating_level_wo_servicing: floatpower_curve: Callablerepair_manager: RepairManagerservicing: boolvalue: floatcable_failure: boolcapacity: floatsubassemblies: list[Subassembly]electrical_system: Subasemblyelectronic_control: Subasemblygearbox: Subasemblygenerator: Subasemblyhydraulic_system: Subasemblymechanical_brake: Subasemblyrotor_blades: Subasemblyrotor_hub: Subasemblysensors: Subasemblysupporting_structure: Subasemblyyaw_system: Subasemblytransformer: Subasemblypower(windspeed: float | numpy.ndaray)interrupt_all_subassembly_processes()Subassemblyid: strname: strenv: WombatEnvironmentdata: SubassemblyDatabroken: booloperating_level: floatturbine: Systemprocesses: dictrun_single_failure(failure: Failure)run_single_maintenance(maintenance: Maintenance)interrupt_processes()interrupt_all_subassembly_processes()Cableid: strname: strenv: WombatEnvironmentdata: SubassemblyDatabroken: boolservicing: booldownstream_failure: boolprocesses: dictoperating_level: floatoperating_level_wo_servicing: floatrepair_manager: RepairManagerservicing: boolvalue: floatcable_failure: boolcapacity: floatrun_single_failure(failure: Failure)run_single_maintenance(maintenance: Maintenance)interrupt_processes()interrupt_all_subassembly_processes()stop_all_upstream_processes()RepairManagerenv: WombatEnvironmentwindfarm: Windfarmdowntime_based_equipment: StrategyMaprequest_based_equipment: StrategyMaprequest_map: dict[str, int]submit_request(request: RepairRequest) : RepairREquestpurge_subassembly_requests(system_id: str, subassembly_id: str, exclude: list[str])get_next_highest_severity_request(equipment_capability: list[str], severity_level: int)get_request_by_system(equipment_capability: list[str], system_id: str)StrategyMapCTV: list[EquipmentMap]LCN: list[EquipmentMap]SCN: list[EquipmentMap]CAB: list[EquipmentMap]DSV: list[EquipmentMap]DRN: list[EquipmentMap]RMT: list[EquipmentMap]is_running: boolupdate(capability: str, threshold: float | int, equipment: ServiceEquipment)EquipmentMapequipment_map: ServiceEquipmentstrategy_threshold: int | floatWombatEnvironmentdata_dir: pathlib.Pathweather_file: strworkday_end: intworkday_end: intsimulation_name: strstart_year: intend_year: intweather: pandas.DataFramemax_run_time: intshift_length: intweather_now: tuple[float, float]simulation_time: datetime.datetimecurrent_time: datetime.datetimeevents_log_fname: stroperations_log_fname: strpower_potential_fname: strpower_production_fname: strrun(until: int | float)hour_in_shift(hour: int, workday_start: int, workday_end: int) : boolhours_to_next_shift(workday_start: int) : floatdate_ix(date: datetime.datetime) int)weather_forecast(hours: int | float)convert_logs_to_csv(delete_original: bool, return_df: True)power_production_potential_to_csv(windfarm: Windfarm, operations: pandas.DataFrame, return_df: bool)cleanup_log_files(log_only: bool)Simulationenv: WombatEnvironmentlibrary_path: pathlib.Pathconfig: dict | Configurationwindfarm: Windfarmrepair_manager: RepairManagerservice_equipment: list[ServiceEquipment]metrics: Metricsfrom_config(cls, config: pathlib.Path | dict)run(until: int, create_metrics: bool, save_metrics_inputs: bool)save_metrics_inputs()Configurationname: strlibrary_path: pathlib.Pathlayout: strservice_equipment: str | list[str]weather: str | pandas.DataFrameworkday_start: intworkday_end: intinflation_rate: floatfixed_costs: strproject_capacity: floatstart_year: intend_year: intSAM_settings: strMetricsdata_dir: pathlib.Pathevents: str | pandas.DataFrameoperations: str | pandas.DataFramepotential: str | pandas.DataFrameproduction: str | pandas.DataFrameinflation_rate: floatproject_capacity: floatturbine_capacities: list[float]substation_id: list[str]turbine_id: list[str]service_equipment_names: list[str]fixed_costs: strSAM_settings: strfrom_simulation_outputs(cls, fpath: Path | str, fname: str)time_based_availability(frequency: str, by: str)production_based_availability(frequency: str, by: str)capacity_factor(which: str, frequency: str, by: str)task_completion_rate(which: str, frequency: str)equipment_costs(frequency: str, by_equipment: bool)service_equipment_utilization(frequency: str)labor_costs(frequency: str, by_type: bool)equipment_labor_cost_breakdowns(frequency: str, by_category: bool)component_costs(frequency: str, by_category: bool, by_action: bool)project_fixed_costs(frequency: str, resolution: str)process_times()power_production(frequency: str, by_turbine: bool)pysam_npv()pysam_lcoe_real()pysam_lcoe_nominal()pysam_irr()pysam_all_outputs() diff --git a/_images/data_classes.svg b/_images/data_classes.svg new file mode 100644 index 00000000..417c1fb6 --- /dev/null +++ b/_images/data_classes.svg @@ -0,0 +1 @@ +FromDictMixinfrom_dict(cls, data)Failurelevel: intscale: floatshape: floattime: floatmaterials: floatdescription: stroperation_reduction: floatservice_equipment: list[str]system_value: floatweibullrequest_id: strassign_id(request_id: str)hours_to_next_failure() : floatMaintenancetime: floatmaterials: floatdescription: stroperation_reduction: floatservice_equipment: list[str]system_value: floatrequest_id: strassign_id(request_id: str)RepairRequestcable: booldetails: Failure | Maintenancerequest_id: strseverity_level: intsubassembly_id: strsubassembly_name: strsystem_id: strsystem_name: strupstream_turbines: list[str]assign_id(request_id: str)ServiceEquipmentDatadata_dict: dictstrategy: strdetermine_type()ServiceCrewday_rate: floathourly_rate: floatn_day_rate: intn_hourly_rate: intScheduledServiceEquipmentDataname: strcapability: list[str]equipment_rate: floatcrew: ServiceCrewn_crews: intcrew_transfer_time: floatstart_day: intstart_month: intend_day: intend_month: intstart_year: intend_year: intstrategy: stronsite: boolworkday_start: intworkday_end: intmethod: strmobilization_cost: floatmobilization_days: intspeed: floatmax_waveheight_repair: floatmax_waveheight_transport: floatmax_windspeed_repair: floatmax_windspeed_transport: floatoperating_dates: numpy.ndarraycreate_date_range()UnscheduledServiceEquipmentDataname: strcapability: list[str]equipment_rate: floatcrew: ServiceCrewn_crews: intcrew_transfer_time: floatcharter_days: intstrategy: strstrategy_threshold: int | floatworkday_start: intworkday_end: intmethod: strmobilization_cost: floatmobilization_days: intspeed: floattow_sped: floatspeed_reduction_factor: floatmax_waveheight_repair: floatmax_waveheight_transport: floatmax_windspeed_repair: floatmax_windspeed_transport: floatport_distance: floatunmoor_hours: floatreconnection_hours: floatonsite: boolPortConfigname: strtugboats: list[UnscheduledServiceEquipmentData]crew: ServiceCrewn_crews: intmax_operations: intworkday_start: intworkday_end: intsite_distance: floatannual_fee: floatSubassemblyDataname: strsystem_value: floatfailures: dict[int, Failure]maintenance: list[Maintenance]FixedCostsoperations: floatannual_lease_fees: floatbrokers_fee: floatbusiness_interruption: floatcondition_monitoring: floatenvironmental_health_safety_monitoring: floatinsurance: floatlabor: floatmarine_management: floatonshore_electrical_maintenance: floatoperating_facilities: floatoperations_all_risk: floatoperations_management_administration: floatproject_management_administration: floatstorm_coverage: floatsubmerge_land_lease_costs: floatthird_party_liability: floattransmission_charges_rights: floatweather_forecasting: floathierarchy: dictresolution: dictcost_category_validator(name: str, sub_name: list[str])ServiceEquipmentenv: WombatEnvironmentmanager: RepairManagerwindfarm: Windfarmsettings: ScheduledServiceEquipmentData | UnscheduledServiceEquipmentDataat_port: boolat_system: boolonsite: boolenroute: booltransferring_crew: boolcurrent_system: boolcalculate_equipment_cost(duration: float)calculate_hourly_cost(duration: float)calculate_salary_cost(duration: float)find_interrupted_weather_window(hours_required: float)find_uninterrupted_weather_window(hours_required: float)run_scheduled()run_unscheduled()mobilize()mobilize_scheduled()weather_delay(hours: float)wait_until_next_shift()wait_until_next_operational_period()proces_repair(request: RepairRequest, time_processed: float, prior_operational_level: float)crew_transfer(system: System, subassembly: Subassembly, requests: RepairRequest, to_system: bool)repair(hours: float, request_details: Failure | Maintenance)travel(start: str, end: str, set_current: str)Systemenv: WombatEnvironmentid: strname: stroperating_level: floatoperating_level_wo_servicing: floatpower_curve: Callablerepair_manager: RepairManagerservicing: boolvalue: floatcable_failure: boolcapacity: floatsubassemblies: list[Subassembly]electrical_system: Subasemblyelectronic_control: Subasemblygearbox: Subasemblygenerator: Subasemblyhydraulic_system: Subasemblymechanical_brake: Subasemblyrotor_blades: Subasemblyrotor_hub: Subasemblysensors: Subasemblysupporting_structure: Subasemblyyaw_system: Subasemblytransformer: Subasemblypower(windspeed: float | numpy.ndaray)interrupt_all_subassembly_processes()Subassemblyid: strname: strenv: WombatEnvironmentdata: SubassemblyDatabroken: booloperating_level: floatturbine: Systemprocesses: dictrun_single_failure(failure: Failure)run_single_maintenance(maintenance: Maintenance)interrupt_processes()interrupt_all_subassembly_processes()Cableid: strname: strenv: WombatEnvironmentdata: SubassemblyDatabroken: boolservicing: booldownstream_failure: boolprocesses: dictoperating_level: floatoperating_level_wo_servicing: floatrepair_manager: RepairManagerservicing: boolvalue: floatcable_failure: boolcapacity: floatrun_single_failure(failure: Failure)run_single_maintenance(maintenance: Maintenance)interrupt_processes()interrupt_all_subassembly_processes()stop_all_upstream_processes()Portenv: WombatEnvironmentmanager: RepairManagerwindfarm: Windfarmsettings: PortConfigturbine_manager: simpy.Resourcecrew_manager: simpy.Resourcetugboat_manager: simpy.Resourceactive_repairs: dict[str, dict[str, simpy.events.Event]]wait_until_next_shift()proces_repair(request: RepairRequest, time_processed: float, prior_operational_level: float)repair_single(request: RepairRequest)transfer_requests_from_manager(system_id: str)run_repairs(system_id: str)run_tow_to_port(request: RepairRequest)run_unscheduled_in_situ(request: RepairRequest) diff --git a/_images/high_level_diagram.svg b/_images/high_level_diagram.svg new file mode 100644 index 00000000..ef1ed7f4 --- /dev/null +++ b/_images/high_level_diagram.svg @@ -0,0 +1 @@ +Wind Farm ModelTurbinesSubstationsRepair ManagerServicing EquipmentCore ModelCablesElectrical SystemElectronic ControlRotor BladesDrivetrainGearboxGeneratorSensorsMechanical BrakeHydraulic SystemSupporting StructureRotor HubYaw SystemTransformerCableSystemsExample SubassembliesEnvironmentPortDataClassesSimulation APIPost Processing diff --git a/_images/package_hierarchy.svg b/_images/package_hierarchy.svg new file mode 100644 index 00000000..04a54bf8 --- /dev/null +++ b/_images/package_hierarchy.svg @@ -0,0 +1 @@ +
wombat.core
wombat
wombat.windfarm
wombat.utilities
simulation_api
post_processor
data_classes
library
environment
repair_management
service_equipment
windfarm
wombat.windfarm.system
system
subassembly
cable
utilities
diff --git a/_images/simulation_api.svg b/_images/simulation_api.svg new file mode 100644 index 00000000..43ded337 --- /dev/null +++ b/_images/simulation_api.svg @@ -0,0 +1 @@ +WombatEnvironmentdata_dir: pathlib.Pathweather_file: strworkday_end: intworkday_end: intsimulation_name: strstart_year: intend_year: intweather: pandas.DataFramemax_run_time: intshift_length: intweather_now: tuple[float, float]simulation_time: datetime.datetimecurrent_time: datetime.datetimeevents_log_fname: stroperations_log_fname: strpower_potential_fname: strpower_production_fname: strrun(until: int | float)hour_in_shift(hour: int, workday_start: int, workday_end: int) : boolhours_to_next_shift(workday_start: int) : floatdate_ix(date: datetime.datetime) int)weather_forecast(hours: int | float)convert_logs_to_csv(delete_original: bool, return_df: True)power_production_potential_to_csv(windfarm: Windfarm, operations: pandas.DataFrame, return_df: bool)cleanup_log_files(log_only: bool)Simulationenv: WombatEnvironmentlibrary_path: pathlib.Pathconfig: dict | Configurationwindfarm: Windfarmrepair_manager: RepairManagerservice_equipment: list[ServiceEquipment]metrics: Metricsfrom_config(cls, config: pathlib.Path | dict)run(until: int, create_metrics: bool, save_metrics_inputs: bool)save_metrics_inputs()Configurationname: strlibrary_path: pathlib.Pathlayout: strservice_equipment: str | list[str]weather: str | pandas.DataFrameworkday_start: intworkday_end: intinflation_rate: floatfixed_costs: strproject_capacity: floatstart_year: intend_year: intSAM_settings: strMetricsdata_dir: pathlib.Pathevents: str | pandas.DataFrameoperations: str | pandas.DataFramepotential: str | pandas.DataFrameproduction: str | pandas.DataFrameinflation_rate: floatproject_capacity: floatturbine_capacities: list[float]substation_id: list[str]turbine_id: list[str]service_equipment_names: list[str]fixed_costs: strSAM_settings: strfrom_simulation_outputs(cls, fpath: Path | str, fname: str)time_based_availability(frequency: str, by: str)production_based_availability(frequency: str, by: str)capacity_factor(which: str, frequency: str, by: str)task_completion_rate(which: str, frequency: str)equipment_costs(frequency: str, by_equipment: bool)service_equipment_utilization(frequency: str)labor_costs(frequency: str, by_type: bool)equipment_labor_cost_breakdowns(frequency: str, by_category: bool)component_costs(frequency: str, by_category: bool, by_action: bool)project_fixed_costs(frequency: str, resolution: str)process_times()power_production(frequency: str, by_turbine: bool)pysam_npv()pysam_lcoe_real()pysam_lcoe_nominal()pysam_irr()pysam_all_outputs() diff --git a/_images/simulation_diagram.png b/_images/simulation_diagram.png new file mode 100644 index 00000000..713803c9 Binary files /dev/null and b/_images/simulation_diagram.png differ diff --git a/_images/simulation_tools.svg b/_images/simulation_tools.svg new file mode 100644 index 00000000..8fb7e942 --- /dev/null +++ b/_images/simulation_tools.svg @@ -0,0 +1 @@ +ServiceEquipmentenv: WombatEnvironmentmanager: RepairManagerwindfarm: Windfarmsettings: ScheduledServiceEquipmentData | UnscheduledServiceEquipmentDataat_port: boolat_system: boolonsite: boolenroute: booltransferring_crew: boolcurrent_system: boolcalculate_equipment_cost(duration: float)calculate_hourly_cost(duration: float)calculate_salary_cost(duration: float)find_interrupted_weather_window(hours_required: float)find_uninterrupted_weather_window(hours_required: float)run_scheduled()run_unscheduled()mobilize()mobilize_scheduled()weather_delay(hours: float)wait_until_next_shift()wait_until_next_operational_period()proces_repair(request: RepairRequest, time_processed: float, prior_operational_level: float)crew_transfer(system: System, subassembly: Subassembly, requests: RepairRequest, to_system: bool)repair(hours: float, request_details: Failure | Maintenance)travel(start: str, end: str, set_current: str)Windfarmenv: WombatEnvironmentwindfarm_layout: strrepair_manager: RepairManagercapacity: floatgraph: networkx.DiGraphsystem_list: list[str]substation_id: list[str]turbine_id: list[str]distance_matrix: numpy.ndarraycurrent_availability: floatcurrent_availability_wo_servicing: floatcalculate_distance_matrix()_log_operations()system(system_id: str) : Systemcable(cable_id: str | tuple) : CableSystemenv: WombatEnvironmentid: strname: stroperating_level: floatoperating_level_wo_servicing: floatpower_curve: Callablerepair_manager: RepairManagerservicing: boolvalue: floatcable_failure: boolcapacity: floatsubassemblies: list[Subassembly]electrical_system: Subasemblyelectronic_control: Subasemblygearbox: Subasemblygenerator: Subasemblyhydraulic_system: Subasemblymechanical_brake: Subasemblyrotor_blades: Subasemblyrotor_hub: Subasemblysensors: Subasemblysupporting_structure: Subasemblyyaw_system: Subasemblytransformer: Subasemblypower(windspeed: float | numpy.ndaray)interrupt_all_subassembly_processes()Subassemblyid: strname: strenv: WombatEnvironmentdata: SubassemblyDatabroken: booloperating_level: floatturbine: Systemprocesses: dictrun_single_failure(failure: Failure)run_single_maintenance(maintenance: Maintenance)interrupt_processes()interrupt_all_subassembly_processes()Cableid: strname: strenv: WombatEnvironmentdata: SubassemblyDatabroken: boolservicing: booldownstream_failure: boolprocesses: dictoperating_level: floatoperating_level_wo_servicing: floatrepair_manager: RepairManagerservicing: boolvalue: floatcable_failure: boolcapacity: floatrun_single_failure(failure: Failure)run_single_maintenance(maintenance: Maintenance)interrupt_processes()interrupt_all_subassembly_processes()stop_all_upstream_processes()RepairManagerenv: WombatEnvironmentwindfarm: Windfarmdowntime_based_equipment: StrategyMaprequest_based_equipment: StrategyMaprequest_map: dict[str, int]submit_request(request: RepairRequest) : RepairREquestpurge_subassembly_requests(system_id: str, subassembly_id: str, exclude: list[str])get_next_highest_severity_request(equipment_capability: list[str], severity_level: int)get_request_by_system(equipment_capability: list[str], system_id: str)RepairRequestcable: booldetails: Failure | Maintenancerequest_id: strseverity_level: intsubassembly_id: strsubassembly_name: strsystem_id: strsystem_name: strupstream_turbines: list[str]assign_id(request_id: str)StrategyMapCTV: list[EquipmentMap]LCN: list[EquipmentMap]SCN: list[EquipmentMap]CAB: list[EquipmentMap]DSV: list[EquipmentMap]DRN: list[EquipmentMap]RMT: list[EquipmentMap]is_running: boolupdate(capability: str, threshold: float | int, equipment: ServiceEquipment)EquipmentMapequipment_map: ServiceEquipmentstrategy_threshold: int | floatWombatEnvironmentdata_dir: pathlib.Pathweather_file: strworkday_end: intworkday_end: intsimulation_name: strstart_year: intend_year: intweather: pandas.DataFramemax_run_time: intshift_length: intweather_now: tuple[float, float]simulation_time: datetime.datetimecurrent_time: datetime.datetimeevents_log_fname: stroperations_log_fname: strpower_potential_fname: strpower_production_fname: strrun(until: int | float)hour_in_shift(hour: int, workday_start: int, workday_end: int) : boolhours_to_next_shift(workday_start: int) : floatdate_ix(date: datetime.datetime) int)weather_forecast(hours: int | float)convert_logs_to_csv(delete_original: bool, return_df: True)power_production_potential_to_csv(windfarm: Windfarm, operations: pandas.DataFrame, return_df: bool)cleanup_log_files(log_only: bool)Simulationenv: WombatEnvironmentlibrary_path: pathlib.Pathconfig: dict | Configurationwindfarm: Windfarmrepair_manager: RepairManagerservice_equipment: list[ServiceEquipment]metrics: Metricsfrom_config(cls, config: pathlib.Path | dict)run(until: int, create_metrics: bool, save_metrics_inputs: bool)save_metrics_inputs()Configurationname: strlibrary_path: pathlib.Pathlayout: strservice_equipment: str | list[str]weather: str | pandas.DataFrameworkday_start: intworkday_end: intinflation_rate: floatfixed_costs: strproject_capacity: floatstart_year: intend_year: intSAM_settings: strMetricsdata_dir: pathlib.Pathevents: str | pandas.DataFrameoperations: str | pandas.DataFramepotential: str | pandas.DataFrameproduction: str | pandas.DataFrameinflation_rate: floatproject_capacity: floatturbine_capacities: list[float]substation_id: list[str]turbine_id: list[str]service_equipment_names: list[str]fixed_costs: strSAM_settings: strfrom_simulation_outputs(cls, fpath: Path | str, fname: str)time_based_availability(frequency: str, by: str)production_based_availability(frequency: str, by: str)capacity_factor(which: str, frequency: str, by: str)task_completion_rate(which: str, frequency: str)equipment_costs(frequency: str, by_equipment: bool)service_equipment_utilization(frequency: str)labor_costs(frequency: str, by_type: bool)equipment_labor_cost_breakdowns(frequency: str, by_category: bool)component_costs(frequency: str, by_category: bool, by_action: bool)project_fixed_costs(frequency: str, resolution: str)process_times()power_production(frequency: str, by_turbine: bool)pysam_npv()pysam_lcoe_real()pysam_lcoe_nominal()pysam_irr()pysam_all_outputs() diff --git a/_sources/API/core.md.txt b/_sources/API/core.md.txt new file mode 100644 index 00000000..bc72c0c2 --- /dev/null +++ b/_sources/API/core.md.txt @@ -0,0 +1,55 @@ +# Simulation Core Classes + +There are a variety of components that enable a simulation to be run, from the +environmen to the management of repairs to the servicing equipment. The below will show +how each of the APIs are powered to enable the full flexibility of modeling. + +```{image} ../images/simulation_tools.svg +:alt: +:align: center +:width: 2400px +``` + +## Environment +```{eval-rst} +.. autoclass:: wombat.core.environment.WombatEnvironment + :members: + :undoc-members: +``` + +## Repair Management +```{eval-rst} +.. autoclass:: wombat.core.repair_management.RepairManager + :members: + :undoc-members: + :exclude-members: _current_id +``` + +## Servicing Equipment +```{eval-rst} +.. automodule:: wombat.core.service_equipment + :members: + :inherited-members: + :exclude-members: ServiceEquipment + +.. autoclass:: wombat.core.service_equipment.ServiceEquipment + :members: + :inherited-members: + :undoc-members: + :exclude-members: env, windfarm, manager, settings, port +``` + +## Port +```{eval-rst} +.. automodule:: wombat.core.port + :exclude-members: Port + +.. autoclass:: wombat.core.port.Port + :members: transfer_requests_from_manager, repair_single, run_repairs, + wait_until_next_shift, run_tow_to_port, run_unscheduled_in_situ + :inherited-members: + :undoc-members: + :exclude-members: env, windfarm, manager, settings, requests_serviced, turbine_manager, + crew_manager, tugboat_manager, active_repairs, GetQueue, PutQueue, capacity, get, + get_queue, items, put, put_queue +``` diff --git a/_sources/API/index.md.txt b/_sources/API/index.md.txt new file mode 100644 index 00000000..b8a68e78 --- /dev/null +++ b/_sources/API/index.md.txt @@ -0,0 +1,31 @@ +# WOMBAT API + +The WOMBAT framework relies on a set of base data classes powered by the attrs +library and a series of simulation classes and methods to perform all the operations. + +To make a it easier for users, there is also a simulation interface provided. + +## Package Hierarchy + +```{image} ../images/package_hierarchy.svg +:alt: +:align: center +``` + +## Class Hierarchy + +```{image} ../images/class_diagram.svg +:alt: +:align: center +:width: 2400px +``` + +```{toctree} +:maxdepth: 2 + +simulation_api +types +core +windfarm +utilities +``` diff --git a/_sources/API/simulation_api.md.txt b/_sources/API/simulation_api.md.txt new file mode 100644 index 00000000..468fbf5d --- /dev/null +++ b/_sources/API/simulation_api.md.txt @@ -0,0 +1,43 @@ +# Simulation API + +```{image} ../images/simulation_api.svg +:alt: +:align: center +:width: 2400px +``` + +## Configuration +```{eval-rst} +.. autoclass:: wombat.core.simulation_api.Configuration + :members: + :undoc-members: + :exclude-members: name, library, layout, service_equipment, weather, workday_start, + workday_end, inflation_rate, fixed_costs, project_capacity, start_year, end_year, + port, port_distance, non_operational_start, non_operational_end, reduced_speed_start, + reduced_speed_end, reduced_speed, random_seed, random_generator +``` + +## Simulation Interface +```{eval-rst} +.. autoclass:: wombat.core.simulation_api.Simulation + :members: + :undoc-members: + :exclude-members: setup_simulation, config, env, initialize_metrics, library_path, + metrics, repair_manager, service_equipment, windfarm, port, random_seed, + random_generator +``` + + +## Metrics Computation +For example usage of the Metrics class and its associated methods, please see the [examples documentation page](../examples/metrics_demonstration) +```{eval-rst} +.. autoclass:: wombat.core.post_processor.Metrics + :members: from_simulation_outputs, power_production, time_based_availability, + production_based_availability, capacity_factor, task_completion_rate, + equipment_costs, service_equipment_utilization, vessel_crew_hours_at_sea, + number_of_tows, labor_costs, equipment_labor_cost_breakdowns, emissions, + process_times, component_costs, port_fees, project_fixed_costs, opex, npv + :member-order: bysource + :undoc-members: + :exclude-members: +``` diff --git a/_sources/API/types.md.txt b/_sources/API/types.md.txt new file mode 100644 index 00000000..0bbfffcb --- /dev/null +++ b/_sources/API/types.md.txt @@ -0,0 +1,175 @@ +# Data Classes + +The WOMBAT architecture relies heavily on a base set of data classes to process most of +the model's inputs. This enables a rigid, yet robust data model to properly define a +simulation. + +```{image} ../images/data_classes.svg +:alt: +:align: center +:width: 2400px +``` + +## What is this FromDictMixin I keep seeing in the code diagrams? + +The `FromDictMixin` class provides a standard method for providing dictionary definitions +to `attrs` dataclasses without worrying about overloading the definition. Plus, you get +the convenience of writing `cls.from_dict(data_dict)` instead of cls(**data_dict), and +hoping for the best. + +```{eval-rst} +.. autoclass:: wombat.core.data_classes.FromDictMixin + :members: + :undoc-members: + :exclude-members: +``` + +## Scheduled and Unscheduled Maintenance + +### Maintenance Tasks +```{eval-rst} +.. autoclass:: wombat.core.data_classes.Maintenance + :members: + :undoc-members: + :exclude-members: time, materials, frequency, equipment, system_value, description, + level, operation_reduction, rng, service_equipment, replacement +``` + +### Failures +```{eval-rst} +.. autoclass:: wombat.core.data_classes.Failure + :members: + :undoc-members: + :exclude-members: scale, shape, time, materials, operation_reduction, weibull, name, + maintenance, failures, level, equipment, system_value, description, rng, + service_equipment, replacement +``` + +### Repair Requests +```{eval-rst} +.. autoclass:: wombat.core.data_classes.RepairRequest + :members: + :undoc-members: + :exclude-members: system_id, system_name, subassembly_id, subassembly_name, + severity_level, details, cable, upstream_turbines, upstream_cables, +``` + +## Servicing Equipment and Crews + +### Service Equipment +```{eval-rst} +.. autoclass:: wombat.core.data_classes.ServiceEquipmentData + :members: + :undoc-members: + :exclude-members: data_dict, strategy +``` + +### ServiceCrew +```{eval-rst} +.. autoclass:: wombat.core.data_classes.ServiceCrew + :members: + :undoc-members: + :exclude-members: n_day_rate, day_rate, n_hourly_rate, hourly_rate +``` + +### Scheduled Service Equipment +```{eval-rst} +.. autoclass:: wombat.core.data_classes.ScheduledServiceEquipmentData + :members: + :undoc-members: + :exclude-members: name, equipment_rate, day_rate, n_day_rate, hourly_rate, + n_hourly_rate, start_month, start_day, start_year, end_month, end_day, end_year, + capability, mobilization_cost, mobilization_days, speed, max_windspeed_repair, + max_windspeed_transport, max_waveheight_transport, max_waveheight_repair, onsite, + method, max_severity, operating_dates, create_date_range, workday_start, + workday_end, crew, crew_transfer_time, n_crews, strategy, port_distance, + reduced_speed_start, reduced_speed_end, reduced_speed, speed_reduction_factor, + non_operational_end, non_operational_start, +``` + +### Unscheduled Service Equipment +```{eval-rst} +.. autoclass:: wombat.core.data_classes.UnscheduledServiceEquipmentData + :members: + :undoc-members: + :exclude-members: name, equipment_rate, day_rate, n_day_rate, hourly_rate, + n_hourly_rate, start_month, start_day, start_year, end_month, end_day, end_year, + capability, mobilization_cost, mobilization_days, speed, max_windspeed_repair, + max_windspeed_transport, max_waveheight_transport, max_waveheight_repair, onsite, + method, max_severity, operating_dates, create_date_range, workday_start, + workday_end, crew, crew_transfer_time, n_crews, strategy, strategy_threshold, + speed_reduction_factor, non_operational_end, non_operational_start, unmoor_hours, + reconnection_hours, port_distance, reduced_speed_start, reduced_speed_end, + reduced_speed, tow_speed, charter_days +``` + +### Port Configuration +```{eval-rst} +.. autoclass:: wombat.core.data_classes.PortConfig + :members: + :undoc-members: + :exclude-members: name, tugboats, crew, n_crews, max_operations, workday_start, + workday_end, site_distance, annual_fee, non_operational_start, non_operational_end, + reduced_speed_start, reduced_speed_end, reduced_speed, non_operational_dates_set, + reduced_speed_dates_set, non_stop_shift +``` + +## Wind Farm Support + +### Subassembly Model +```{eval-rst} +.. autoclass:: wombat.core.data_classes.SubassemblyData + :members: + :undoc-members: + :exclude-members: name, maintenance, failures, system_id, system_name, system_value, + subassembly_id, subassembly_name, severity_level, details, cable, upstream_turbines, + rng +``` + +### Wind Farm Map +```{eval-rst} +.. autoclass:: wombat.core.data_classes.WindFarmMap + :members: + :undoc-members: + :exclude-members: substation_map, export_cables +``` + +### Substation Map +```{eval-rst} +.. autoclass:: wombat.core.data_classes.SubstationMap + :members: + :undoc-members: + :exclude-members: string_starts, string_map, downstream +``` + +### String +```{eval-rst} +.. autoclass:: wombat.core.data_classes.String + :members: + :undoc-members: + :exclude-members: start, upstream_map +``` + +### Sub String +```{eval-rst} +.. autoclass:: wombat.core.data_classes.SubString + :members: + :undoc-members: + :exclude-members: downstream, upstream +``` + +## Miscellaneous + +### Fixed Cost Model +```{eval-rst} +.. autoclass:: wombat.core.data_classes.FixedCosts + :members: + :undoc-members: + :exclude-members: operations, operations_management_administration, + project_management_administration, marine_management, weather_forecasting, + condition_monitoring, operating_facilities, environmental_health_safety_monitoring, + insurance, brokers_fee, operations_all_risk, business_interruption, + third_party_liability, storm_coverage, annual_leases_fees, submerge_land_lease_costs, + transmission_charges_rights, onshore_electrical_maintenance, labor, resolution, + hierarchy, cost_category_validator +``` diff --git a/_sources/API/utilities.md.txt b/_sources/API/utilities.md.txt new file mode 100644 index 00000000..2fef8efa --- /dev/null +++ b/_sources/API/utilities.md.txt @@ -0,0 +1,33 @@ +# Helpers and Plotting + +## Plotting + +```{eval-rst} +.. automodule:: wombat.utilities.plot + :members: + :undoc-members: +``` + +## Logging functions + +```{eval-rst} +.. automodule:: wombat.utilities.logging + :members: + :undoc-members: +``` + +## Time Calculations + +```{eval-rst} +.. automodule:: wombat.utilities.time + :members: + :undoc-members: +``` + +## Miscellaneous + +```{eval-rst} +.. automodule:: wombat.utilities.utilities + :members: + :undoc-members: +``` diff --git a/_sources/API/windfarm.md.txt b/_sources/API/windfarm.md.txt new file mode 100644 index 00000000..98599a37 --- /dev/null +++ b/_sources/API/windfarm.md.txt @@ -0,0 +1,33 @@ +# Wind Farm Classes + +The wind farm classes define how the wind farm's graph model are created, and power the +timing out of the failures and maintenance events within the simulation for the cables, +substations, and turbines. + +## Windfarm +```{eval-rst} +.. automodule:: wombat.windfarm.windfarm + :members: + :undoc-members: +``` + +## System: Wind turbine or substation +```{eval-rst} +.. automodule:: wombat.windfarm.system.system + :members: + :undoc-members: +``` + +## Subassembly: The modeled componenents of a system +```{eval-rst} +.. automodule:: wombat.windfarm.system.subassembly + :members: + :undoc-members: +``` + +## Cable: Hybrid system and subassembly model +```{eval-rst} +.. automodule:: wombat.windfarm.system.cable + :members: + :undoc-members: +``` diff --git a/_sources/changelog.md.txt b/_sources/changelog.md.txt new file mode 100644 index 00000000..16c7724e --- /dev/null +++ b/_sources/changelog.md.txt @@ -0,0 +1,4 @@ +# Change Log + +```{include} ../../CHANGELOG.md +``` diff --git a/_sources/examples/examples_reference.md.txt b/_sources/examples/examples_reference.md.txt new file mode 100644 index 00000000..a9b344b4 --- /dev/null +++ b/_sources/examples/examples_reference.md.txt @@ -0,0 +1,60 @@ +Examples Reference +================== + +This page will provide a brief overview of the varying examples available in the +[examples folder on GitHub](https://github.com/WISDEM/WOMBAT/blob/main/examples). + +## `archival/` + +This contains the old notebooks and data used to prepare the unveiling of the model to +DOE. The results are displayed in +[presentations section](../presentations.md#fy20-doe-presentation) of the documentation. + +## Explanations + +The following notebooks are aimed at demonstrating and explaining various functionality +to users. + +### `how_to.ipynb` + +This is a Jupyter Notebook version of the [*How To* section](./how_to.md) of the documentation that +allows users to interact with the walk through. + +### `strategy_demonstration.ipynb` + +This is a Jupyter Notebook version of the +[*Strategy Demonstration* section](./strategy_demonstration.md) of the documentation +that allows users to interact with the walk through. + +### `metrics_demonstration.ipynb` + +This is a Jupyter Notebook version of the +[*Metrics Demonstration* section](./metrics_demonstration.md) of the documentation that +allows users to interact with the varying maintenance strategy models. + +### `NAWEA_interactive_walkthrough.ipynb` + +This is the notebook used for the NAWEA WindTech 2023 Workshop. See +[here](../workshops/nawea_wind_tech_2023.md) for details. + + +## Validation + +### `dinwoodie_validation.ipynb` + +This shows the latest results of the model validation using the modeling parameters from +Dinwoodie, et. al, 2015 [^dinwoodie2015reference]. + +### `iea_26_validation.ipynb` + +This shows the latest results of the model validation using the modeling parameters from +IEA, 2016 [^smart2016iea]. + +### `timing_benchmarks.ipynb` + +This notebook is a simple benchmark for comparing run times between releases from NREL's +FY22 & FY23. That said, there has been a change in computers since running this, and +the listed times for v0.7 in the notebooks will not match those in this example. + +[^dinwoodie2015reference]: Iain Dinwoodie, Ole-Erik V Endrerud, Matthias Hofmann, Rebecca Martin, and Iver Bakken Sperstad. Reference cases for verification of operation and maintenance simulation models for offshore wind farms. *Wind Engineering*, 39(1):1–14, 2015. +[^smart2016iea]: Gavin Smart, Aaron Smith, Ethan Warner, Iver Bakken Sperstad, Bob Prinsen, and Roberto Lacal-Arantegui. Iea wind task 26: offshore wind farm baseline documentation. Technical Report, National Renewable Energy Lab.(NREL), Golden, CO (United States), 2016. diff --git a/_sources/examples/how_to.md.txt b/_sources/examples/how_to.md.txt new file mode 100644 index 00000000..4eabceda --- /dev/null +++ b/_sources/examples/how_to.md.txt @@ -0,0 +1,645 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# How To Use WOMBAT + +This tutorial will walk through the setup, running, and results stages of a WOMBAT +simulation while providing background information about how each component is related. + +## Imports + +The following code block demonstrates a typical setup for working with WOMBAT and running +analyses. + +```{code-cell} ipython3 +from time import perf_counter # timing purposes only + +import numpy as np +import pandas as pd + +from wombat import Simulation +from wombat.core.library import load_yaml, DINWOODIE + +# Seed the random variable for consistently randomized results +np.random.seed(0) + +# Improve the legibility of DataFrames +pd.set_option("display.float_format", '{:,.2f}'.format) +pd.set_option("display.max_rows", 1000) +pd.set_option("display.max_columns", 1000) +``` + + +## Defining the Simulation + +The following will demonstrate the required information to run a simulation. For the +purposes of this tutorial, we'll be working with the data under +`library/code_comparison/dinwoodie` in the Github repository, and specifically the base +case. + +One important item to note is that the library structure is enforced within the code so all +data must be placed in the appropriate locations in your analysis' library as follows: + +```{warning} +As of v0.6, the following structure will be adopted to mirror the format of the +[ORBIT library structure](https://github.com/WISDEM/ORBIT/blob/master/ORBIT/core/library.py#L7-L24) +to increase compatibility between similar libraries. + +As of v0.9, the library structure shown below is the only one that will work. +``` + +To help users convert to the new structure, the following method is provided to create +the required folder structure for users. + +```{code-block} python + +from wombat import create_library_structure # located in wombat.core.library + +new_library = "library" +create_library_structure(new_library) +``` + +The above method call will produce the below folder and subfolder structure. + +``` + + ├── project + ├── config <- Project-level configuration files + ├── port <- Port configuration files + ├── plant <- Wind farm layout files + ├── cables <- Export and Array cable configuration files + ├── substations <- Substation configuration files + ├── turbines <- Turbine configuration and power curve files + ├── vessels <- Land-based and offshore servicing equipment configuration files + ├── weather <- Weather profiles + ├── results <- The analysis log files and any saved output data +``` + +As a convenience feature you can import the provided validation data libraries as +`DINWOODIE` or `IEA_26` as is seen in the imports above, and a consistent path will be +enabled. + +In practice, any folder location can be used so long as it follows the subfolder structure provided +here. + + +### Windfarm Layout + +The windfarm layout is determined by a csv file, `dinwoodie/windfarm/layout.csv` in this case. Below is a sample of what information is required and how to use each field, followed by a visual example. It should be noted that none of the headings are case sensitive. + + +id (required) +: Unique identifier for the asset; no spaces allowed. + +substation_id (required) +: The id field for the substation that the asset connects to; in the case that this is a substation, then this field should be the same as id; no spaces allowed. + +name (required) +: A descriptive name for the turbine, if desired. This can be the same as id. + +type (optional) +: One of "turbine" or "substation". This is required to accurately model a multi-substation wind farm. The base assumption is that a substation connects to itself as a means to model the export cable connecting to the interconnection point, however, this is not always the case, as substations may be connected through their export systems. Using this filed allows for that connection to be modeled accurately. + +longitude (optional) +: The longitudinal position of the asset, can be in any geospatial reference; optional. + +latitude (optional) +: The latitude position of the asset, can be in any geospatial reference; optional. + +string (required) +: The integer, zero-indexed, string number for where the turbine will be positioned. + +order (required) +: The integer, zero-indexed position on the string for where the turbine will be positioned. + +distance (optional) +: The distance to the upstream asset; if this is calculated (input = 0), then the straightline distance is calculated using the provided coordinates (WGS-84 assumed). + +subassembly (required) +: The file that defines the asset's modeling parameters. + +upstream_cable (required) +: The file that defines the upstream cable's modeling parameters. + +upstream_cable_name (optional) +: The descriptive name to give to the cable that will be used during logging. This enables users to use a single cable definition file while maintaining the naming conventions used for the wind farm being simulated. + +```{note} +In the example below, there are a few noteworthy caveats that will set the stage for +later input reviews: + - The cables are not modeled, which has a couple of implications + - There only needs to be one string "0" + - The cable specifications are required, even if not being modeled (details later) + - longitude, latitude, and distance are all "0" because the spatial locations are not used + - subassembly is all "vestas_v90.yaml", but having to input the turbine subassembly model + means that multiple turbine types can be used on a windfarm. + - This same logic applies to the upstream_cable so that multiple cable types can be + used as appopriate. +``` + +
+ +| id | substation_id | name | type | longitude | latitude | string | order | distance | subassembly | upstream_cable | +| :-- | :-- | :-- | :-- | --: | --: | --: | --: | --: | :-- | :-- | +| OSS1 | OSS1 | OSS1 | substation | 0 | 0 | | | | offshore_substation.yaml | export.yaml | +| S00T1 | OSS1 | S00T1 | turbine | 0 | 0 | 0 | 0 | 0 | vestas_v90.yaml | array.yaml | +| S00T2 | OSS1 | S00T2 | turbine | 0 | 0 | 0 | 1 | 0 | vestas_v90.yaml | array.yaml | +| S00T3 | OSS1 | S00T3 | turbine | 0 | 0 | 0 | 2 | 0 | vestas_v90.yaml | array.yaml | +| ... | +| S00T79 | OSS1 | S00T79 | turbine | 0 | 0 | 0 | 78 | 0 | vestas_v90.yaml | array.yaml | +| S00T80 | OSS1 | S00T80 | turbine | 0 | 0 | 0 | 79 | 0 | vestas_v90.yaml | array.yaml | +
+ +### Weather Profile + +The weather profile will broadly define the simulation range with its start and stop +points, though a narrower one can be used when defining the simulation (more later). + +The following columns should exist in the data set with the provided guidelines. + +datetime (required) +: A date and time stamp, any format. + +windspeed (required) +: The hourly, mean windspeed, in meters per second at the time stamp. + +waveheight (optional) +: The hourly, mean waveheight, in meters, at the time stamp. If waves are not required, + this can be filled with zeros, or be left out entirely. + + +Below, is a demonstration of what `weather/alpha_ventus_weather_2002_2014.csv` looks like. + +| datetime | windspeed | waveheight | +| :-- | --: | --: | +| 1/1/02 0:00 | 11.75561096 | 1.281772405 | +| 1/1/02 1:00 | 10.41321252 | 1.586584315 | +| 1/1/02 2:00 | 8.959270788 | 1.725690828 | +| 1/1/02 3:00 | 9.10014808 | 1.680982063 | +| ... | +| 12/31/14 22:00 | 14.40838803 | 0.869625003 | +| 12/31/14 23:00 | 14.04563195 | 0.993031445 | + + +### Environmental Considerations + +In addition to using weather conditions for site characteristics, WOMBAT is able to +model environmental considerations where a port or site cannot be accessed, for example, +when silt builds up and water depths become too low to tow a turbine into the port. +There is also a feature for imposing maximum operating speeds, such as when there are +animal migrations and vessels must slow down to avoid collisions with endangered species. + +Defining the `non_operational_start` and `non_operational_end` at either the servicing +equipment, environment, or port level allows for the creation of an annualized date +range spanning the length of the simulation where operations are not allowed to occur. +When defined at the environment level, all servicing equipment and a port, if defined, +will have this non-operational period applied, and if it's already existing, the more +conservative of the date ranges will be applied. When defined at the port level, all +associated servicing equipment (tugboats) will have the same inuring priority as when +defined at the environment level. + +The same logic applies when defining the `reduced_speed_start` and `reduced_speed_end` +for defining when the operating speeds of servicing equipment are capped at the +`reduced_speed`. As is the case above, these variables can also be defined at the +servicing equipment level for further customization. + + +### Fixed Costs + +Please see the [`FixedCosts` API documentation](../API/types.md#fixed-cost-model) for +details on this optional piece of financial modeling. + +For modeling a tow-to-port strategy that the port rental costs should be included in +this category, and not in the port configuration. + + +### Servicing Equipment + +The servicing equipment control the actual repairs within a simulation, and as of v0.5, +there are four different repair strategies that can be used: scheduled, downtime-based +unscheduled, repair-based unscheduled, and tow-to-port. These are options are controlled +through the `capability` settings in each equipment's configuration in conjunction with +the `service_equipment` setting in the maintenance and failure configurations for each +subassembly. + +For complete documentation of how the servicing equipment parameters are defined, please +see the [ServiceEquipmentData API documentation](../API/types.md#service-equipment) + +Below is an definition of the different equipment codes and their designations to show +the breadth of what can be simulated. These codes do not have separate operating models, +but instead allow the user to specify the types of operations the servicing equipment +will be able to operate on. This model should be aligned with the `service_equipment` +requirements in the subassembly failure and maintenance models. + +RMT +: remote (no actual equipment BUT no special implementation), akin to remote resets + +DRN +: drone, or potentially even helicopters by changing the costs + +CTV +: crew transfer vessel/onsite truck + +SCN +: small crane (i.e., field support vessel or cherry picker) + +LCN +: large crane (i.e., heavy lift vessel or crawler crane) + +CAB +: cabling-specific vessel/vehicle + +DSV +: diving support vessel + +TOW +: tugboat/towing (a failure with this setting will trigger a tow-to-port scenario where +the simulation's `Port` will dispatch the tugboats as needed) + +AHV +: anchor handling vessel (this is a variation on the tugboat for mooring repairs that +will not tow anthing between port and site, but operate at the site) + +Aside from the TOW and AHV capabilities there are no operations specific to each +capability. The remaining configurations of a servicing equipment such as equipment +rates, mobilization, labor, and operating limits will define the nature of its operation +in tandem with a failure's `time` field. So for a remote reset (RMT), there will be a +trivial equipment rate, if any, associated with it to account for the specific operations +or resetting the subassembly remotely. Similarly, a drone repair or inspection (DRN) +will not require any onboard crew, so labor will 0, or low if assuming an operator that +is unaccounted for in the site `FixedCosts`, but will require more time than a remote +reset in addition to a higher equipment cost. + +In addition to a variety of servicing equipment types, there is support for +3 different equipment-level dispatch strategies, as described below. For a set of +example scenarios, please see the [strategy demonstration](strategy_demonstration.ipynb). + +scheduled +: dispatch servicing equipment for a specified date range each year + +requests +: dispatch the servicing equipment once a `strategy_threshold` number of requests + that the equipment can service has been reached + +downtime +: dispatch the servicing equipment once the windfarm's operating level reaches the + `strategy_threshold` percent downtime. + + +### The System Models + +The actual assets on the windfarm such as cables, turbines, and substations, are +referred to as systems in WOMBAT, and each has their own individual model. Within each +of these systems, there are user-defined subassemblies (or componenents) that rely on +two types of repair models: + +- maintenance: scheduled, fixed time interval-based maintenance tasks +- failures: unscheduled, Weibull distribution-based modeled, maintenance tasks + +The subassemblies keys in the system YAML definition can be user-defined +to accommodate the varying language among industry groups and generations of wind +technologies. The only restrictions are the YAML keys must not contain any special +characters, and cannot be repeated + +In the example below we show a `generator` subassembly with an annual service task and +frequent manual reset. For a thorough definition, please read the API +documentation of the [Maintenance](../API/types.md#maintenance-tasks) and +[Failure](../API/types.md#failures) data classes. Note that the yaml defintion below +specifies that maintenance tasks are in a bulleted list format and that failure +defintions require a dictionary-style input with keys to match the severity level of a +given failure. For more details on the complete subassembly definition, please visit the +[Subassembly API documentation](../API/types.md#subassembly-model). + +```{code-block} yaml +generator: + name: generator # doesn't need to match the subassembly key that becomes System.id + maintenance: + - description: annual service + time: 60 + materials: 18500 + service_equipment: CTV + frequency: 365 + operation_reduction: 0 # default value + failures: + 1: + scale: 0.1333 + shape: 1 + time: 3 + materials: 0 + service_equipment: CTV + operation_reduction: 0.0 + replacement: False # default value + level: 1 # Note that the "level" value matches the key "1" + description: manual reset +``` + + +#### Substations + +The substation model relies on two specific inputs, and one subassembly input (transformer). + +capacity_kw +: The capacity of all turbines in the windfarm, neglecting any losses. Only needed if +a $/kw cost basis is being used. + +capex_kw +: The $/kw cost of the machine, if not providing absolute costs. + +Additional keys can be added to represent subassemblies, such as a transformer, in the +same format as the generator example above. Similarly, a user can define as man or as +few of the subassemblies as desired with their preferred naming conventions + +The following is an example of substation YAML definition with no modeled subasemblies. + +```{code-block} yaml +capacity_kw: 670000 +capex_kw: 140 +transformer: + name: transformer + maintenance: + - + description: n/a + time: 0 + materials: 0 + service_equipment: CTV + frequency: 0 + failures: + 1: + scale: 0 + shape: 0 + time: 0 + materials: 0 + service_equipment: [CTV] + operation_reduction: 0 + level: 1 + description: n/a +``` + + +#### Turbines + +The turbine has the most to define out of the three systems in the windfarm model. +Similar to the substation, it relies mainly on the subassembly model with a few extra +parameters, as defined here: + +capacity_kw +: The capacity of the system. Only needed if a $/kw cost basis is being used. + +capex_kw +: The $/kw cost of the machine, if not providing absolute costs. + +power_curve: file +: File that provides the power curve definition. + +power_curve: bin_width +The desired interval, in m/s, between adjacent points on the power curve to be used for +power calculations. + +The `windfarm/vestas_v90.yaml` data file provides the following definition in addition +to the the maintenance and failure definitions that were shown previously. + +```{code-block} yaml +capacity_kw: 3000 +capex_kw: 1300 +power_curve: + file: vestas_v90_power_curve.csv + bin_width: 0.5 +``` + +The power curve input CSV requires the following two columns: `windspeed_ms` and +`power_kw` that should be defined using the windspeed for a bin, in m/s and the power produced at that +windspeed, in kW. The current method available for generating the power curve is the IEC +61400-12-1-2 method for a wind-speed binned power curve. If there is a need/desire for +additional power curve methodologies, then [please submit an issue on the GitHub](https://github.com/WISDEM/WOMBAT/issues)! + +For an open source listing of a variety of land-based, offshore, and distributed wind +turbine power curves, please visit the +[NREL Turbine Models repository](https://github.com/NREL/turbine-models). + + +#### Cables + +The array cable is the simplest format in that you only define a descriptive name, +and the maintenance and failure events as below. It should be noted that the scheme +is a combination of both the system and subassembly configuration. + +For export cables that connect substations, as opposed to an interconnection, they will +not create dependencies because it is assumed they bypass the substation altogether, +and connect directly with the export system. This means that if there is a failure at a +downstream substation, the connecting export cable and its upstream turbine, substation, +and cable connections will continue to operate normally. + +```{code-block} yaml +name: array cable +maintenance: + - + description: n/a + time: 0 + materials: 0 + service_equipment: CTV + frequency: 0 +failures: + 1: + scale: 0 + shape: 0 + time: 0 + materials: 0 + operation_reduction: 0 + service_equipment: CAB + level: 1 + description: n/a +``` + + +## Set Up the Simulation + +In the remaining sections of the tutorial, we will work towards setting up and running +a simulation. + +### Define the data library path + +The set library enables WOMBAT to easily access and store data files in a consistent +manner. Here the `DINWOODIE` reference is going to be used again. + +```{note} +If a custom library is being used, the `library_path` must be the full path name to the +location of the folder where the configuration data is contained. +``` + +```{warning} +In v0.6, a new library structure +``` + +```{code-cell} ipython3 +library_path = DINWOODIE # or user-defined path for an external data library +``` + +### The configuration file + + +In the configuration below, there are a number of data points that will define our +windfarm layout, weather conditions, working hours, customized start and completion +years, project size, financials, and the servicing equipment to be used. Note that there +can be as many or as few of the servicing equipment units as desired. + +The purpose of an overarching configuration file is to provide a single place to define +the primary inputs for a simulation. Below the base configuration is loaded and displayed +with comments to show where each of files are located in the library structure. WOMBAT +will know where to go for these pointers when the simulation is initialized so the data +is constructed and validated correctly. + +```{code-cell} ipython3 +config = load_yaml(library_path / "project/config", "base.yaml") +``` + +```{code-block} yaml +# Contents of: dinwoodie / config / base.yaml +name: dinwoodie_base +weather: alpha_ventus_weather_2002_2014.csv # located in: dinwoodie / weather +service_equipment: +# YAML-encoded list, but could also be created in standard Python list notation with +# square brackets: [ctv1.yaml, ctv2.yaml, ..., hlv_requests.yaml] +# All below equipment configurations are located in: dinwoodie / vessels + - ctv1.yaml + - ctv2.yaml + - ctv3.yaml + - fsv_requests.yaml + - hlv_requests.yaml +layout: layout.csv # located in: dinwoodie / windfarm +inflation_rate: 0 +fixed_costs: fixed_costs.yaml # located in: dinwoodie / project / config +workday_start: 7 +workday_end: 19 +start_year: 2003 +end_year: 2012 +project_capacity: 240 +# port: base_port.yaml <- When running a tow-to-port simulation the port configuration +# pointer is provided here and located in: dinwoodie / project / port +``` + +## Create a simulation + +There are two ways that this could be done, the first is to use the classmethod +`Simulation.from_config()`, which allows for the full path string, a dictionary, or +`Configuration` object to passed as an input, and the second is through a standard +class initialization. + +### Option 1: `Simulation.from_config()` + +Load the file from the `Configuration` object that was created in the prior code black + +```{code-cell} ipython3 + +sim = Simulation.from_config(library_path=library_path, config=config) + +# Delete any files that get initialized through the simulation environment +sim.env.cleanup_log_files() +``` + +### Option 2: `Simulation()` + +Load the configuration file automatically given a library path and configuration file name. + +In this usage, the string "DINWOODIE" can be used because the `Simulation` class knows +to look for this library mapping, as well as the "IEA_26" mapping for the two validation +cases that we demonstrate in the examples folder. + +```{note} +In Option 2, the config parameter can also be set with a dictionary. + +The library path in the configuration file should match the one provided, or the +setup steps will fail in the simulation. +``` + +```{code-cell} ipython3 +sim = Simulation( + library_path="DINWOODIE", # automatically directs to the provided library + config="base.yaml" +) +sim.env.cleanup_log_files() +``` + +### Seeding the simulation random variable + +Using `random_seed` a simulation can be seeded to produce the same results every single +time, or a `random_generator` can be provided to use the same generator for a batch of +identical simulations to better understand the variations in results. + +```{code-cell} ipython3 +sim = Simulation( + library_path="DINWOODIE", # automatically directs to the provided library + config="base.yaml", + random_seed=2023, # integer value indicating how to seed the internally-created generator +) +sim.env.cleanup_log_files() + +rng = np.random.default_rng(seed=2023) # create the generator +sim = Simulation( + library_path="DINWOODIE", # automatically directs to the provided library + config="base.yaml", + random_generator=rng, # generator that can be shared among all processes +) +``` + +## Run the analysis + +When the run method is called, the default run time is for the full length of the +simulation, however, if a shorter run than was previously designed is required for +debugging, or something similar, we can use `sum.run(until=)` to do this. In +the `run` method, not only is the simulation run, but the metrics class is loaded at the +end to quickly transition to results aggregation without any further code. + +```{code-cell} ipython3 +# Timing for a demonstration of performance +start = perf_counter() + +sim.run() + +end = perf_counter() + +timing = end - start +print(f"Run time: {timing / 60:,.2f} minutes") +``` + + +## Metric computation + +For a more complete view of what metrics can be compiled, please see the [metrics notebook](metrics_demonstration.ipynb), though for the sake of demonstration a few methods will +be shown here + +```{code-cell} ipython3 +net_cf = sim.metrics.capacity_factor(which="net", frequency="project", by="windfarm").values[0][0] +gross_cf = sim.metrics.capacity_factor(which="gross", frequency="project", by="windfarm").values[0][0] +print(f" Net Capacity Factor: {net_cf:2.1%}") +print(f"Gross Capacity Factor: {gross_cf:2.1%}") +``` + +```{code-cell} ipython3 +# Report back a subset of the metrics +total = sim.metrics.time_based_availability(frequency="project", by="windfarm") +print(f" Project time-based availability: {total.windfarm[0]:.1%}") + +total = sim.metrics.production_based_availability(frequency="project", by="windfarm") +print(f"Project energy-based availability: {total.windfarm[0]:.1%}") + +total = sim.metrics.equipment_costs(frequency="project", by_equipment=False) +print(f" Project equipment costs: ${total.values[0][0] / sim.metrics.project_capacity:,.2f}/MW") + +``` + + +## Optional: Delete the logging files + +In the case that a lot of simulations are going to be run, and the processed outputs are all that is required, then there is a convenience method to cleanup these files automatically once you are done. + +```{code-cell} ipython3 +sim.env.cleanup_log_files() +``` diff --git a/_sources/examples/index.md.txt b/_sources/examples/index.md.txt new file mode 100644 index 00000000..cf15b432 --- /dev/null +++ b/_sources/examples/index.md.txt @@ -0,0 +1,145 @@ + +# User Guide + +This page provides an overview of what WOMBAT is currently able to model, broken down by +general category. Following this, there are separate pages for how this is done as +demonstrated through example notebooks. To fully follow the particulars of each example, +it is recommended to see how each model's configuration files are composed. + +For thorough explanations of the design and implementation ethos of the model, please +see our NREL Technical Report: https://www.osti.gov/biblio/1894867, which was published +alongside v0.5.1, so some functionality has been updated. +## Feature Overview + +For a complete and detailed description of the functionality provided in WOMBAT, it is +recommended to read the API documentation. However, that is quite a task, so below is +a short listing of the core functionality that WOMBAT can provide. + +### Post Processing and the Simulation API + +- The [`Simulation` class](../API/simulation_api.md#simulation-api) and its + [`Configuration`](../API/simulation_api.md#configuration) allow users unfamiliar with + Python to run the simulation library with minimal code. This was the primary design + choice for the model: to create a configuration-based model to enable users with nearly + any level Python exposure. +- The [`Metrics`](../API/simulation_api.md#metrics-computation) class provides a + straightfoward interface for computing a wide variety of operational summary statistics + from performance to wind power plant financials. + +### Environmental Considerations + +- Windspeed (m/s) and wave height (m) for the duration of a simulation (user-provided, + hourly profile) +- Reduced speed periods for animal migrations. This is primarily an offshore-focused + feature, but can be defined at the simulation, port, or servicing equipment level + using the following three variables: `reduced_speed_start`, `reduced_speed_end`, and + `reduced_speed` (km/hr). This translates to a maximum speed of `reduced_speed` being + enforced between the starting and ending dates each year of the simulation. For more + details, see the documentation pages for the + [environment](../API/core.md#environment), [port](../API/core.md#port), + [unshcheduled servicing equipment](../API/types.md#unscheduled-service-equipment), or + [scheduled servicing equipment](../API/types.md#scheduled-service-equipment). +- Prohibited operation periods. This dictates periods of time where a site or port may + be inaccessible, or when a piece of servicing equipment should not be allowed to be + mobilized/dispatched. Similar to the speed reduction periods, `non_operational_start` + and `non_operational_end` dictate an annual date range for when servicing equipment + can't be active, or a port or the whole site is inaccessible. The same documenation + pages as above can be referenced for more details. + +### System and Subassembly Modeling + +Each substation, turbine, and cable operate with the same core logic of +[maintenance](../API/types.md#maintenance-tasks) and [failure](../API/types.md#failures) +events. The following will break down the differences between each of these systems' +modeling assumptions as well as the basic operations of the maintenance and failure models. + +- Maintenance + - `frequency`: based on a fixed number of days between an event + - `operation_reduction`: the percentage of degradation the triggering of this event + cause +- Failure + - Randomly samples from a Weibull distribution to determine the next failure + - `operation_reduction`: the percentage of degradation the triggering of this event + cause + - `replacement`: if `True`, then all preceeding failures and maintenance tasks are + cancelled and the time until the next event is reset because a replacement is + required, otherwise, each successive failure is added to the repair queue +- Commonalities between Substations, Turbines, and Cables + - Maintenance and Failures compose the actual "model" + - `operating_level` provides the real degradation of a model, including if it is turned + off for servicing + - `operating_level_wo_servicing` provides the operting level as if there were no + ongoing operations to know the potential operating operating +- Substations + - Any failure or maintenance with an `operation_reduction` of 1 will turn off every + upstream connected (and modeled) cable and turbine + - For subations that are connected by an export cable this, the upstream connections + made from the export cable will not be considered upstream connections and therefore + shutdown when a downstream substation has a failure +- Array cables + - Similar to substations, any failure or maintenance with an `operation_reduction` of + 1 will turn off every connected upstream cable and turbine on the same string +- Export Cables + - Similar to array cables, any failure or maintenance with an `operation_reduction` of + 1 will turn off every connected upstream substation and string of cable(s) and + turbine(s) + - As noted in the substation section, export cables connecting substations act + independently and not as a string of connected systems + - The final export cable connecting a substation to the interconnection point can + however shut down the entire wind farm + +### Repair Modeling + +- Repair Management: see [here](../API/core.md#repair-management) for complete details + - Generally, repairs and maintenance tasks operate on a first-in, first-out basis with + higher severity level `Maintenance.level` and `Failure.level` being addressed first. + - Servicing equipment, however, can specify if they operate on a "severity" or "turbine" + basis that prioritizes focusing on either the highest serverity level first, or + a single system first, respectively. +- Servicing Equipment: see [here](../API/core.md#servicing-equipment) for complete details + - Can either be modeled on a scheduled basis, such as a year-round schedule for onsite + equipment or equipment with an annual visit schedule during safe weather, or on + an unscheduled basis with a threshold for number of submitted repair requests or + farm operating level. + - Mobilizations can be modeled with a fixed number of days to get to site and a flat + cost for both scheduled and unscheduled servicing equipment. For scheduled equipment, + the mobilization is scheduled so that the equipment arrives at the site for the + start of it's first scheduled day. + - A wide range of generalized capabilities can be specified for various modeling + scenarios. + - RMT: remote (no actual equipment BUT no special implementation) + - DRN: drone + - CTV: crew transfer vessel/vehicle + - SCN: small crane (i.e., field support vessel) + - LCN: large crane (i.e., heavy lift vessel) + - CAB: cabling vessel/vehicle + - DSV: diving support vessel + - Operating limits can be applied for both transiting and repair logic to ensure site + safety is considered in the model. +- Ports + - Currently only used for tow-to-port repairs or tugboat based repairs, which adds the + following additional capabilities: + - TOW: tugboat or towing equipment + - AHV: anchor handling vessel (tugboat that doesn't trigger tow-to-port) + - See the [API docs](../API/core.md#port) for more details + +## Examples and Validation Work + +[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/WISDEM/WOMBAT/main?filepath=examples) + +Below are a few examples to get started, for users interested in the validation work in +the [code-to-code comparison presentations](../presentations.md#code-to-code-comparison), +the notebooks generating [the most up-to-date results can be found in the main repository](https://github.com/WISDEM/WOMBAT/examples/), where there is a separate analysis +for the +[Dinwoodie, et. al, 2015 comparison](https://github.com/WISDEM/WOMBAT/blob/main/examples/dinwoodie_validation.ipynb), +and for the [IEA Task 26, 2016 comparison](https://github.com/WISDEM/blob/main/WOMBAT/examples/iea_26_validation.ipynb). + +```{toctree} +:maxdepth: 1 +:caption: Examples + +how_to +strategy_demonstration +metrics_demonstration +examples_reference +``` diff --git a/_sources/examples/metrics_demonstration.md.txt b/_sources/examples/metrics_demonstration.md.txt new file mode 100644 index 00000000..efdb85f9 --- /dev/null +++ b/_sources/examples/metrics_demonstration.md.txt @@ -0,0 +1,568 @@ +--- +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Demonstration of the Available Metrics + +For a complete list of metrics and their documentation, please see the API Metrics [documentation](../API/simulation_api.md#metrics-computation). + +This demonstration will rely on the results produced in the "How To" notebook and serves as an extension of the API documentation to show what the results will look like depending on what inputs are provided. + + +```{code-cell} ipython3 +:tags: ["output_scroll"] +from pprint import pprint +from functools import partial + +import pandas as pd +from pandas.io.formats.style import Styler + +from wombat.core import Simulation, Metrics + +# Clean up the aesthetics for the pandas outputs +pd.set_option("display.max_rows", 30) +pd.set_option("display.max_columns", 10) +style = partial( + Styler, + table_attributes='style="font-size: 14px; grid-column-count: 6"', + precision=2, + thousands=",", +) +``` + +## Table of Contents + +Below is a list of top-level sections to demonstrate how to use WOMBAT's `Metrics` class methods and an explanation of each individual metric. + +If you don't see a metric or result computation that is core to your work, please submit +an [issue](https://github.com/WISDEM/WOMBAT/issues/new) with details on what the metric +is, and how it should be computed. + + - [Setup](#setup): Running a simulation to gather the results + - [Common Parameters](#common-parameter-explanations): Explanation of frequently used parameter settings + - [Availability](#availability): Time-based and energy-based availability + - [Capacity Factor](#capacity-factor): Gross and net capacity factor + - [Task Completion Rate](#task-completion-rate): Task completion metrics + - [Equipment Costs](#equipment-costs): Cost breakdowns by servicing equipment + - [Service Equipment Utilization Rate](#service-equipment-utilization-rate): Utilization of servicing equipment + - [Vessel-Crew Hours at Sea](#vessel-crew-hours-at-sea): Number of crew or vessel hours spent at sea + - [Number of Tows](#number-of-tows): Number of tows breakdowns + - [Labor Costs](#labor-costs): Breakdown of labor costs + - [Equipment and Labor Costs](#equipment-and-labor-costs): Combined servicing equipment and labor cost breakdown + - [Emissions](#emissions): Emissions of servicing equipment based on activity + - [Component Costs](#component-costs): Materials costs + - [Fixed Cost Impacts](#fixed-cost-impacts): Total fixed costs + - [OpEx](#opex): Project OpEx + - [Process Times](#process-times): Timing of various stages of repair and maintenance + - [Power Production](#power-production): Potential and actually produced power + - [Net Present Value](#net-present-value): Project NPV calculator + +(setup)= +## Setup + +The simulations from the How To notebook are going to be rerun as it is not recommended to create a Metrics class from scratch due to the +large number of inputs that are required and the initialization is provided in the simulation API's run method. + +To simplify this process, a feature has been added to save the simulation outputs required to generate the Metrics inputs and a method to reload those outputs as inputs. + +```{code-cell} ipython3 +:tags: ["output_scroll"] +sim = Simulation("DINWOODIE", "base.yaml") + +# Both of these parameters are True by default for convenience +sim.run(create_metrics=True, save_metrics_inputs=True) + +# Load the metrics data +fpath = sim.env.metrics_input_fname.parent +fname = sim.env.metrics_input_fname.name +metrics = Metrics.from_simulation_outputs(fpath, fname) + +# Delete the log files now that they're loaded in +sim.env.cleanup_log_files() + +# Alternatively, in this case because the simulation was run, we can use the +# following for convenience convenience only +metrics = sim.metrics +``` + +## Common Parameter Explanations + +Before diving into each and every metric, and how they can be customized, it is is worth noting some of the most common parameters used throughout, and their meanings to reduce redundancy. The varying output forms are demonstrated in the [availability](#availability) section below. + +### `frequency` + + project + : Computed across the whole simulation, with the resulting `DataFrame` having an empty index. + + annual + : Summary of each year in the simulation, with the resulting `DataFrame` having "year" as the index. + + monthly + : Summary of each month of the year, aggregated across years, with the resulting `DataFrame` having "month" as the index. + + month-year + : computed on a month-by-year basis, producing the results for every month of the simulation, with the resulting `DataFrame` having "year" and "month" as the index. + +### `by` + + windfarm + : Aggregated across all turbines, with the resulting `DataFrame` having only "windfarm" as a column + + turbine + : Computed for each turbine, with the resulting `DataFrame` having a column for each turbine + +## Availability + +There are two methods to produce availability, which have their own function calls: + - energy: actual power produced divided by potential power produced + - {py:meth}`wombat.core.post_processor.Metrics.production_based_availability` + - time: The ratio of all non-zero hours to all hours in the simulation, or the proportion of the simulation where turbines are operating + - {py:meth}`wombat.core.post_processor.Metrics.time_based_availability` + +Here, we will go through the various input definitions to get time-based availability data as both methods use the same inputs, and provide outputs in the same format. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations) options: "project", "annual", "monthly", and "month-year" +- `by`, as explained [above](#common-parameter-explanations) options: "windfarm" and "turbine" + + +Below is a demonstration of the variations on `frequency` and `by` for `time_based_availability`. + +```{code-cell} ipython3 +:tags: ["output_scroll"] +style(metrics.time_based_availability(frequency="project", by="windfarm")) +``` + +```{code-cell} ipython3 +:tags: ["output_scroll"] +style(metrics.production_based_availability(frequency="project", by="windfarm")) +``` + +Note that in the two above examples, that the values are equal. This is due to the fact that the example simulation does not have any operating reduction applied to failures, unless it's a catastrophic failure, so +there is be no expected difference. + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Demonstrate the by turbine granularity +style(metrics.time_based_availability(frequency="project", by="turbine")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Demonstrate the annualized outputs +style(metrics.time_based_availability(frequency="annual", by="windfarm")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Demonstrate the month aggregations +style(metrics.time_based_availability(frequency="monthly", by="windfarm")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Demonstrate the granular monthly reporting +style(metrics.time_based_availability(frequency="month-year", by="windfarm")) +``` + + +## Capacity Factor + +The capacity factor is the ratio of actual (net) or potential (gross) energy production divided by the project's capacity. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.capacity_factor`. + +**Inputs**: + +- `which` + - "net": net capcity factor, actual production divided by the plant capacity + - "gross": gross capacity factor, potential production divided by the plant capacity +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by`, as explained [above](#common-parameter-explanations), options: "windfarm" and "turbine" + +**Example Usage**: + + +```{code-cell} ipython3 +:tags: ["output_scroll"] +net_cf = metrics.capacity_factor(which="net", frequency="project", by="windfarm").values[0][0] +gross_cf = metrics.capacity_factor(which="gross", frequency="project", by="windfarm").values[0][0] +print(f" Net capacity factor: {net_cf:.2%}") +print(f"Gross capacity factor: {gross_cf:.2%}") +``` + +## Task Completion Rate + +The task completion rate is the ratio of tasks completed aggregated to the desired `frequency`. It is possible to have a >100% completion rate if all maintenance and failure requests submitted in a time period were completed in addition to those that went unfinished in prior time periods. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.task_completion_rate`. + +**Inputs**: + +- `which` + - "scheduled": scheduled maintenance only (classified as maintenace tasks in inputs) + - "unscheduled": unscheduled maintenance only (classified as failure events in inputs) + - "both": Combined completion rate for all tasks +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +scheduled = metrics.task_completion_rate(which="scheduled", frequency="project").values[0][0] +unscheduled = metrics.task_completion_rate(which="unscheduled", frequency="project").values[0][0] +combined = metrics.task_completion_rate(which="both", frequency="project").values[0][0] +print(f" Scheduled Task Completion Rate: {scheduled:.2%}") +print(f"Unscheduled Task Completion Rate: {unscheduled:.2%}") +print(f" Overall Task Completion Rate: {combined:.2%}") +``` + +## Equipment Costs + +Sum of the costs associated with a simulation's servicing equipment, which excludes materials, downtime, etc. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.equipment_costs`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by_equipment` + - `True`: Aggregates all equipment into a single cost + - `False`: Computes for each unit of servicing equipment + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project total at the whole wind farm level +style(metrics.equipment_costs(frequency="project", by_equipment=False)) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals at servicing equipment level +style(metrics.equipment_costs(frequency="project", by_equipment=True)) +``` + +## Service Equipment Utilization Rate + +Ratio of days when the servicing equipment is in use (not delayed for a whole day due to either weather or lack of repairs to be completed) to the number of days it's present in the simulation. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.service_equipment_utilization`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project" and "annual" + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals +style(metrics.service_equipment_utilization(frequency="project")) +``` + +## Vessel-Crew Hours at Sea + +The number of vessel hours or crew hours at sea for offshore wind power plant simulations. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.vessel_crew_hours_at_sea`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by_equipment` + - `True`: Aggregates all equipment into a single cost + - `False`: Computes for each unit of servicing equipment +- `vessel_crew_assumption`: A dictionary of vessel names (`ServiceEquipment.settings.name`, but also found at `Metrics.service_equipment_names`) and the number of crew onboard at any given time. The application of this assumption transforms the results from vessel hours at sea to crew hours at sea. + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project total, not broken out by vessel +style(metrics.vessel_crew_hours_at_sea(frequency="project", by_equipment=False)) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Annual project totals, broken out by vessel +style(metrics.vessel_crew_hours_at_sea(frequency="annual", by_equipment=True)) +``` + +## Number of Tows + +The number of tows performed during the simulation. If tow-to-port was not used in the simulation, a DataFrame with a single value of 0 will be returned. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.number_of_tows`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by_tug` + - `True`: Computed for each tugboat (towing vessel) + - `False`: Aggregates all the tugboats +- `by_direction` + - `True`: Computed for each direction a tow was performed (to port or to site) + - `False`: Aggregates to the total number of tows + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project Total +# NOTE: This example has no towing, so it will return 0 +style(metrics.number_of_tows(frequency="project")) +``` + +## Labor Costs + +Sum of all labor costs associated with servicing equipment, excluding the labor defined in the fixed costs, which can be broken out by type. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.labor_costs`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by_type` + - `True`: Computed for each labor type (salary and hourly) + - `False`: Aggregates all the labor costs + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project total at the whole wind farm level +total = metrics.labor_costs(frequency="project", by_type=False) +print(f"Project total: ${total.values[0][0] / metrics.project_capacity:,.2f}/MW") +``` + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals for each type of labor +# NOTE: this simulation relies on using a fixed labor cost, so this is still $0 +style(metrics.labor_costs(frequency="project", by_type=True)) +``` + +## Equipment and Labor Costs + +Sum of all labor and servicing equipment costs, excluding the labor defined in the fixed costs, which can be broken out by each category. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.equipment_labor_cost_breakdown`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by_category` + - `True`: Computed for each unit servicing equipment and labor category + - `False`: Aggregated to the sum of all costs + +`reason` definitions: + - Maintenance: routine maintenance, or events defined as a {py:class}`wombat.core.data_classes.Maintenance` + - Repair: unscheduled maintenance, ranging from inspections to replacements, or events defined as a {py:class}`wombat.core.data_classes.Failure` + - Mobilization: Cost of mobilizing servicing equipment + - Crew Transfer: Costs incurred while crew are transfering between a turbine or substation and the servicing equipment + - Site Travel: Costs incurred while transiting to/from the site and while at the site + - Weather Delay: Any delays caused by unsafe weather conditions + - No Requests: Equipment and labor is active, but there are no repairs or maintenance tasks to be completed + - Not in Shift: Any time outside of the operating hours of the wind farm (or the servicing equipment's specific operating hours) + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals +style(metrics.equipment_labor_cost_breakdowns(frequency="project", by_category=False)) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals by each category +style(metrics.equipment_labor_cost_breakdowns(frequency="project", by_category=True)) +``` + +## Emissions + +Emissions (tons or other provided units) of all servicing equipment activity, except overnight waiting periods between shifts. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.emissions`. + +**Inputs**: + +- `emissions_factors`: Dictionary of servicing equipment names and the emissions per hour of the following activities: `transit`, `maneuvering`, `idle at site`, and `idle at port`, where port is stand-in for wherever the servicing equipment might be based when not at site. +- `maneuvering_factor`: The proportion of transit time that can generally be associated with positioning servicing, by default 10%. +- `port_engine_on_factor`: The proportion of the idling at port time where the engine is running and producing emissions, by default 25%. + +```{code-cell} ipython3 +# Create the emissions factors, in tons per hour +emissions_factors = { + "Crew Transfer Vessel 1": { + "transit": 4, + "maneuvering": 3, + "idle at site": 0.5, + "idle at port": 0.25, + }, + "Crew Transfer Vessel 2": { + "transit": 4, + "maneuvering": 3, + "idle at site": 0.5, + "idle at port": 0.25, + }, + "Crew Transfer Vessel 3": { + "transit": 4, + "maneuvering": 3, + "idle at site": 0.5, + "idle at port": 0.25, + }, + "Field Support Vessel": { + "transit": 6, + "maneuvering": 4, + "idle at site": 1, + "idle at port": 0.5, + }, + "Heavy Lift Vessel": { + "transit": 12, + "maneuvering": 7, + "idle at site": 1, + "idle at port": 0.5, + }, +} + +style(metrics.emissions(emissions_factors=emissions_factors, maneuvering_factor=0.075, port_engine_on_factor=0.20)) +``` + +## Component Costs + +All of the costs associated with maintenance and failure events during the simulation, including delays incurred during the repair process, but excluding costs not directly tied to a repair. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.component_costs`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by_category` + - `True`: Computed across each cost category + - `False`: Aggregated to the sum of all categories +- `by_action` + - `True`: Computed by each of "repair", "maintenance", and "delay", and is included in the MultiIndex + - `False`: Aggregated as the sum of all actions + +`action` definitions: + - maintenance: routine maintenance + - repair: unscheduled maintenance, ranging from inspections to replacements + - delay: Any delays caused by unsafe weather conditions or not being able to finish a process within a single shift + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals by component +style(metrics.component_costs(frequency="project", by_category=False, by_action=False)) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals by each category and action type +style(metrics.component_costs(frequency="project", by_category=True, by_action=True)) +``` + +## Fixed Cost Impacts + +Computes the total costs of the fixed costs categories. For further documentation, see the defintion docs, here: {py:class}`wombat.core.data_classes.FixedCosts`, or the API docs here: {py:meth}`wombat.core.post_processor.Metrics.fixed_costs`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `resolution` (also, demonstrated below) + - "high": Computed across the most granular cost levels + - "medium": Computed for each general cost category + - "low": Aggregated to a single sum of costs + +```{code-cell} ipython3 +:tags: ["output_scroll"] +pprint(metrics.fixed_costs.hierarchy) +``` + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals at the highest level +# NOTE: there were no fixed costs defined in this example, so all values will be 0, so +# this will just be demonstrating the output format +style(metrics.project_fixed_costs(frequency="project", resolution="low")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals at the medium level +style(metrics.project_fixed_costs(frequency="project", resolution="medium")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals at the lowest level +style(metrics.project_fixed_costs(frequency="project", resolution="high")) +``` + +## OpEx + +Computes the total cost of all operating expenditures for the duration of the simulation, including fixed costs. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.opex`. +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" + +- `by_category` + - `True` shows the port fees, fixed costs, labor costs, equipment costs, and materials costs in addition the total OpEx + - `False` shows only the total OpEx + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +style(metrics.opex("annual")) +``` + +```{code-cell} ipython3 +:tags: ["output_scroll"] +style(metrics.opex("annual", by_category=True)) +``` + +## Process Times + +Computes the total number of hours spent from repair request submission to completion, performing repairs, and the number of rquest for each repair category. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.process_times`. + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +style(metrics.process_times()) +``` + +## Power Production + +Computes the total power production for the wind farm. For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.power_production`. +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `by`, as explained [above](#common-parameter-explanations) options: "windfarm" and "turbine" +- `units` + - "kwh": kilowatt-hours (kWh) + - "mwh": megawatt-hours (MWh) + - "gwh": gigawatt-hours (GWh) + +**Example Usage**: + +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals, in kWh, at the wind farm level +style(metrics.power_production(frequency="project", by="windfarm", units="kwh")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals, in MWh, at the wind farm level +style(metrics.power_production(frequency="project", units="mwh")) +``` +```{code-cell} ipython3 +:tags: ["output_scroll"] +# Project totals, in GWh, at the wind farm level +style(metrics.power_production(frequency="project")) +``` + +## Net Present Value + +Calcualtes the net present value (NPV) for the project, as $NPV = (Power * OfftakePrice - OpEx) / (1 + DiscountRate)$. + +For further documentation, see the API docs here: {py:meth}`wombat.core.post_processor.Metrics.npv`. + +**Inputs**: + +- `frequency`, as explained [above](#common-parameter-explanations), options: "project", "annual", "monthly", and "month-year" +- `discount_rate`: The rate of return that could be earned on alternative investments, by default 0.025. +- `offtake_price`: Price of energy, per MWh, by default 80. + + +```{code-cell} ipython3 +:tags: ["output_scroll"] +style(metrics.opex("annual")) +``` diff --git a/_sources/examples/strategy_demonstration.md.txt b/_sources/examples/strategy_demonstration.md.txt new file mode 100644 index 00000000..ae73901a --- /dev/null +++ b/_sources/examples/strategy_demonstration.md.txt @@ -0,0 +1,182 @@ +--- +jupytext: + formats: md:myst + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Servicing Strategies + +In this example, we'll demonstrate the essential differences in scheduled servicing, unscheduled servicing, and tow-to-port repair repair strategies. Each of the examples demonstrated below will be based on the 2015 Dinwoodie, et al. paper, though the the variations will be for demonstration purposes only. + + +## WOMBAT Setup and Variables + +The vessels that will be changed in this demonstration are the field support vessel (FSV) with capability: "SCN", the heavy lift vessel (HLV) with capability: "LCN", and the tugboats, which have capability: "TOW". + +```{note} +When running tow-to-port a `Port` configuration is also required, which will control the tugboats. However, the port costs will still be accounted for in the +`FixedCosts` class as port fees are assumed to be constant. These costs are not considered in this example, so differences in cost should be taken with a grain +of salt given the reduction of HLV and FSV operational and mobilization costs. +``` + + +Scenario descriptions: +- **Scheduled**: exactly the same as the base case (fsv_scheduled.yaml and hlv_scheduled.yaml) +- **Unscheduled: requests**: the FSV and HLV are called to site when 10 requests that + they can service are logged (fsv_requests.yaml and hlv_requests.yaml) +- **Unscheduled: downtime**: the FSV and HLV are called to site once the wind farm's + operating level hits 90% or lower (fsv_downtime.yaml and hlv_downtime.yaml) + - **Unscheduled: tow-to-port**: the FSV and HLV will be replaced with three identical + tugboats (tugboat1.yaml, tugboat2.yaml, tugboat3.yaml), and all the failures associated + with the FSV and HLV will be changed to capability "TOW" to trigger the tow-to-port + repairs. These processes will be triggered on the first request (WOMBAT base + assumption that can't be changed for now). + +In this example, we will demonstrate how the results for the base case for the Dinwoodie, et al. example vary based on how each of the vessels are scheduled. The configuration details all remain the same, regardless of details, except for the strategy information, which is defined as follows: + +This example is set up similarly to that of the validation cases to show how the results differ, and not a step-by-step guide for setting up the analyses. We refer the reader to the exensive [documentation](../API/index.md) and [How To example](how_to.md) for more information. + +## Imports and notebook configuration + +```{code-cell} ipython3 +from copy import deepcopy +from time import perf_counter + +import pandas as pd + +from wombat.core import Simulation +from wombat.core.library import DINWOODIE + +pd.set_option("display.max_rows", 1000) +pd.set_option("display.max_columns", 1000) +pd.options.display.float_format = '{:,.2f}'.format +``` + +## Simulation and results setup + +Here we're providing the names of the configuration files (found at: dinwoodie / config) +without their .yaml extensions (added in later) and the results that we want to compare +between simulations to understand some of the timing and cost trade offs between +simulations. + +The dictionary of keys and lists will be used to create the results data frame where the +keys will be the indices and the lists will be the row values for each of the above +configurations. + +```{code-cell} ipython3 +configs = [ + "base_scheduled", + "base_requests", + "base_downtime", + "base_tow_to_port", +] + +columns = deepcopy(configs) # Create a unique copy of the config names for column naming +results = { + "availability - time based": [], + "availability - production based": [], + "capacity factor - net": [], + "capacity factor - gross": [], + "power production": [], + "task completion rate": [], + "annual direct O&M cost": [], + "annual vessel cost": [], + "ctv cost": [], + "fsv cost": [], + "hlv cost": [], + "tow cost": [], + "annual repair cost": [], + "annual technician cost": [], + "ctv utilization": [], + "fsv utilization": [], + "hlv utilization": [], + "tow utilization": [], +} +``` + +## Run the simulations and display the results + +```{code-cell} ipython3 +timing_df = pd.DataFrame([], columns=["Load Time (min)", "Run Time (min)"], index=configs) +timing_df.index.name = "Scenario" + +for config in configs: + + # Load the simulation + start = perf_counter() + sim = Simulation(DINWOODIE , f"{config}.yaml") + end = perf_counter() + timing_df.loc[config, "Load Time (min)"] = (end - start) / 60 + + # Run the simulation + start = perf_counter() + sim.run() + end = perf_counter() + timing_df.loc[config, "Run Time (min)"] = (end - start) / 60 + + # Gather the results of interest + years = sim.metrics.events.year.unique().shape[0] + mil = 1000000 + + # Gather the high-level results for the simulation + availability_time = sim.metrics.time_based_availability(frequency="project", by="windfarm") + availability_production = sim.metrics.production_based_availability(frequency="project", by="windfarm") + cf_net = sim.metrics.capacity_factor(which="net", frequency="project", by="windfarm") + cf_gross = sim.metrics.capacity_factor(which="gross", frequency="project", by="windfarm") + power_production = sim.metrics.power_production(frequency="project", by="windfarm") + completion_rate = sim.metrics.task_completion_rate(which="both", frequency="project") + parts = sim.metrics.events[["materials_cost"]].sum().sum() + techs = sim.metrics.project_fixed_costs(frequency="project", resolution="low").operations[0] + total = sim.metrics.events[["total_cost"]].sum().sum() + + # Gather the equipment costs and separate the results by equipment type + equipment = sim.metrics.equipment_costs(frequency="project", by_equipment=True) + equipment_sum = equipment.sum().sum() + hlv = equipment[[el for el in equipment.columns if "Heavy Lift Vessel" in el]].sum().sum() + fsv = equipment[[el for el in equipment.columns if "Field Support Vessel" in el]].sum().sum() + ctv = equipment[[el for el in equipment.columns if "Crew Transfer Vessel" in el]].sum().sum() + tow = equipment[[el for el in equipment.columns if "Tugboat" in el]].sum().sum() + + # Gather the equipment utilization data frame and separate the results by equipment type + utilization = sim.metrics.service_equipment_utilization(frequency="project") + hlv_ur = utilization[[el for el in utilization.columns if "Heavy Lift Vessel" in el]].mean().mean() + fsv_ur = utilization[[el for el in utilization.columns if "Field Support Vessel" in el]].mean().mean() + ctv_ur = utilization[[el for el in utilization.columns if "Crew Transfer Vessel" in el]].mean().mean() + tow_ur = utilization[[el for el in utilization.columns if "Tugboat" in el]].mean().mean() + + # Log the results of interest + results["availability - time based"].append(availability_time.values[0][0]) + results["availability - production based"].append(availability_production.values[0][0]) + results["capacity factor - net"].append(cf_net.values[0][0]) + results["capacity factor - gross"].append(cf_gross.values[0][0]) + results["power production"].append(power_production.values[0][0]) + results["task completion rate"].append(completion_rate.values[0][0]) + results["annual direct O&M cost"].append((total + techs) / mil / years) + results["annual vessel cost"].append(equipment_sum / mil / years) + results["ctv cost"].append(ctv / mil / years) + results["fsv cost"].append(fsv / mil / years) + results["hlv cost"].append(hlv / mil / years) + results["tow cost"].append(tow / mil / years) + results["annual repair cost"].append(parts / mil / years) + results["annual technician cost"].append(techs / mil / years) + results["ctv utilization"].append(ctv_ur) + results["fsv utilization"].append(fsv_ur) + results["hlv utilization"].append(hlv_ur) + results["tow utilization"].append(tow_ur) + + # Clear the logs + sim.env.cleanup_log_files() + +timing_df +``` + +```{code-cell} ipython3 +results_df = pd.DataFrame(results.values(), columns=columns, index=results.keys()).fillna(0) +results_df +``` diff --git a/_sources/index.rst.txt b/_sources/index.rst.txt new file mode 100644 index 00000000..237a7cc9 --- /dev/null +++ b/_sources/index.rst.txt @@ -0,0 +1,172 @@ +WOMBAT - Windfarm Operations and Maintenance cost-Benefit Analysis Tool +======================================================================= + +.. image:: https://img.shields.io/badge/DOI-10.2172%2F1894867-brightgreen?link=https://doi.org/10.2172/1894867 + :target: https://www.osti.gov/biblio/1894867 + +.. image:: https://badge.fury.io/py/wombat.svg + :target: https://badge.fury.io/py/wombat + +.. image:: https://img.shields.io/badge/License-Apache%202.0-blue.svg + :target: https://opensource.org/licenses/Apache-2.0 + +.. image:: https://mybinder.org/badge_logo.svg + :target: https://mybinder.org/v2/gh/WISDEM/WOMBAT/main?filepath=examples + +.. image:: https://jupyterbook.org/badge.svg + :target: https://wisdem.github.io/WOMBAT + +.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white + :target: https://github.com/pre-commit/pre-commit + :alt: pre-commit + +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + +.. image:: https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336 + :target: https://pycqa.github.io/isort/ + + + +Overview +-------- + +The WOMBAT framework is designed to provide an open source tool adhering to FLOSS principles +for the windfarm lifecycle research community. Specifically, WOMBAT is meant to serve as +a what-if, or scenario-based, simulation tool, so that you can model the trade-offs in +decision making for the operations and maintenance phase of a windfarm. + +As a supplement to this documentation site, there is also an NREL Technical Report that +goes through much of the design and implementation details available at: +https://www.osti.gov/biblio/1894867. If you use this software, please cite it using the +follwing BibTex information, or in commonly used citation formats +`here `_. + +.. code-block:: bibtex + + @techreport{hammond2022wombat, + title = {Windfarm Operations and Maintenance cost-Benefit Analysis Tool (WOMBAT)}, + author = {Hammond, Rob and Cooperman, Aubryn}, + doi = {10.2172/1894867}, + url = {https://www.osti.gov/biblio/1894867}, + place = {United States}, + year = {2022}, + month = {10}, + institution = {National Renewable Energy Lab. (NREL)}, + } + +For any questions, feel free to open up an issue in the repository or email: +rob.hammond@nrel.gov. + +Latest Changes? +--------------- + +As of v0.8, a series of bug fixes in the cable, subassembly, repair management, and +servicing equipment models that ensure repairs can't happen twice under limited +circumstances or that more than one repair can occur simutaneously. New features include +an emissions metric and random seeding of simulations, with significant simulation +speedups across the board due to using Polars for managing the weather and datetime +functionality. + +Please see the CHANGELOG for details! + +* On this site: https://wisdem.github.io/WOMBAT/changelog.html +* On GitHub: https://github.com/WISDEM/WOMBAT/blob/main/CHANGELOG.md + + +The Model in 30 Seconds Or Less +------------------------------- + +In general, the model has 2 overarching branches: the windfarm itself (the technology +strategy), and the simulation environment (the maintenance strategy). For the wind farm +model we can control the varying assets (system in the code)--substations, turbines, and +cables--as well as the components that comprise each asset (subassemblies in the code). +This separation allows for each turbine, cable, or substation component to have its own +unique failure and maintenance models. + +As for the environment, this is where the discrete event simulation itself happens, in +addition to logging, repair logic, and other necessary modeling pieces. The image +below provides a more visual representation of this description. + + +High Level Architecture +^^^^^^^^^^^^^^^^^^^^^^^ + +The code is largely broken up into two categories: the windfarm and objects contained +within it, and the simulation and simulation evironment components. The windfarm is +composed of systems: substation(s), cables, and turbines, and each of those systems is +composed of subassemblies (a conglomerate of components). For the simulation environment, +we consider all of the pieces that allow the simulation to happen such as the API, +servicing equipment, repair manager to hold and pass on tasks, and results post-processing. + +.. image:: images/high_level_diagram.svg + +The following section describes how a windfarm is simulated and the flow of events as +they occur within the model. + +Simulation Architecture +^^^^^^^^^^^^^^^^^^^^^^^ + +In the diagram below, we demonstrate the lifecycle of the simulation through the +lifecycle of a single failure. + +1) The maximum length of the simulation is defined by the amount of wind and wave + timeseries data is provided for the simulation. +2) Each subassemly failure model is a random sampling from a Weibull distribution, so + for the sake of clarity we'll consider this to be a catastrophic drivetrain failure. + When the timeout (time to failure) is reached in the simulation, the subassembly's operating level is + reduced to 0%, and a message is passed to the turbine level (the overarching system + model). +3) From there, the turbine will shut off, and signal to all other subassembly models to + not accrue time for their respective maintenance and failure timeouts. Then, the + turbine creates a repair request and passes that to the repair manager. +4) The repair manager will store the submitted task and depending on the servicing + equipment's maintenance strategy, a crane or vessel will be called to site. +5) The vessel (or crane) will mobilize and accrue time there, all the while, the turbine + and wind power plant will accrue downtime. When the servicing equipment arrives at + the site it will operate according to its operation limits, current weather + conditions, and site/equipment-specific working hours and continue to log costs and + downtime. +6) When the servicing is complete, the subassembly will be placed back to good-as-new + condition and the turbine will be reset to operating. From there all the turbine's + and drivetrain's failure and maintenance models be turned back on, and the simulation + will continue on in the same manner until it reaches it's user- or weather-defined + ending point. + +.. image:: images/simulation_diagram.png + + +Welcome +------- + +.. toctree:: + :maxdepth: 2 + :caption: Getting Started + + install + presentations + workshops/index + team + + + +Using WOMBAT +------------ + +.. toctree:: + :maxdepth: 2 + :caption: Working with the code base + + examples/index + API/index + changelog + + +License +------- + +Notice on the NREL application of the Apache-2 license, also found on the +`GitHub `_, along with the +complete `license `_ details. + +.. include:: ../../NOTICE diff --git a/_sources/install.md.txt b/_sources/install.md.txt new file mode 100644 index 00000000..b0d689e1 --- /dev/null +++ b/_sources/install.md.txt @@ -0,0 +1,103 @@ +# Installation + + +## Create a conda environoment + +Download the latest version of [Miniconda]() +for the appropriate OS. Follow the remaining [steps]() +for the appropriate OS version. + + +Using conda, create a new virtual environment, replacing `` with a name +of your choosing (without spaces): +```text +$ conda create -n python=3 +$ conda activate +``` +You can now use ``conda activate `` to enter the environment and +``conda deactivate`` to exit the environment. + + +## Installing + +```{note} +The folowing all asssume you are in your `wombat` environment! +``` + + +### Pip + +WOMBAT is listed on PyPI under `wombat`, and can be installed for users that don't +intend to modify the code, by running the following: + +```text +$ pip install wombat +``` + +### From Source + +If you plan to read through the code, in addition to running simulations, it is +recommended to clone repository, and install the local version. + +```{note} +It has been noted that some Windows users have had issues with accessing the included +data libraries using the following, so if issues arise, uninstall wombat, and reinstall +using the `pip install -e '.[dev]'` prompt in the next section +``` + +```text +$ git clone https://github.com/WISDEM/WOMBAT.git +$ cd wombat +$ pip install wombat/ +``` + + +### For contributors: + +Anyone seeking to work modify the code and contribute to the repository should follow +the below prompt. This repository relies on automatic code formatting and linting +provided through the pre-commit framework, and any contributors should having this +functionality run and pass before submitting pull requests. + +```text +$ git clone https://github.com/WISDEM/WOMBAT.git +$ cd wombat +$ pip install -e '.[dev]' # some users may need double quotes here, not single quotes +# Required for automatic code formatting! +$ pre-commit install +``` + +#### Running the tests + +In addition, pytest is used to manage the testing framework, and can be run using the +following to ensure that contributions don't break the existing tests. This will produce +the results of the tests, including code coverage results, if the tests pass. + +```text +# From the top level of the repository +$ pytest tests/ +``` + + +### For documentation: + +Additionally, for users that wish to modify the documentation or build the documentation +site locally, the following prompt with will install the required documentation building +packages. + +```text +$ git clone https://github.com/WISDEM/WOMBAT.git +$ cd wombat +$ pip install -e '.[docs]' +``` + +#### Build the documentation site + +```text +$ cd docs/ +$ sphinx-build -b html source _build +$ make html +``` + +Now, the documentation site should be able to be viewed locally at +docs/_build/html/index.html diff --git a/_sources/presentations.md.txt b/_sources/presentations.md.txt new file mode 100644 index 00000000..13a6178c --- /dev/null +++ b/_sources/presentations.md.txt @@ -0,0 +1,56 @@ +# Presentations and Publications +## Publications +[![DOI 10.2172/1894867](https://img.shields.io/badge/DOI-10.2172%2F1894867-brightgreen?link=https://doi.org/10.2172/1894867)](https://www.osti.gov/biblio/1894867) + +Please cite our below publication if you use this software in your studies! + +Hammond, Rob, & Cooperman, Aubryn. Windfarm Operations and Maintenance cost-Benefit Analysis Tool (WOMBAT). United States. https://doi.org/10.2172/1894867 + +```bibtex +@techreport{hammond2022wombat, + title = {Windfarm Operations and Maintenance cost-Benefit Analysis Tool (WOMBAT)}, + author = {Hammond, Rob and Cooperman, Aubryn}, + doi = {10.2172/1894867}, + url = {https://www.osti.gov/biblio/1894867}, + place = {United States}, + year = {2022}, + month = {10}, + institution = {National Renewable Energy Lab. (NREL)}, +} +``` + +## 2023 NAWEA WindTech Workshop + +This document is the actual presentation given in associating with the conference +workshop. Please also see the [workshop's homepage](/workshops/nawea_wind_tech_2023). + +Link coming soon! + + +## FY20 DOE Presentation + +This document is a link to the Fiscal Year 2020 DOE presentation on the first prototype +of the model. +
+{download}`presentation PDF <../../presentation_material/operations_and_maintenance_model_FY20.pdf>` + +## Code-to-Code Comparison + +This document serves as a demonstration of the capabilities of this work in reference to +two major works to document simulated O&M results from various industry and research +software systems. Here we compare our software to the published results from +Dinwoodie, et. al, 2015 [^dinwoodie2015reference] and IEA, 2016 [^smart2016iea]. +
+{download}`presentation PDF <../../presentation_material/code_comparison.pdf>` + + +## IEA Task 26 Presentation + +This presentation was given to the IEA Task Force responsible for [^smart2016iea] +as a demonstration of NREL's efforts on O&M model development. +
+{download}`presentation PDF <../../presentation_material/WOMBAT_IEA_task_26_presentation_6_May_2021.pdf>` + +[^dinwoodie2015reference]: Iain Dinwoodie, Ole-Erik V Endrerud, Matthias Hofmann, Rebecca Martin, and Iver Bakken Sperstad. Reference cases for verification of operation and maintenance simulation models for offshore wind farms. *Wind Engineering*, 39(1):1–14, 2015. +[^smart2016iea]: Gavin Smart, Aaron Smith, Ethan Warner, Iver Bakken Sperstad, Bob Prinsen, and Roberto Lacal-Arantegui. Iea wind task 26: offshore wind farm baseline documentation. Technical Report, National Renewable Energy Lab.(NREL), Golden, CO (United States), 2016. diff --git a/_sources/team.md.txt b/_sources/team.md.txt new file mode 100644 index 00000000..ed4f8e51 --- /dev/null +++ b/_sources/team.md.txt @@ -0,0 +1,22 @@ +# Team + +All members are assumed NREL employees, unless otherwise noted. + +## Software + - Rob Hammond + - Alicia Key + +## Methodology + - Rob Hammond + - Aubryn Cooperman + - Aaron Barker + - Annika Eberle + - Matt Shields + - Shawn Sheng + - Owen Roberts + - Alicia Key + +## Leadership + - Rob Hammond + - Annika Eberle + - Matt Shields diff --git a/_sources/workshops/index.md.txt b/_sources/workshops/index.md.txt new file mode 100644 index 00000000..061b4a33 --- /dev/null +++ b/_sources/workshops/index.md.txt @@ -0,0 +1,10 @@ +# Workshops + +This is the homepage for all workshops relating to WOMBAT. For now, there is just one, for the 2023 NAWEA WindTech Conference. + +```{toctree} +:maxdepth: 1 +:caption: Workshops + +nawea_wind_tech_2023 +``` \ No newline at end of file diff --git a/_sources/workshops/nawea_wind_tech_2023.md.txt b/_sources/workshops/nawea_wind_tech_2023.md.txt new file mode 100644 index 00000000..5da643ee --- /dev/null +++ b/_sources/workshops/nawea_wind_tech_2023.md.txt @@ -0,0 +1,59 @@ +# NAWEA WindTech 2023 WOMBAT Tutorial + +## What You'll Need + +- [COREWIND Floating Wind Description](https://corewind.eu/wp-content/uploads/files/publications/COREWIND-D6.1-General-frame-of-the-analysis-and-description-of-the-new-FOW-assessment-app.pdf) +- [COREWIND Floating Wind O&M Strategies Assessment](https://corewind.eu/wp-content/uploads/files/publications/COREWIND-D4.2-Floating-Wind-O-and-M-Strategies-Assessment.pdf) +- Code editor (VSCode is what I use and therefore recommend) +- Working python environment (Miniconda is my preference because it's lightweight) + - Anaconda: https://docs.anaconda.com/free/anaconda/install + - Miniconda: https://docs.conda.io/projects/miniconda/en/latest/miniconda-install.html +- Basic git proficiency (we just need to clone a project and change branches) +- Basic terminal proficiency (or Windows alternative) +- Basic Python proficiency (WOMBAT doesn't requires very little code, but you still need to use Python) + +## Slides and Data + +Just note that the spoken commentary is not included, but the materials to drive that +content are all included in the slides with links to the appropriate documentation pages +and the relevant screenshots so that participants can track down the required data in +the COREWIND publications more easily. + + +- Presentation coming soon! +- The accompanying example notebook is + [here](https://github.com/WISDEM/WOMBAT/blob/main/examples/NAWEA_interactive_walkthrough.ipynb). +- Example dataset for the Morro Bay, California, USA 9D layout with connected + substations is [here](https://github.com/WISDEM/WOMBAT/blob/main/library/corewind/). + +## Pre-Workshop Setup + +1. Create a new Python environment (conda instructtions) + + ``` + conda create -n wombat_workshop python=3.10 + conda config --set pip_interop_enabled true + ``` + +2. Download WOMBAT from GitHub + + ``` + git clone git clone https://github.com/WISDEM/WOMBAT.git + ``` + +3. Install WOMBAT as an editable package + + ``` + conda activate wombat_workshop # or what you called in the first step + cd wombat/ + pip install -e . + ``` + +4. Ensure that it all worked (assuming no error messages at any of the prior stages) + + ``` + python + + >>> import wombat + >>> wombat.__version__ + ``` diff --git a/_static/basic.css b/_static/basic.css new file mode 100644 index 00000000..d54be806 --- /dev/null +++ b/_static/basic.css @@ -0,0 +1,906 @@ +/* + * basic.css + * ~~~~~~~~~ + * + * Sphinx stylesheet -- basic theme. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/* -- main layout ----------------------------------------------------------- */ + +div.clearer { + clear: both; +} + +div.section::after { + display: block; + content: ''; + clear: left; +} + +/* -- relbar ---------------------------------------------------------------- */ + +div.related { + width: 100%; + font-size: 90%; +} + +div.related h3 { + display: none; +} + +div.related ul { + margin: 0; + padding: 0 0 0 10px; + list-style: none; +} + +div.related li { + display: inline; +} + +div.related li.right { + float: right; + margin-right: 5px; +} + +/* -- sidebar --------------------------------------------------------------- */ + +div.sphinxsidebarwrapper { + padding: 10px 5px 0 10px; +} + +div.sphinxsidebar { + float: left; + width: 270px; + margin-left: -100%; + font-size: 90%; + word-wrap: break-word; + overflow-wrap : break-word; +} + +div.sphinxsidebar ul { + list-style: none; +} + +div.sphinxsidebar ul ul, +div.sphinxsidebar ul.want-points { + margin-left: 20px; + list-style: square; +} + +div.sphinxsidebar ul ul { + margin-top: 0; + margin-bottom: 0; +} + +div.sphinxsidebar form { + margin-top: 10px; +} + +div.sphinxsidebar input { + border: 1px solid #98dbcc; + font-family: sans-serif; + font-size: 1em; +} + +div.sphinxsidebar #searchbox form.search { + overflow: hidden; +} + +div.sphinxsidebar #searchbox input[type="text"] { + float: left; + width: 80%; + padding: 0.25em; + box-sizing: border-box; +} + +div.sphinxsidebar #searchbox input[type="submit"] { + float: left; + width: 20%; + border-left: none; + padding: 0.25em; + box-sizing: border-box; +} + + +img { + border: 0; + max-width: 100%; +} + +/* -- search page ----------------------------------------------------------- */ + +ul.search { + margin: 10px 0 0 20px; + padding: 0; +} + +ul.search li { + padding: 5px 0 5px 20px; + background-image: url(file.png); + background-repeat: no-repeat; + background-position: 0 7px; +} + +ul.search li a { + font-weight: bold; +} + +ul.search li p.context { + color: #888; + margin: 2px 0 0 30px; + text-align: left; +} + +ul.keywordmatches li.goodmatch a { + font-weight: bold; +} + +/* -- index page ------------------------------------------------------------ */ + +table.contentstable { + width: 90%; + margin-left: auto; + margin-right: auto; +} + +table.contentstable p.biglink { + line-height: 150%; +} + +a.biglink { + font-size: 1.3em; +} + +span.linkdescr { + font-style: italic; + padding-top: 5px; + font-size: 90%; +} + +/* -- general index --------------------------------------------------------- */ + +table.indextable { + width: 100%; +} + +table.indextable td { + text-align: left; + vertical-align: top; +} + +table.indextable ul { + margin-top: 0; + margin-bottom: 0; + list-style-type: none; +} + +table.indextable > tbody > tr > td > ul { + padding-left: 0em; +} + +table.indextable tr.pcap { + height: 10px; +} + +table.indextable tr.cap { + margin-top: 10px; + background-color: #f2f2f2; +} + +img.toggler { + margin-right: 3px; + margin-top: 3px; + cursor: pointer; +} + +div.modindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +div.genindex-jumpbox { + border-top: 1px solid #ddd; + border-bottom: 1px solid #ddd; + margin: 1em 0 1em 0; + padding: 0.4em; +} + +/* -- domain module index --------------------------------------------------- */ + +table.modindextable td { + padding: 2px; + border-collapse: collapse; +} + +/* -- general body styles --------------------------------------------------- */ + +div.body { + min-width: 450px; + max-width: 800px; +} + +div.body p, div.body dd, div.body li, div.body blockquote { + -moz-hyphens: auto; + -ms-hyphens: auto; + -webkit-hyphens: auto; + hyphens: auto; +} + +a.headerlink { + visibility: hidden; +} + +a.brackets:before, +span.brackets > a:before{ + content: "["; +} + +a.brackets:after, +span.brackets > a:after { + content: "]"; +} + +h1:hover > a.headerlink, +h2:hover > a.headerlink, +h3:hover > a.headerlink, +h4:hover > a.headerlink, +h5:hover > a.headerlink, +h6:hover > a.headerlink, +dt:hover > a.headerlink, +caption:hover > a.headerlink, +p.caption:hover > a.headerlink, +div.code-block-caption:hover > a.headerlink { + visibility: visible; +} + +div.body p.caption { + text-align: inherit; +} + +div.body td { + text-align: left; +} + +.first { + margin-top: 0 !important; +} + +p.rubric { + margin-top: 30px; + font-weight: bold; +} + +img.align-left, figure.align-left, .figure.align-left, object.align-left { + clear: left; + float: left; + margin-right: 1em; +} + +img.align-right, figure.align-right, .figure.align-right, object.align-right { + clear: right; + float: right; + margin-left: 1em; +} + +img.align-center, figure.align-center, .figure.align-center, object.align-center { + display: block; + margin-left: auto; + margin-right: auto; +} + +img.align-default, figure.align-default, .figure.align-default { + display: block; + margin-left: auto; + margin-right: auto; +} + +.align-left { + text-align: left; +} + +.align-center { + text-align: center; +} + +.align-default { + text-align: center; +} + +.align-right { + text-align: right; +} + +/* -- sidebars -------------------------------------------------------------- */ + +div.sidebar, +aside.sidebar { + margin: 0 0 0.5em 1em; + border: 1px solid #ddb; + padding: 7px; + background-color: #ffe; + width: 40%; + float: right; + clear: right; + overflow-x: auto; +} + +p.sidebar-title { + font-weight: bold; +} + +div.admonition, div.topic, blockquote { + clear: left; +} + +/* -- topics ---------------------------------------------------------------- */ + +div.topic { + border: 1px solid #ccc; + padding: 7px; + margin: 10px 0 10px 0; +} + +p.topic-title { + font-size: 1.1em; + font-weight: bold; + margin-top: 10px; +} + +/* -- admonitions ----------------------------------------------------------- */ + +div.admonition { + margin-top: 10px; + margin-bottom: 10px; + padding: 7px; +} + +div.admonition dt { + font-weight: bold; +} + +p.admonition-title { + margin: 0px 10px 5px 0px; + font-weight: bold; +} + +div.body p.centered { + text-align: center; + margin-top: 25px; +} + +/* -- content of sidebars/topics/admonitions -------------------------------- */ + +div.sidebar > :last-child, +aside.sidebar > :last-child, +div.topic > :last-child, +div.admonition > :last-child { + margin-bottom: 0; +} + +div.sidebar::after, +aside.sidebar::after, +div.topic::after, +div.admonition::after, +blockquote::after { + display: block; + content: ''; + clear: both; +} + +/* -- tables ---------------------------------------------------------------- */ + +table.docutils { + margin-top: 10px; + margin-bottom: 10px; + border: 0; + border-collapse: collapse; +} + +table.align-center { + margin-left: auto; + margin-right: auto; +} + +table.align-default { + margin-left: auto; + margin-right: auto; +} + +table caption span.caption-number { + font-style: italic; +} + +table caption span.caption-text { +} + +table.docutils td, table.docutils th { + padding: 1px 8px 1px 5px; + border-top: 0; + border-left: 0; + border-right: 0; + border-bottom: 1px solid #aaa; +} + +table.footnote td, table.footnote th { + border: 0 !important; +} + +th { + text-align: left; + padding-right: 5px; +} + +table.citation { + border-left: solid 1px gray; + margin-left: 1px; +} + +table.citation td { + border-bottom: none; +} + +th > :first-child, +td > :first-child { + margin-top: 0px; +} + +th > :last-child, +td > :last-child { + margin-bottom: 0px; +} + +/* -- figures --------------------------------------------------------------- */ + +div.figure, figure { + margin: 0.5em; + padding: 0.5em; +} + +div.figure p.caption, figcaption { + padding: 0.3em; +} + +div.figure p.caption span.caption-number, +figcaption span.caption-number { + font-style: italic; +} + +div.figure p.caption span.caption-text, +figcaption span.caption-text { +} + +/* -- field list styles ----------------------------------------------------- */ + +table.field-list td, table.field-list th { + border: 0 !important; +} + +.field-list ul { + margin: 0; + padding-left: 1em; +} + +.field-list p { + margin: 0; +} + +.field-name { + -moz-hyphens: manual; + -ms-hyphens: manual; + -webkit-hyphens: manual; + hyphens: manual; +} + +/* -- hlist styles ---------------------------------------------------------- */ + +table.hlist { + margin: 1em 0; +} + +table.hlist td { + vertical-align: top; +} + +/* -- object description styles --------------------------------------------- */ + +.sig { + font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace; +} + +.sig-name, code.descname { + background-color: transparent; + font-weight: bold; +} + +.sig-name { + font-size: 1.1em; +} + +code.descname { + font-size: 1.2em; +} + +.sig-prename, code.descclassname { + background-color: transparent; +} + +.optional { + font-size: 1.3em; +} + +.sig-paren { + font-size: larger; +} + +.sig-param.n { + font-style: italic; +} + +/* C++ specific styling */ + +.sig-inline.c-texpr, +.sig-inline.cpp-texpr { + font-family: unset; +} + +.sig.c .k, .sig.c .kt, +.sig.cpp .k, .sig.cpp .kt { + color: #0033B3; +} + +.sig.c .m, +.sig.cpp .m { + color: #1750EB; +} + +.sig.c .s, .sig.c .sc, +.sig.cpp .s, .sig.cpp .sc { + color: #067D17; +} + + +/* -- other body styles ----------------------------------------------------- */ + +ol.arabic { + list-style: decimal; +} + +ol.loweralpha { + list-style: lower-alpha; +} + +ol.upperalpha { + list-style: upper-alpha; +} + +ol.lowerroman { + list-style: lower-roman; +} + +ol.upperroman { + list-style: upper-roman; +} + +:not(li) > ol > li:first-child > :first-child, +:not(li) > ul > li:first-child > :first-child { + margin-top: 0px; +} + +:not(li) > ol > li:last-child > :last-child, +:not(li) > ul > li:last-child > :last-child { + margin-bottom: 0px; +} + +ol.simple ol p, +ol.simple ul p, +ul.simple ol p, +ul.simple ul p { + margin-top: 0; +} + +ol.simple > li:not(:first-child) > p, +ul.simple > li:not(:first-child) > p { + margin-top: 0; +} + +ol.simple p, +ul.simple p { + margin-bottom: 0; +} + +dl.footnote > dt, +dl.citation > dt { + float: left; + margin-right: 0.5em; +} + +dl.footnote > dd, +dl.citation > dd { + margin-bottom: 0em; +} + +dl.footnote > dd:after, +dl.citation > dd:after { + content: ""; + clear: both; +} + +dl.field-list { + display: grid; + grid-template-columns: fit-content(30%) auto; +} + +dl.field-list > dt { + font-weight: bold; + word-break: break-word; + padding-left: 0.5em; + padding-right: 5px; +} + +dl.field-list > dt:after { + content: ":"; +} + +dl.field-list > dd { + padding-left: 0.5em; + margin-top: 0em; + margin-left: 0em; + margin-bottom: 0em; +} + +dl { + margin-bottom: 15px; +} + +dd > :first-child { + margin-top: 0px; +} + +dd ul, dd table { + margin-bottom: 10px; +} + +dd { + margin-top: 3px; + margin-bottom: 10px; + margin-left: 30px; +} + +dl > dd:last-child, +dl > dd:last-child > :last-child { + margin-bottom: 0; +} + +dt:target, span.highlighted { + background-color: #fbe54e; +} + +rect.highlighted { + fill: #fbe54e; +} + +dl.glossary dt { + font-weight: bold; + font-size: 1.1em; +} + +.versionmodified { + font-style: italic; +} + +.system-message { + background-color: #fda; + padding: 5px; + border: 3px solid red; +} + +.footnote:target { + background-color: #ffa; +} + +.line-block { + display: block; + margin-top: 1em; + margin-bottom: 1em; +} + +.line-block .line-block { + margin-top: 0; + margin-bottom: 0; + margin-left: 1.5em; +} + +.guilabel, .menuselection { + font-family: sans-serif; +} + +.accelerator { + text-decoration: underline; +} + +.classifier { + font-style: oblique; +} + +.classifier:before { + font-style: normal; + margin: 0 0.5em; + content: ":"; + display: inline-block; +} + +abbr, acronym { + border-bottom: dotted 1px; + cursor: help; +} + +/* -- code displays --------------------------------------------------------- */ + +pre { + overflow: auto; + overflow-y: hidden; /* fixes display issues on Chrome browsers */ +} + +pre, div[class*="highlight-"] { + clear: both; +} + +span.pre { + -moz-hyphens: none; + -ms-hyphens: none; + -webkit-hyphens: none; + hyphens: none; + white-space: nowrap; +} + +div[class*="highlight-"] { + margin: 1em 0; +} + +td.linenos pre { + border: 0; + background-color: transparent; + color: #aaa; +} + +table.highlighttable { + display: block; +} + +table.highlighttable tbody { + display: block; +} + +table.highlighttable tr { + display: flex; +} + +table.highlighttable td { + margin: 0; + padding: 0; +} + +table.highlighttable td.linenos { + padding-right: 0.5em; +} + +table.highlighttable td.code { + flex: 1; + overflow: hidden; +} + +.highlight .hll { + display: block; +} + +div.highlight pre, +table.highlighttable pre { + margin: 0; +} + +div.code-block-caption + div { + margin-top: 0; +} + +div.code-block-caption { + margin-top: 1em; + padding: 2px 5px; + font-size: small; +} + +div.code-block-caption code { + background-color: transparent; +} + +table.highlighttable td.linenos, +span.linenos, +div.highlight span.gp { /* gp: Generic.Prompt */ + user-select: none; + -webkit-user-select: text; /* Safari fallback only */ + -webkit-user-select: none; /* Chrome/Safari */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* IE10+ */ +} + +div.code-block-caption span.caption-number { + padding: 0.1em 0.3em; + font-style: italic; +} + +div.code-block-caption span.caption-text { +} + +div.literal-block-wrapper { + margin: 1em 0; +} + +code.xref, a code { + background-color: transparent; + font-weight: bold; +} + +h1 code, h2 code, h3 code, h4 code, h5 code, h6 code { + background-color: transparent; +} + +.viewcode-link { + float: right; +} + +.viewcode-back { + float: right; + font-family: sans-serif; +} + +div.viewcode-block:target { + margin: -1px -10px; + padding: 0 10px; +} + +/* -- math display ---------------------------------------------------------- */ + +img.math { + vertical-align: middle; +} + +div.body div.math p { + text-align: center; +} + +span.eqno { + float: right; +} + +span.eqno a.headerlink { + position: absolute; + z-index: 1; +} + +div.math:hover a.headerlink { + visibility: visible; +} + +/* -- printout stylesheet --------------------------------------------------- */ + +@media print { + div.document, + div.documentwrapper, + div.bodywrapper { + margin: 0 !important; + width: 100%; + } + + div.sphinxsidebar, + div.related, + div.footer, + #top-link { + display: none; + } +} \ No newline at end of file diff --git a/_static/doctools.js b/_static/doctools.js new file mode 100644 index 00000000..e1bfd708 --- /dev/null +++ b/_static/doctools.js @@ -0,0 +1,358 @@ +/* + * doctools.js + * ~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for all documentation. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +/** + * select a different prefix for underscore + */ +$u = _.noConflict(); + +/** + * make the code below compatible with browsers without + * an installed firebug like debugger +if (!window.console || !console.firebug) { + var names = ["log", "debug", "info", "warn", "error", "assert", "dir", + "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", + "profile", "profileEnd"]; + window.console = {}; + for (var i = 0; i < names.length; ++i) + window.console[names[i]] = function() {}; +} + */ + +/** + * small helper function to urldecode strings + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL + */ +jQuery.urldecode = function(x) { + if (!x) { + return x + } + return decodeURIComponent(x.replace(/\+/g, ' ')); +}; + +/** + * small helper function to urlencode strings + */ +jQuery.urlencode = encodeURIComponent; + +/** + * This function returns the parsed url parameters of the + * current request. Multiple values per key are supported, + * it will always return arrays of strings for the value parts. + */ +jQuery.getQueryParameters = function(s) { + if (typeof s === 'undefined') + s = document.location.search; + var parts = s.substr(s.indexOf('?') + 1).split('&'); + var result = {}; + for (var i = 0; i < parts.length; i++) { + var tmp = parts[i].split('=', 2); + var key = jQuery.urldecode(tmp[0]); + var value = jQuery.urldecode(tmp[1]); + if (key in result) + result[key].push(value); + else + result[key] = [value]; + } + return result; +}; + +/** + * highlight a given string on a jquery object by wrapping it in + * span elements with the given class name. + */ +jQuery.fn.highlightText = function(text, className) { + function highlight(node, addItems) { + if (node.nodeType === 3) { + var val = node.nodeValue; + var pos = val.toLowerCase().indexOf(text); + if (pos >= 0 && + !jQuery(node.parentNode).hasClass(className) && + !jQuery(node.parentNode).hasClass("nohighlight")) { + var span; + var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg"); + if (isInSVG) { + span = document.createElementNS("http://www.w3.org/2000/svg", "tspan"); + } else { + span = document.createElement("span"); + span.className = className; + } + span.appendChild(document.createTextNode(val.substr(pos, text.length))); + node.parentNode.insertBefore(span, node.parentNode.insertBefore( + document.createTextNode(val.substr(pos + text.length)), + node.nextSibling)); + node.nodeValue = val.substr(0, pos); + if (isInSVG) { + var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect"); + var bbox = node.parentElement.getBBox(); + rect.x.baseVal.value = bbox.x; + rect.y.baseVal.value = bbox.y; + rect.width.baseVal.value = bbox.width; + rect.height.baseVal.value = bbox.height; + rect.setAttribute('class', className); + addItems.push({ + "parent": node.parentNode, + "target": rect}); + } + } + } + else if (!jQuery(node).is("button, select, textarea")) { + jQuery.each(node.childNodes, function() { + highlight(this, addItems); + }); + } + } + var addItems = []; + var result = this.each(function() { + highlight(this, addItems); + }); + for (var i = 0; i < addItems.length; ++i) { + jQuery(addItems[i].parent).before(addItems[i].target); + } + return result; +}; + +/* + * backward compatibility for jQuery.browser + * This will be supported until firefox bug is fixed. + */ +if (!jQuery.browser) { + jQuery.uaMatch = function(ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + jQuery.browser = {}; + jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true; +} + +/** + * Small JavaScript module for the documentation. + */ +var Documentation = { + + init : function() { + this.fixFirefoxAnchorBug(); + this.highlightSearchWords(); + this.initIndexTable(); + this.initOnKeyListeners(); + }, + + /** + * i18n support + */ + TRANSLATIONS : {}, + PLURAL_EXPR : function(n) { return n === 1 ? 0 : 1; }, + LOCALE : 'unknown', + + // gettext and ngettext don't access this so that the functions + // can safely bound to a different name (_ = Documentation.gettext) + gettext : function(string) { + var translated = Documentation.TRANSLATIONS[string]; + if (typeof translated === 'undefined') + return string; + return (typeof translated === 'string') ? translated : translated[0]; + }, + + ngettext : function(singular, plural, n) { + var translated = Documentation.TRANSLATIONS[singular]; + if (typeof translated === 'undefined') + return (n == 1) ? singular : plural; + return translated[Documentation.PLURALEXPR(n)]; + }, + + addTranslations : function(catalog) { + for (var key in catalog.messages) + this.TRANSLATIONS[key] = catalog.messages[key]; + this.PLURAL_EXPR = new Function('n', 'return +(' + catalog.plural_expr + ')'); + this.LOCALE = catalog.locale; + }, + + /** + * add context elements like header anchor links + */ + addContextElements : function() { + $('div[id] > :header:first').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this headline')). + appendTo(this); + }); + $('dt[id]').each(function() { + $('\u00B6'). + attr('href', '#' + this.id). + attr('title', _('Permalink to this definition')). + appendTo(this); + }); + }, + + /** + * workaround a firefox stupidity + * see: https://bugzilla.mozilla.org/show_bug.cgi?id=645075 + */ + fixFirefoxAnchorBug : function() { + if (document.location.hash && $.browser.mozilla) + window.setTimeout(function() { + document.location.href += ''; + }, 10); + }, + + /** + * highlight the search words provided in the url in the text + */ + highlightSearchWords : function() { + var params = $.getQueryParameters(); + var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : []; + if (terms.length) { + var body = $('div.body'); + if (!body.length) { + body = $('body'); + } + window.setTimeout(function() { + $.each(terms, function() { + body.highlightText(this.toLowerCase(), 'highlighted'); + }); + }, 10); + $('') + .appendTo($('#searchbox')); + } + }, + + /** + * init the domain index toggle buttons + */ + initIndexTable : function() { + var togglers = $('img.toggler').click(function() { + var src = $(this).attr('src'); + var idnum = $(this).attr('id').substr(7); + $('tr.cg-' + idnum).toggle(); + if (src.substr(-9) === 'minus.png') + $(this).attr('src', src.substr(0, src.length-9) + 'plus.png'); + else + $(this).attr('src', src.substr(0, src.length-8) + 'minus.png'); + }).css('display', ''); + if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) { + togglers.click(); + } + }, + + /** + * helper function to hide the search marks again + */ + hideSearchWords : function() { + $('#searchbox .highlight-link').fadeOut(300); + $('span.highlighted').removeClass('highlighted'); + var url = new URL(window.location); + url.searchParams.delete('highlight'); + window.history.replaceState({}, '', url); + }, + + /** + * helper function to focus on search bar + */ + focusSearchBar : function() { + $('input[name=q]').first().focus(); + }, + + /** + * make the url absolute + */ + makeURL : function(relativeURL) { + return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL; + }, + + /** + * get the current relative url + */ + getCurrentURL : function() { + var path = document.location.pathname; + var parts = path.split(/\//); + $.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() { + if (this === '..') + parts.pop(); + }); + var url = parts.join('/'); + return path.substring(url.lastIndexOf('/') + 1, path.length - 1); + }, + + initOnKeyListeners: function() { + // only install a listener if it is really needed + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS && + !DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + return; + + $(document).keydown(function(event) { + var activeElementType = document.activeElement.tagName; + // don't navigate when in search box, textarea, dropdown or button + if (activeElementType !== 'TEXTAREA' && activeElementType !== 'INPUT' && activeElementType !== 'SELECT' + && activeElementType !== 'BUTTON') { + if (event.altKey || event.ctrlKey || event.metaKey) + return; + + if (!event.shiftKey) { + switch (event.key) { + case 'ArrowLeft': + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) + break; + var prevHref = $('link[rel="prev"]').prop('href'); + if (prevHref) { + window.location.href = prevHref; + return false; + } + break; + case 'ArrowRight': + if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) + break; + var nextHref = $('link[rel="next"]').prop('href'); + if (nextHref) { + window.location.href = nextHref; + return false; + } + break; + case 'Escape': + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + break; + Documentation.hideSearchWords(); + return false; + } + } + + // some keyboard layouts may need Shift to get / + switch (event.key) { + case '/': + if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) + break; + Documentation.focusSearchBar(); + return false; + } + } + }); + } +}; + +// quick alias for translations +_ = Documentation.gettext; + +$(document).ready(function() { + Documentation.init(); +}); diff --git a/_static/documentation_options.js b/_static/documentation_options.js new file mode 100644 index 00000000..00041dff --- /dev/null +++ b/_static/documentation_options.js @@ -0,0 +1,14 @@ +var DOCUMENTATION_OPTIONS = { + URL_ROOT: document.getElementById("documentation_options").getAttribute('data-url_root'), + VERSION: '0.9', + LANGUAGE: 'None', + COLLAPSE_INDEX: false, + BUILDER: 'html', + FILE_SUFFIX: '.html', + LINK_SUFFIX: '.html', + HAS_SOURCE: true, + SOURCELINK_SUFFIX: '.txt', + NAVIGATION_WITH_KEYS: true, + SHOW_SEARCH_SUMMARY: true, + ENABLE_SEARCH_SHORTCUTS: true, +}; \ No newline at end of file diff --git a/_static/file.png b/_static/file.png new file mode 100644 index 00000000..a858a410 Binary files /dev/null and b/_static/file.png differ diff --git a/_static/images/logo_binder.svg b/_static/images/logo_binder.svg new file mode 100644 index 00000000..45fecf75 --- /dev/null +++ b/_static/images/logo_binder.svg @@ -0,0 +1,19 @@ + + + + +logo + + + + + + + + diff --git a/_static/images/logo_colab.png b/_static/images/logo_colab.png new file mode 100644 index 00000000..b7560ec2 Binary files /dev/null and b/_static/images/logo_colab.png differ diff --git a/_static/images/logo_deepnote.svg b/_static/images/logo_deepnote.svg new file mode 100644 index 00000000..fa77ebfc --- /dev/null +++ b/_static/images/logo_deepnote.svg @@ -0,0 +1 @@ + diff --git a/_static/images/logo_jupyterhub.svg b/_static/images/logo_jupyterhub.svg new file mode 100644 index 00000000..60cfe9f2 --- /dev/null +++ b/_static/images/logo_jupyterhub.svg @@ -0,0 +1 @@ +logo_jupyterhubHub diff --git a/_static/jquery-3.5.1.js b/_static/jquery-3.5.1.js new file mode 100644 index 00000000..50937333 --- /dev/null +++ b/_static/jquery-3.5.1.js @@ -0,0 +1,10872 @@ +/*! + * jQuery JavaScript Library v3.5.1 + * https://jquery.com/ + * + * Includes Sizzle.js + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + * + * Date: 2020-05-04T22:49Z + */ +( function( global, factory ) { + + "use strict"; + + if ( typeof module === "object" && typeof module.exports === "object" ) { + + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1 +// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode +// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common +// enough that all such attempts are guarded in a try block. +"use strict"; + +var arr = []; + +var getProto = Object.getPrototypeOf; + +var slice = arr.slice; + +var flat = arr.flat ? function( array ) { + return arr.flat.call( array ); +} : function( array ) { + return arr.concat.apply( [], array ); +}; + + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var fnToString = hasOwn.toString; + +var ObjectFunctionString = fnToString.call( Object ); + +var support = {}; + +var isFunction = function isFunction( obj ) { + + // Support: Chrome <=57, Firefox <=52 + // In some browsers, typeof returns "function" for HTML elements + // (i.e., `typeof document.createElement( "object" ) === "function"`). + // We don't want to classify *any* DOM node as a function. + return typeof obj === "function" && typeof obj.nodeType !== "number"; + }; + + +var isWindow = function isWindow( obj ) { + return obj != null && obj === obj.window; + }; + + +var document = window.document; + + + + var preservedScriptAttributes = { + type: true, + src: true, + nonce: true, + noModule: true + }; + + function DOMEval( code, node, doc ) { + doc = doc || document; + + var i, val, + script = doc.createElement( "script" ); + + script.text = code; + if ( node ) { + for ( i in preservedScriptAttributes ) { + + // Support: Firefox 64+, Edge 18+ + // Some browsers don't support the "nonce" property on scripts. + // On the other hand, just using `getAttribute` is not enough as + // the `nonce` attribute is reset to an empty string whenever it + // becomes browsing-context connected. + // See https://github.com/whatwg/html/issues/2369 + // See https://html.spec.whatwg.org/#nonce-attributes + // The `node.getAttribute` check was added for the sake of + // `jQuery.globalEval` so that it can fake a nonce-containing node + // via an object. + val = node[ i ] || node.getAttribute && node.getAttribute( i ); + if ( val ) { + script.setAttribute( i, val ); + } + } + } + doc.head.appendChild( script ).parentNode.removeChild( script ); + } + + +function toType( obj ) { + if ( obj == null ) { + return obj + ""; + } + + // Support: Android <=2.3 only (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; +} +/* global Symbol */ +// Defining this global in .eslintrc.json would create a danger of using the global +// unguarded in another place, it seems safer to define global only for this module + + + +var + version = "3.5.1", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + + // Return all the elements in a clean array + if ( num == null ) { + return slice.call( this ); + } + + // Return just the one element from the set + return num < 0 ? this[ num + this.length ] : this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + even: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return ( i + 1 ) % 2; + } ) ); + }, + + odd: function() { + return this.pushStack( jQuery.grep( this, function( _elem, i ) { + return i % 2; + } ) ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !isFunction( target ) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + copy = options[ name ]; + + // Prevent Object.prototype pollution + // Prevent never-ending loop + if ( name === "__proto__" || target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = Array.isArray( copy ) ) ) ) { + src = target[ name ]; + + // Ensure proper type for the source value + if ( copyIsArray && !Array.isArray( src ) ) { + clone = []; + } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) { + clone = {}; + } else { + clone = src; + } + copyIsArray = false; + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isPlainObject: function( obj ) { + var proto, Ctor; + + // Detect obvious negatives + // Use toString instead of jQuery.type to catch host objects + if ( !obj || toString.call( obj ) !== "[object Object]" ) { + return false; + } + + proto = getProto( obj ); + + // Objects with no prototype (e.g., `Object.create( null )`) are plain + if ( !proto ) { + return true; + } + + // Objects with prototype are plain iff they were constructed by a global Object function + Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor; + return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString; + }, + + isEmptyObject: function( obj ) { + var name; + + for ( name in obj ) { + return false; + } + return true; + }, + + // Evaluates a script in a provided context; falls back to the global one + // if not specified. + globalEval: function( code, options, doc ) { + DOMEval( code, { nonce: options && options.nonce }, doc ); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return flat( ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ]; +} + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( _i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: real iOS 8.2 only (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = toType( obj ); + + if ( isFunction( obj ) || isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.3.5 + * https://sizzlejs.com/ + * + * Copyright JS Foundation and other contributors + * Released under the MIT license + * https://js.foundation/ + * + * Date: 2020-03-14 + */ +( function( window ) { +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + nonnativeSelectorCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // Instance methods + hasOwn = ( {} ).hasOwnProperty, + arr = [], + pop = arr.pop, + pushNative = arr.push, + push = arr.push, + slice = arr.slice, + + // Use a stripped-down indexOf as it's faster than native + // https://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[ i ] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + + "ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram + identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + + // "Attribute values must be CSS identifiers [capture 5] + // or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + + whitespace + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + + "*" ), + rdescend = new RegExp( whitespace + "|>" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rhtml = /HTML$/i, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + + // CSS escapes + // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + funescape = function( escape, nonHex ) { + var high = "0x" + escape.slice( 1 ) - 0x10000; + + return nonHex ? + + // Strip the backslash prefix from a non-hex escape sequence + nonHex : + + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // CSS string/identifier serialization + // https://drafts.csswg.org/cssom/#common-serializing-idioms + rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, + fcssescape = function( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }, + + inDisabledFieldset = addCombinator( + function( elem ) { + return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + }, + { dir: "parentNode", next: "legend" } + ); + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + ( arr = slice.call( preferredDoc.childNodes ) ), + preferredDoc.childNodes + ); + + // Support: Android<4.0 + // Detect silently failing push.apply + // eslint-disable-next-line no-unused-expressions + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + pushNative.apply( target, slice.call( els ) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + + // Can't trust NodeList.length + while ( ( target[ j++ ] = els[ i++ ] ) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + setDocument( context ); + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) { + + // ID selector + if ( ( m = match[ 1 ] ) ) { + + // Document context + if ( nodeType === 9 ) { + if ( ( elem = context.getElementById( m ) ) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && ( elem = newContext.getElementById( m ) ) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[ 2 ] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && + + // Support: IE 8 only + // Exclude object elements + ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + + newSelector = selector; + newContext = context; + + // qSA considers elements outside a scoping root when evaluating child or + // descendant combinators, which is not what we want. + // In such cases, we work around the behavior by prefixing every selector in the + // list with an ID selector referencing the scope context. + // The technique has to be used as well when a leading combinator is used + // as such selectors are not recognized by querySelectorAll. + // Thanks to Andrew Dupont for this technique. + if ( nodeType === 1 && + ( rdescend.test( selector ) || rcombinators.test( selector ) ) ) { + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + + // We can use :scope instead of the ID hack if the browser + // supports it & if we're not changing the context. + if ( newContext !== context || !support.scope ) { + + // Capture the context ID, setting it first if necessary + if ( ( nid = context.getAttribute( "id" ) ) ) { + nid = nid.replace( rcssescape, fcssescape ); + } else { + context.setAttribute( "id", ( nid = expando ) ); + } + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + while ( i-- ) { + groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " + + toSelector( groups[ i ] ); + } + newSelector = groups.join( "," ); + } + + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + nonnativeSelectorCache( selector, true ); + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return ( cache[ key + " " ] = value ); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created element and returns a boolean result + */ +function assert( fn ) { + var el = document.createElement( "fieldset" ); + + try { + return !!fn( el ); + } catch ( e ) { + return false; + } finally { + + // Remove from its parent by default + if ( el.parentNode ) { + el.parentNode.removeChild( el ); + } + + // release memory in IE + el = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split( "|" ), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[ i ] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + a.sourceIndex - b.sourceIndex; + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( ( cur = cur.nextSibling ) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return ( name === "input" || name === "button" ) && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for :enabled/:disabled + * @param {Boolean} disabled true for :disabled; false for :enabled + */ +function createDisabledPseudo( disabled ) { + + // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable + return function( elem ) { + + // Only certain elements can match :enabled or :disabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled + // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled + if ( "form" in elem ) { + + // Check for inherited disabledness on relevant non-disabled elements: + // * listed form-associated elements in a disabled fieldset + // https://html.spec.whatwg.org/multipage/forms.html#category-listed + // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled + // * option elements in a disabled optgroup + // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled + // All such elements have a "form" property. + if ( elem.parentNode && elem.disabled === false ) { + + // Option elements defer to a parent optgroup if present + if ( "label" in elem ) { + if ( "label" in elem.parentNode ) { + return elem.parentNode.disabled === disabled; + } else { + return elem.disabled === disabled; + } + } + + // Support: IE 6 - 11 + // Use the isDisabled shortcut property to check for disabled fieldset ancestors + return elem.isDisabled === disabled || + + // Where there is no isDisabled, check manually + /* jshint -W018 */ + elem.isDisabled !== !disabled && + inDisabledFieldset( elem ) === disabled; + } + + return elem.disabled === disabled; + + // Try to winnow out elements that can't be disabled before trusting the disabled property. + // Some victims get caught in our net (label, legend, menu, track), but it shouldn't + // even exist on them, let alone have a boolean value. + } else if ( "label" in elem ) { + return elem.disabled === disabled; + } + + // Remaining elements are neither :enabled nor :disabled + return false; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction( function( argument ) { + argument = +argument; + return markFunction( function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ ( j = matchIndexes[ i ] ) ] ) { + seed[ j ] = !( matches[ j ] = seed[ j ] ); + } + } + } ); + } ); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + var namespace = elem.namespaceURI, + docElem = ( elem.ownerDocument || elem ).documentElement; + + // Support: IE <=8 + // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes + // https://bugs.jquery.com/ticket/4833 + return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, subWindow, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( preferredDoc != document && + ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { + + // Support: IE 11, Edge + if ( subWindow.addEventListener ) { + subWindow.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( subWindow.attachEvent ) { + subWindow.attachEvent( "onunload", unloadHandler ); + } + } + + // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, + // Safari 4 - 5 only, Opera <=11.6 - 12.x only + // IE/Edge & older browsers don't support the :scope pseudo-class. + // Support: Safari 6.0 only + // Safari 6.0 supports :scope but it's an alias of :root there. + support.scope = assert( function( el ) { + docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); + return typeof el.querySelectorAll !== "undefined" && + !el.querySelectorAll( ":scope fieldset div" ).length; + } ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert( function( el ) { + el.className = "i"; + return !el.getAttribute( "className" ); + } ); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert( function( el ) { + el.appendChild( document.createComment( "" ) ); + return !el.getElementsByTagName( "*" ).length; + } ); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + docElem.appendChild( el ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + } ); + + // ID filter and find + if ( support.getById ) { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute( "id" ) === attrId; + }; + }; + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var elem = context.getElementById( id ); + return elem ? [ elem ] : []; + } + }; + } else { + Expr.filter[ "ID" ] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode( "id" ); + return node && node.value === attrId; + }; + }; + + // Support: IE 6 - 7 only + // getElementById is not reliable as a find shortcut + Expr.find[ "ID" ] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var node, i, elems, + elem = context.getElementById( id ); + + if ( elem ) { + + // Verify the id attribute + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + + // Fall back on getElementsByName + elems = context.getElementsByName( id ); + i = 0; + while ( ( elem = elems[ i++ ] ) ) { + node = elem.getAttributeNode( "id" ); + if ( node && node.value === id ) { + return [ elem ]; + } + } + } + + return []; + } + }; + } + + // Tag + Expr.find[ "TAG" ] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See https://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { + + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { + + var input; + + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // https://bugs.jquery.com/ticket/12359 + docElem.appendChild( el ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } + + // Support: Firefox <=3.6 - 5 only + // Old Firefox doesn't throw on a badly-escaped identifier. + el.querySelectorAll( "\\\f" ); + rbuggyQSA.push( "[\\r\\n\\f]" ); + } ); + + assert( function( el ) { + el.innerHTML = "" + + ""; + + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( el.querySelectorAll( "[name=d]" ).length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE9-11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + docElem.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: Opera 10 - 11 only + // Opera 10-11 does not throw on post-comma invalid pseudos + el.querySelectorAll( "*,:x" ); + rbuggyQSA.push( ",.*:" ); + } ); + } + + if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector ) ) ) ) { + + assert( function( el ) { + + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( el, "*" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( el, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + } ); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); + } : + function( a, b ) { + if ( b ) { + while ( ( b = b.parentNode ) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) { + + // Choose the first element that is related to our preferred document + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( a == document || a.ownerDocument == preferredDoc && + contains( preferredDoc, a ) ) { + return -1; + } + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( b == document || b.ownerDocument == preferredDoc && + contains( preferredDoc, b ) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + return a == document ? -1 : + b == document ? 1 : + /* eslint-enable eqeqeq */ + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( ( cur = cur.parentNode ) ) { + ap.unshift( cur ); + } + cur = b; + while ( ( cur = cur.parentNode ) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[ i ] === bp[ i ] ) { + i++; + } + + return i ? + + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[ i ], bp[ i ] ) : + + // Otherwise nodes in our document sort first + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + /* eslint-disable eqeqeq */ + ap[ i ] == preferredDoc ? -1 : + bp[ i ] == preferredDoc ? 1 : + /* eslint-enable eqeqeq */ + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + setDocument( elem ); + + if ( support.matchesSelector && documentIsHTML && + !nonnativeSelectorCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch ( e ) { + nonnativeSelectorCache( expr, true ); + } + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( context.ownerDocument || context ) != document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + + // Set document vars if needed + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( ( elem.ownerDocument || elem ) != document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; +}; + +Sizzle.escape = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( ( elem = results[ i++ ] ) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[ 1 ] = match[ 1 ].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || + match[ 5 ] || "" ).replace( runescape, funescape ); + + if ( match[ 2 ] === "~=" ) { + match[ 3 ] = " " + match[ 3 ] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[ 1 ] = match[ 1 ].toLowerCase(); + + if ( match[ 1 ].slice( 0, 3 ) === "nth" ) { + + // nth-* requires argument + if ( !match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[ 4 ] = +( match[ 4 ] ? + match[ 5 ] + ( match[ 6 ] || 1 ) : + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); + + // other types prohibit arguments + } else if ( match[ 3 ] ) { + Sizzle.error( match[ 0 ] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[ 6 ] && match[ 2 ]; + + if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[ 3 ] ) { + match[ 2 ] = match[ 4 ] || match[ 5 ] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + + // Get excess from tokenize (recursively) + ( excess = tokenize( unquoted, true ) ) && + + // advance to the next closing parenthesis + ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) { + + // excess is a negative index + match[ 0 ] = match[ 0 ].slice( 0, excess ); + match[ 2 ] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { + return true; + } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + ( pattern = new RegExp( "(^|" + whitespace + + ")" + className + "(" + whitespace + "|$)" ) ) && classCache( + className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); + } ); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + /* eslint-disable max-len */ + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + /* eslint-enable max-len */ + + }; + }, + + "CHILD": function( type, what, _argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, _context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( ( node = node[ dir ] ) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( ( node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + + // Use previously-cached element index if available + if ( useCache ) { + + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + + // Use the same loop as above to seek `elem` from the start + while ( ( node = ++nodeIndex && node && node[ dir ] || + ( diff = nodeIndex = 0 ) || start.pop() ) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || + ( node[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + ( outerCache[ node.uniqueID ] = {} ); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction( function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[ i ] ); + seed[ idx ] = !( matches[ idx ] = matched[ i ] ); + } + } ) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + + // Potentially complex pseudos + "not": markFunction( function( selector ) { + + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction( function( seed, matches, _context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( ( elem = unmatched[ i ] ) ) { + seed[ i ] = !( matches[ i ] = elem ); + } + } + } ) : + function( elem, _context, xml ) { + input[ 0 ] = elem; + matcher( input, null, xml, results ); + + // Don't keep the element (issue #299) + input[ 0 ] = null; + return !results.pop(); + }; + } ), + + "has": markFunction( function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + } ), + + "contains": markFunction( function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + }; + } ), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + + // lang value must be a valid identifier + if ( !ridentifier.test( lang || "" ) ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( ( elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 ); + return false; + }; + } ), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && + ( !document.hasFocus || document.hasFocus() ) && + !!( elem.type || elem.href || ~elem.tabIndex ); + }, + + // Boolean properties + "enabled": createDisabledPseudo( false ), + "disabled": createDisabledPseudo( true ), + + "checked": function( elem ) { + + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return ( nodeName === "input" && !!elem.checked ) || + ( nodeName === "option" && !!elem.selected ); + }, + + "selected": function( elem ) { + + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + // eslint-disable-next-line no-unused-expressions + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos[ "empty" ]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( ( attr = elem.getAttribute( "type" ) ) == null || + attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo( function() { + return [ 0 ]; + } ), + + "last": createPositionalPseudo( function( _matchIndexes, length ) { + return [ length - 1 ]; + } ), + + "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + } ), + + "even": createPositionalPseudo( function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "odd": createPositionalPseudo( function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? + argument + length : + argument > length ? + length : + argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ), + + "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + } ) + } +}; + +Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || ( match = rcomma.exec( soFar ) ) ) { + if ( match ) { + + // Don't consume trailing commas as valid + soFar = soFar.slice( match[ 0 ].length ) || soFar; + } + groups.push( ( tokens = [] ) ); + } + + matched = false; + + // Combinators + if ( ( match = rcombinators.exec( soFar ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + + // Cast descendant combinators to space + type: match[ 0 ].replace( rtrim, " " ) + } ); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] || + ( match = preFilters[ type ]( match ) ) ) ) { + matched = match.shift(); + tokens.push( { + value: matched, + type: type, + matches: match + } ); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[ i ].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + skip = combinator.next, + key = skip || dir, + checkNonElements = base && key === "parentNode", + doneName = done++; + + return combinator.first ? + + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + return false; + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( ( elem = elem[ dir ] ) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || + ( outerCache[ elem.uniqueID ] = {} ); + + if ( skip && skip === elem.nodeName.toLowerCase() ) { + elem = elem[ dir ] || elem; + } else if ( ( oldCache = uniqueCache[ key ] ) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return ( newCache[ 2 ] = oldCache[ 2 ] ); + } else { + + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ key ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { + return true; + } + } + } + } + } + return false; + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[ i ]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[ 0 ]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[ i ], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( ( elem = unmatched[ i ] ) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction( function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( + selector || "*", + context.nodeType ? [ context ] : context, + [] + ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( ( elem = temp[ i ] ) ) { + matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem ); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) ) { + + // Restore matcherIn since elem is not yet a final match + temp.push( ( matcherIn[ i ] = elem ) ); + } + } + postFinder( null, ( matcherOut = [] ), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( ( elem = matcherOut[ i ] ) && + ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + + seed[ temp ] = !( results[ temp ] = elem ); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + } ); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[ 0 ].type ], + implicitRelative = leadingRelative || Expr.relative[ " " ], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + ( checkContext = context ).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) { + matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ]; + } else { + matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[ j ].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens + .slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), + len = elems.length; + + if ( outermost ) { + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + outermostContext = context == document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( !context && elem.ownerDocument != document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( ( matcher = elementMatchers[ j++ ] ) ) { + if ( matcher( elem, context || document, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + + // They will have gone through all possible matchers + if ( ( elem = !matcher && elem ) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( ( matcher = setMatchers[ j++ ] ) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !( unmatched[ i ] || setMatched[ i ] ) ) { + setMatched[ i ] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[ i ] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( + selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) + ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( ( selector = compiled.selector || selector ) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[ 0 ] = match[ 0 ].slice( 0 ); + if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + + context = ( Expr.find[ "ID" ]( token.matches[ 0 ] + .replace( runescape, funescape ), context ) || [] )[ 0 ]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[ i ]; + + // Abort if we hit a combinator + if ( Expr.relative[ ( type = token.type ) ] ) { + break; + } + if ( ( find = Expr.find[ type ] ) ) { + + // Search, expanding context for leading sibling combinators + if ( ( seed = find( + token.matches[ 0 ].replace( runescape, funescape ), + rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || + context + ) ) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert( function( el ) { + + // Should return 1, but returns 4 (following) + return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; +} ); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert( function( el ) { + el.innerHTML = ""; + return el.firstChild.getAttribute( "href" ) === "#"; +} ) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + } ); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert( function( el ) { + el.innerHTML = ""; + el.firstChild.setAttribute( "value", "" ); + return el.firstChild.getAttribute( "value" ) === ""; +} ) ) { + addHandle( "value", function( elem, _name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + } ); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert( function( el ) { + return el.getAttribute( "disabled" ) == null; +} ) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + ( val = elem.getAttributeNode( name ) ) && val.specified ? + val.value : + null; + } + } ); +} + +return Sizzle; + +} )( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; + +// Deprecated +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; +jQuery.escapeSelector = Sizzle.escape; + + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +}; +var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); + + + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + return !!qualifier.call( elem, i, elem ) !== not; + } ); + } + + // Single element + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + } + + // Arraylike of elements (jQuery, arguments, Array) + if ( typeof qualifier !== "string" ) { + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) > -1 ) !== not; + } ); + } + + // Filtered directly for both simple and complex selectors + return jQuery.filter( qualifier, elements, not ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + if ( elems.length === 1 && elem.nodeType === 1 ) { + return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : []; + } + + return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, ret, + len = this.length, + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + ret = this.pushStack( [] ); + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + return len > 1 ? jQuery.uniqueSort( ret ) : ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + // Shortcut simple #id case for speed + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Method init() accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[ 0 ] === "<" && + selector[ selector.length - 1 ] === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + if ( elem ) { + + // Inject the element directly into the jQuery object + this[ 0 ] = elem; + this.length = 1; + } + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( isFunction( selector ) ) { + return root.ready !== undefined ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter( function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + targets = typeof selectors !== "string" && jQuery( selectors ); + + // Positional selectors never match, since there's no _selection_ context + if ( !rneedsContext.test( selectors ) ) { + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( targets ? + targets.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, _i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, _i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, _i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + if ( elem.contentDocument != null && + + // Support: IE 11+ + // elements with no `data` attribute has an object + // `contentDocument` with a `null` prototype. + getProto( elem.contentDocument ) ) { + + return elem.contentDocument; + } + + // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only + // Treat the template element as a regular one in browsers that + // don't support it. + if ( nodeName( elem, "template" ) ) { + elem = elem.content || elem; + } + + return jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.uniqueSort( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +} ); +var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = locked || options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && toType( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = queue = []; + if ( !memory && !firing ) { + list = memory = ""; + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +function Identity( v ) { + return v; +} +function Thrower( ex ) { + throw ex; +} + +function adoptValue( value, resolve, reject, noValue ) { + var method; + + try { + + // Check for promise aspect first to privilege synchronous behavior + if ( value && isFunction( ( method = value.promise ) ) ) { + method.call( value ).done( resolve ).fail( reject ); + + // Other thenables + } else if ( value && isFunction( ( method = value.then ) ) ) { + method.call( value, resolve, reject ); + + // Other non-thenables + } else { + + // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer: + // * false: [ value ].slice( 0 ) => resolve( value ) + // * true: [ value ].slice( 1 ) => resolve() + resolve.apply( undefined, [ value ].slice( noValue ) ); + } + + // For Promises/A+, convert exceptions into rejections + // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in + // Deferred#then to conditionally suppress rejection. + } catch ( value ) { + + // Support: Android 4.0 only + // Strict mode functions invoked without .call/.apply get global-object context + reject.apply( undefined, [ value ] ); + } +} + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, callbacks, + // ... .then handlers, argument index, [final state] + [ "notify", "progress", jQuery.Callbacks( "memory" ), + jQuery.Callbacks( "memory" ), 2 ], + [ "resolve", "done", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 0, "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), + jQuery.Callbacks( "once memory" ), 1, "rejected" ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + "catch": function( fn ) { + return promise.then( null, fn ); + }, + + // Keep pipe for back-compat + pipe: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( _i, tuple ) { + + // Map tuples (progress, done, fail) to arguments (done, fail, progress) + var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ]; + + // deferred.progress(function() { bind to newDefer or newDefer.notify }) + // deferred.done(function() { bind to newDefer or newDefer.resolve }) + // deferred.fail(function() { bind to newDefer or newDefer.reject }) + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + then: function( onFulfilled, onRejected, onProgress ) { + var maxDepth = 0; + function resolve( depth, deferred, handler, special ) { + return function() { + var that = this, + args = arguments, + mightThrow = function() { + var returned, then; + + // Support: Promises/A+ section 2.3.3.3.3 + // https://promisesaplus.com/#point-59 + // Ignore double-resolution attempts + if ( depth < maxDepth ) { + return; + } + + returned = handler.apply( that, args ); + + // Support: Promises/A+ section 2.3.1 + // https://promisesaplus.com/#point-48 + if ( returned === deferred.promise() ) { + throw new TypeError( "Thenable self-resolution" ); + } + + // Support: Promises/A+ sections 2.3.3.1, 3.5 + // https://promisesaplus.com/#point-54 + // https://promisesaplus.com/#point-75 + // Retrieve `then` only once + then = returned && + + // Support: Promises/A+ section 2.3.4 + // https://promisesaplus.com/#point-64 + // Only check objects and functions for thenability + ( typeof returned === "object" || + typeof returned === "function" ) && + returned.then; + + // Handle a returned thenable + if ( isFunction( then ) ) { + + // Special processors (notify) just wait for resolution + if ( special ) { + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ) + ); + + // Normal processors (resolve) also hook into progress + } else { + + // ...and disregard older resolution values + maxDepth++; + + then.call( + returned, + resolve( maxDepth, deferred, Identity, special ), + resolve( maxDepth, deferred, Thrower, special ), + resolve( maxDepth, deferred, Identity, + deferred.notifyWith ) + ); + } + + // Handle all other returned values + } else { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Identity ) { + that = undefined; + args = [ returned ]; + } + + // Process the value(s) + // Default process is resolve + ( special || deferred.resolveWith )( that, args ); + } + }, + + // Only normal processors (resolve) catch and reject exceptions + process = special ? + mightThrow : + function() { + try { + mightThrow(); + } catch ( e ) { + + if ( jQuery.Deferred.exceptionHook ) { + jQuery.Deferred.exceptionHook( e, + process.stackTrace ); + } + + // Support: Promises/A+ section 2.3.3.3.4.1 + // https://promisesaplus.com/#point-61 + // Ignore post-resolution exceptions + if ( depth + 1 >= maxDepth ) { + + // Only substitute handlers pass on context + // and multiple values (non-spec behavior) + if ( handler !== Thrower ) { + that = undefined; + args = [ e ]; + } + + deferred.rejectWith( that, args ); + } + } + }; + + // Support: Promises/A+ section 2.3.3.3.1 + // https://promisesaplus.com/#point-57 + // Re-resolve promises immediately to dodge false rejection from + // subsequent errors + if ( depth ) { + process(); + } else { + + // Call an optional hook to record the stack, in case of exception + // since it's otherwise lost when execution goes async + if ( jQuery.Deferred.getStackHook ) { + process.stackTrace = jQuery.Deferred.getStackHook(); + } + window.setTimeout( process ); + } + }; + } + + return jQuery.Deferred( function( newDefer ) { + + // progress_handlers.add( ... ) + tuples[ 0 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onProgress ) ? + onProgress : + Identity, + newDefer.notifyWith + ) + ); + + // fulfilled_handlers.add( ... ) + tuples[ 1 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onFulfilled ) ? + onFulfilled : + Identity + ) + ); + + // rejected_handlers.add( ... ) + tuples[ 2 ][ 3 ].add( + resolve( + 0, + newDefer, + isFunction( onRejected ) ? + onRejected : + Thrower + ) + ); + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 5 ]; + + // promise.progress = list.add + // promise.done = list.add + // promise.fail = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( + function() { + + // state = "resolved" (i.e., fulfilled) + // state = "rejected" + state = stateString; + }, + + // rejected_callbacks.disable + // fulfilled_callbacks.disable + tuples[ 3 - i ][ 2 ].disable, + + // rejected_handlers.disable + // fulfilled_handlers.disable + tuples[ 3 - i ][ 3 ].disable, + + // progress_callbacks.lock + tuples[ 0 ][ 2 ].lock, + + // progress_handlers.lock + tuples[ 0 ][ 3 ].lock + ); + } + + // progress_handlers.fire + // fulfilled_handlers.fire + // rejected_handlers.fire + list.add( tuple[ 3 ].fire ); + + // deferred.notify = function() { deferred.notifyWith(...) } + // deferred.resolve = function() { deferred.resolveWith(...) } + // deferred.reject = function() { deferred.rejectWith(...) } + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments ); + return this; + }; + + // deferred.notifyWith = list.fireWith + // deferred.resolveWith = list.fireWith + // deferred.rejectWith = list.fireWith + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( singleValue ) { + var + + // count of uncompleted subordinates + remaining = arguments.length, + + // count of unprocessed arguments + i = remaining, + + // subordinate fulfillment data + resolveContexts = Array( i ), + resolveValues = slice.call( arguments ), + + // the master Deferred + master = jQuery.Deferred(), + + // subordinate callback factory + updateFunc = function( i ) { + return function( value ) { + resolveContexts[ i ] = this; + resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( !( --remaining ) ) { + master.resolveWith( resolveContexts, resolveValues ); + } + }; + }; + + // Single- and empty arguments are adopted like Promise.resolve + if ( remaining <= 1 ) { + adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject, + !remaining ); + + // Use .then() to unwrap secondary thenables (cf. gh-3000) + if ( master.state() === "pending" || + isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) { + + return master.then(); + } + } + + // Multiple arguments are aggregated like Promise.all array elements + while ( i-- ) { + adoptValue( resolveValues[ i ], updateFunc( i ), master.reject ); + } + + return master.promise(); + } +} ); + + +// These usually indicate a programmer mistake during development, +// warn about them ASAP rather than swallowing them by default. +var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; + +jQuery.Deferred.exceptionHook = function( error, stack ) { + + // Support: IE 8 - 9 only + // Console exists when dev tools are open, which can happen at any time + if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { + window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + } +}; + + + + +jQuery.readyException = function( error ) { + window.setTimeout( function() { + throw error; + } ); +}; + + + + +// The deferred used on DOM ready +var readyList = jQuery.Deferred(); + +jQuery.fn.ready = function( fn ) { + + readyList + .then( fn ) + + // Wrap jQuery.readyException in a function so that the lookup + // happens at the time of error handling instead of callback + // registration. + .catch( function( error ) { + jQuery.readyException( error ); + } ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + } +} ); + +jQuery.ready.then = readyList.then; + +// The ready event handler and self cleanup method +function completed() { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + jQuery.ready(); +} + +// Catch cases where $(document).ready() is called +// after the browser event has already occurred. +// Support: IE <=9 - 10 only +// Older IE sometimes signals "interactive" too soon +if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + +} else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); +} + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( toType( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, _key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( + elems[ i ], key, raw ? + value : + value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + if ( chainable ) { + return elems; + } + + // Gets + if ( bulk ) { + return fn.call( elems ); + } + + return len ? fn( elems[ 0 ], key ) : emptyGet; +}; + + +// Matches dashed string for camelizing +var rmsPrefix = /^-ms-/, + rdashAlpha = /-([a-z])/g; + +// Used by camelCase as callback to replace() +function fcamelCase( _all, letter ) { + return letter.toUpperCase(); +} + +// Convert dashed to camelCase; used by the css and data modules +// Support: IE <=9 - 11, Edge 12 - 15 +// Microsoft forgot to hump their vendor prefix (#9572) +function camelCase( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); +} +var acceptData = function( owner ) { + + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + + + +function Data() { + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; + +Data.prototype = { + + cache: function( owner ) { + + // Check if the owner object already has a cache + var value = owner[ this.expando ]; + + // If not, create one + if ( !value ) { + value = {}; + + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return an empty object. + if ( acceptData( owner ) ) { + + // If it is a node unlikely to be stringify-ed or looped over + // use plain assignment + if ( owner.nodeType ) { + owner[ this.expando ] = value; + + // Otherwise secure it in a non-enumerable property + // configurable must be true to allow the property to be + // deleted when data is removed + } else { + Object.defineProperty( owner, this.expando, { + value: value, + configurable: true + } ); + } + } + } + + return value; + }, + set: function( owner, data, value ) { + var prop, + cache = this.cache( owner ); + + // Handle: [ owner, key, value ] args + // Always use camelCase key (gh-2257) + if ( typeof data === "string" ) { + cache[ camelCase( data ) ] = value; + + // Handle: [ owner, { properties } ] args + } else { + + // Copy the properties one-by-one to the cache object + for ( prop in data ) { + cache[ camelCase( prop ) ] = data[ prop ]; + } + } + return cache; + }, + get: function( owner, key ) { + return key === undefined ? + this.cache( owner ) : + + // Always use camelCase key (gh-2257) + owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ]; + }, + access: function( owner, key, value ) { + + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ( ( key && typeof key === "string" ) && value === undefined ) ) { + + return this.get( owner, key ); + } + + // When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, + cache = owner[ this.expando ]; + + if ( cache === undefined ) { + return; + } + + if ( key !== undefined ) { + + // Support array or space separated string of keys + if ( Array.isArray( key ) ) { + + // If key is an array of keys... + // We always set camelCase keys, so remove that. + key = key.map( camelCase ); + } else { + key = camelCase( key ); + + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + key = key in cache ? + [ key ] : + ( key.match( rnothtmlwhite ) || [] ); + } + + i = key.length; + + while ( i-- ) { + delete cache[ key[ i ] ]; + } + } + + // Remove the expando if there's no more data + if ( key === undefined || jQuery.isEmptyObject( cache ) ) { + + // Support: Chrome <=35 - 45 + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted) + if ( owner.nodeType ) { + owner[ this.expando ] = undefined; + } else { + delete owner[ this.expando ]; + } + } + }, + hasData: function( owner ) { + var cache = owner[ this.expando ]; + return cache !== undefined && !jQuery.isEmptyObject( cache ); + } +}; +var dataPriv = new Data(); + +var dataUser = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /[A-Z]/g; + +function getData( data ) { + if ( data === "true" ) { + return true; + } + + if ( data === "false" ) { + return false; + } + + if ( data === "null" ) { + return null; + } + + // Only convert to a number if it doesn't change the string + if ( data === +data + "" ) { + return +data; + } + + if ( rbrace.test( data ) ) { + return JSON.parse( data ); + } + + return data; +} + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = getData( data ); + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + dataUser.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend( { + hasData: function( elem ) { + return dataUser.hasData( elem ) || dataPriv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return dataUser.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + dataUser.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to dataPriv methods, these can be deprecated. + _data: function( elem, name, data ) { + return dataPriv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + dataPriv.remove( elem, name ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = dataUser.get( elem ); + + if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE 11 only + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + dataPriv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + dataUser.set( this, key ); + } ); + } + + return access( this, function( value ) { + var data; + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + + // Attempt to get data from the cache + // The key will always be camelCased in Data + data = dataUser.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, key ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each( function() { + + // We always store the camelCased key + dataUser.set( this, key, value ); + } ); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each( function() { + dataUser.remove( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = dataPriv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || Array.isArray( data ) ) { + queue = dataPriv.access( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return dataPriv.get( elem, key ) || dataPriv.access( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + dataPriv.remove( elem, [ type + "queue", key ] ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = dataPriv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var documentElement = document.documentElement; + + + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only + // Check attachment across shadow DOM boundaries when possible (gh-3504) + // Support: iOS 10.0-10.2 only + // Early iOS 10 versions support `attachShadow` but not `getRootNode`, + // leading to errors. We need to check for `getRootNode`. + if ( documentElement.getRootNode ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } +var isHiddenWithinTree = function( elem, el ) { + + // isHiddenWithinTree might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + + // Inline style trumps all + return elem.style.display === "none" || + elem.style.display === "" && + + // Otherwise, check computed style + // Support: Firefox <=43 - 45 + // Disconnected elements can have computed display: none, so first confirm that elem is + // in the document. + isAttached( elem ) && + + jQuery.css( elem, "display" ) === "none"; + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, scale, + maxIterations = 20, + currentValue = tween ? + function() { + return tween.cur(); + } : + function() { + return jQuery.css( elem, prop, "" ); + }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = elem.nodeType && + ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Support: Firefox <=54 + // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144) + initial = initial / 2; + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + while ( maxIterations-- ) { + + // Evaluate and update our best guess (doubling guesses that zero out). + // Finish if the scale equals or crosses 1 (making the old*new product non-positive). + jQuery.style( elem, prop, initialInUnit + unit ); + if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) { + maxIterations = 0; + } + initialInUnit = initialInUnit / scale; + + } + + initialInUnit = initialInUnit * 2; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +var defaultDisplayMap = {}; + +function getDefaultDisplay( elem ) { + var temp, + doc = elem.ownerDocument, + nodeName = elem.nodeName, + display = defaultDisplayMap[ nodeName ]; + + if ( display ) { + return display; + } + + temp = doc.body.appendChild( doc.createElement( nodeName ) ); + display = jQuery.css( temp, "display" ); + + temp.parentNode.removeChild( temp ); + + if ( display === "none" ) { + display = "block"; + } + defaultDisplayMap[ nodeName ] = display; + + return display; +} + +function showHide( elements, show ) { + var display, elem, + values = [], + index = 0, + length = elements.length; + + // Determine new display value for elements that need to change + for ( ; index < length; index++ ) { + elem = elements[ index ]; + if ( !elem.style ) { + continue; + } + + display = elem.style.display; + if ( show ) { + + // Since we force visibility upon cascade-hidden elements, an immediate (and slow) + // check is required in this first loop unless we have a nonempty display value (either + // inline or about-to-be-restored) + if ( display === "none" ) { + values[ index ] = dataPriv.get( elem, "display" ) || null; + if ( !values[ index ] ) { + elem.style.display = ""; + } + } + if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) { + values[ index ] = getDefaultDisplay( elem ); + } + } else { + if ( display !== "none" ) { + values[ index ] = "none"; + + // Remember what we're overwriting + dataPriv.set( elem, "display", display ); + } + } + } + + // Set the display of the elements in a second loop to avoid constant reflow + for ( index = 0; index < length; index++ ) { + if ( values[ index ] != null ) { + elements[ index ].style.display = values[ index ]; + } + } + + return elements; +} + +jQuery.fn.extend( { + show: function() { + return showHide( this, true ); + }, + hide: function() { + return showHide( this ); + }, + toggle: function( state ) { + if ( typeof state === "boolean" ) { + return state ? this.show() : this.hide(); + } + + return this.each( function() { + if ( isHiddenWithinTree( this ) ) { + jQuery( this ).show(); + } else { + jQuery( this ).hide(); + } + } ); + } +} ); +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i ); + +var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i ); + + + +( function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Android 4.0 - 4.3 only + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Android <=4.1 only + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE <=11 only + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // Support: IE <=9 only + // IE <=9 replaces "; + support.option = !!div.lastChild; +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + + // XHTML parsers do not magically insert elements in the + // same way that tag soup parsers do. So we cannot shorten + // this by omitting or other required elements. + thead: [ 1, "", "
" ], + col: [ 2, "", "
" ], + tr: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + _default: [ 0, "", "" ] +}; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: IE <=9 only +if ( !support.option ) { + wrapMap.optgroup = wrapMap.option = [ 1, "" ]; +} + + +function getAll( context, tag ) { + + // Support: IE <=9 - 11 only + // Use typeof to avoid zero-argument method invocation on host objects (#15151) + var ret; + + if ( typeof context.getElementsByTagName !== "undefined" ) { + ret = context.getElementsByTagName( tag || "*" ); + + } else if ( typeof context.querySelectorAll !== "undefined" ) { + ret = context.querySelectorAll( tag || "*" ); + + } else { + ret = []; + } + + if ( tag === undefined || tag && nodeName( context, tag ) ) { + return jQuery.merge( [ context ], ret ); + } + + return ret; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + dataPriv.set( + elems[ i ], + "globalEval", + !refElements || dataPriv.get( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/; + +function buildFragment( elems, context, scripts, selection, ignored ) { + var elem, tmp, tag, wrap, attached, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( toType( elem ) === "object" ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement( "div" ) ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + continue; + } + + attached = isAttached( elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( attached ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; +} + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE <=9 - 11+ +// focus() and blur() are asynchronous, except when they are no-op. +// So expect focus to be synchronous when the element is already active, +// and blur to be synchronous when the element is not already active. +// (focus and blur are always synchronous in other supported browsers, +// this just defines when we can count on it). +function expectSync( elem, type ) { + return ( elem === safeActiveElement() ) === ( type === "focus" ); +} + +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.get( elem ); + + // Only attach events to objects that accept data + if ( !acceptData( elem ) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Ensure that invalid selectors throw exceptions at attach time + // Evaluate against documentElement in case elem is a non-element node (e.g., document) + if ( selector ) { + jQuery.find.matchesSelector( documentElement, selector ); + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = Object.create( null ); + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = dataPriv.hasData( elem ) && dataPriv.get( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnothtmlwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove data and the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + dataPriv.remove( elem, "handle events" ); + } + }, + + dispatch: function( nativeEvent ) { + + var i, j, ret, matched, handleObj, handlerQueue, + args = new Array( arguments.length ), + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( nativeEvent ), + + handlers = ( + dataPriv.get( this, "events" ) || Object.create( null ) + )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + + for ( i = 1; i < arguments.length; i++ ) { + args[ i ] = arguments[ i ]; + } + + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // If the event is namespaced, then each handler is only invoked if it is + // specially universal or its namespaces are a superset of the event's. + if ( !event.rnamespace || handleObj.namespace === false || + event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, handleObj, sel, matchedHandlers, matchedSelectors, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + if ( delegateCount && + + // Support: IE <=9 + // Black-hole SVG instance trees (trac-13180) + cur.nodeType && + + // Support: Firefox <=42 + // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861) + // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click + // Support: IE 11 only + // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343) + !( event.type === "click" && event.button >= 1 ) ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) { + matchedHandlers = []; + matchedSelectors = {}; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matchedSelectors[ sel ] === undefined ) { + matchedSelectors[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matchedSelectors[ sel ] ) { + matchedHandlers.push( handleObj ); + } + } + if ( matchedHandlers.length ) { + handlerQueue.push( { elem: cur, handlers: matchedHandlers } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + cur = this; + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + addProp: function( name, hook ) { + Object.defineProperty( jQuery.Event.prototype, name, { + enumerable: true, + configurable: true, + + get: isFunction( hook ) ? + function() { + if ( this.originalEvent ) { + return hook( this.originalEvent ); + } + } : + function() { + if ( this.originalEvent ) { + return this.originalEvent[ name ]; + } + }, + + set: function( value ) { + Object.defineProperty( this, name, { + enumerable: true, + configurable: true, + writable: true, + value: value + } ); + } + } ); + }, + + fix: function( originalEvent ) { + return originalEvent[ jQuery.expando ] ? + originalEvent : + new jQuery.Event( originalEvent ); + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + click: { + + // Utilize native event to ensure correct state for checkable inputs + setup: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Claim the first handler + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + // dataPriv.set( el, "click", ... ) + leverageNative( el, "click", returnTrue ); + } + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function( data ) { + + // For mutual compressibility with _default, replace `this` access with a local var. + // `|| data` is dead code meant only to preserve the variable through minification. + var el = this || data; + + // Force setup before triggering a click + if ( rcheckableType.test( el.type ) && + el.click && nodeName( el, "input" ) ) { + + leverageNative( el, "click" ); + } + + // Return non-false to allow normal event-path propagation + return true; + }, + + // For cross-browser consistency, suppress native .click() on links + // Also prevent it if we're currently inside a leveraged native-event stack + _default: function( event ) { + var target = event.target; + return rcheckableType.test( target.type ) && + target.click && nodeName( target, "input" ) && + dataPriv.get( target, "click" ) || + nodeName( target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + } +}; + +// Ensure the presence of an event listener that handles manually-triggered +// synthetic events by interrupting progress until reinvoked in response to +// *native* events that it fires directly, ensuring that state changes have +// already occurred before other listeners are invoked. +function leverageNative( el, type, expectSync ) { + + // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add + if ( !expectSync ) { + if ( dataPriv.get( el, type ) === undefined ) { + jQuery.event.add( el, type, returnTrue ); + } + return; + } + + // Register the controller as a special universal handler for all event namespaces + dataPriv.set( el, type, false ); + jQuery.event.add( el, type, { + namespace: false, + handler: function( event ) { + var notAsync, result, + saved = dataPriv.get( this, type ); + + if ( ( event.isTrigger & 1 ) && this[ type ] ) { + + // Interrupt processing of the outer synthetic .trigger()ed event + // Saved data should be false in such cases, but might be a leftover capture object + // from an async native handler (gh-4350) + if ( !saved.length ) { + + // Store arguments for use when handling the inner native event + // There will always be at least one argument (an event object), so this array + // will not be confused with a leftover capture object. + saved = slice.call( arguments ); + dataPriv.set( this, type, saved ); + + // Trigger the native event and capture its result + // Support: IE <=9 - 11+ + // focus() and blur() are asynchronous + notAsync = expectSync( this, type ); + this[ type ](); + result = dataPriv.get( this, type ); + if ( saved !== result || notAsync ) { + dataPriv.set( this, type, false ); + } else { + result = {}; + } + if ( saved !== result ) { + + // Cancel the outer synthetic event + event.stopImmediatePropagation(); + event.preventDefault(); + return result.value; + } + + // If this is an inner synthetic event for an event with a bubbling surrogate + // (focus or blur), assume that the surrogate already propagated from triggering the + // native event and prevent that from happening again here. + // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the + // bubbling surrogate propagates *after* the non-bubbling base), but that seems + // less bad than duplication. + } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) { + event.stopPropagation(); + } + + // If this is a native event triggered above, everything is now in order + // Fire an inner synthetic event with the original arguments + } else if ( saved.length ) { + + // ...and capture the result + dataPriv.set( this, type, { + value: jQuery.event.trigger( + + // Support: IE <=9 - 11+ + // Extend with the prototype to reset the above stopImmediatePropagation() + jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), + saved.slice( 1 ), + this + ) + } ); + + // Abort handling of the native event + event.stopImmediatePropagation(); + } + } + } ); +} + +jQuery.removeEvent = function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } +}; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: Android <=2.3 only + src.returnValue === false ? + returnTrue : + returnFalse; + + // Create target properties + // Support: Safari <=6 - 7 only + // Target should not be a text node (#504, #13143) + this.target = ( src.target && src.target.nodeType === 3 ) ? + src.target.parentNode : + src.target; + + this.currentTarget = src.currentTarget; + this.relatedTarget = src.relatedTarget; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || Date.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + isSimulated: false, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && !this.isSimulated ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && !this.isSimulated ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Includes all common event props including KeyEvent and MouseEvent specific props +jQuery.each( { + altKey: true, + bubbles: true, + cancelable: true, + changedTouches: true, + ctrlKey: true, + detail: true, + eventPhase: true, + metaKey: true, + pageX: true, + pageY: true, + shiftKey: true, + view: true, + "char": true, + code: true, + charCode: true, + key: true, + keyCode: true, + button: true, + buttons: true, + clientX: true, + clientY: true, + offsetX: true, + offsetY: true, + pointerId: true, + pointerType: true, + screenX: true, + screenY: true, + targetTouches: true, + toElement: true, + touches: true, + + which: function( event ) { + var button = event.button; + + // Add which for key events + if ( event.which == null && rkeyEvent.test( event.type ) ) { + return event.charCode != null ? event.charCode : event.keyCode; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) { + if ( button & 1 ) { + return 1; + } + + if ( button & 2 ) { + return 3; + } + + if ( button & 4 ) { + return 2; + } + + return 0; + } + + return event.which; + } +}, jQuery.event.addProp ); + +jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + jQuery.event.special[ type ] = { + + // Utilize native event if possible so blur/focus sequence is correct + setup: function() { + + // Claim the first handler + // dataPriv.set( this, "focus", ... ) + // dataPriv.set( this, "blur", ... ) + leverageNative( this, type, expectSync ); + + // Return false to allow normal processing in the caller + return false; + }, + trigger: function() { + + // Force setup before trigger + leverageNative( this, type ); + + // Return non-false to allow normal event-path propagation + return true; + }, + + delegateType: delegateType + }; +} ); + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://bugs.chromium.org/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + } +} ); + + +var + + // Support: IE <=10 - 11, Edge 12 - 13 only + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g; + +// Prefer a tbody over its parent table for containing new rows +function manipulationTarget( elem, content ) { + if ( nodeName( elem, "table" ) && + nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) { + + return jQuery( elem ).children( "tbody" )[ 0 ] || elem; + } + + return elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) { + elem.type = elem.type.slice( 5 ); + } else { + elem.removeAttribute( "type" ); + } + + return elem; +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( dataPriv.hasData( src ) ) { + pdataOld = dataPriv.get( src ); + events = pdataOld.events; + + if ( events ) { + dataPriv.remove( dest, "handle events" ); + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( dataUser.hasData( src ) ) { + udataOld = dataUser.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + dataUser.set( dest, udataCur ); + } +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = flat( args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + valueIsFunction = isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( valueIsFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( valueIsFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android <=4.0 only, PhantomJS 1 only + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !dataPriv.access( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl && !node.noModule ) { + jQuery._evalUrl( node.src, { + nonce: node.nonce || node.getAttribute( "nonce" ) + }, doc ); + } + } else { + DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc ); + } + } + } + } + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + nodes = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = nodes[ i ] ) != null; i++ ) { + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && isAttached( node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html; + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = isAttached( elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + cleanData: function( elems ) { + var data, elem, type, + special = jQuery.event.special, + i = 0; + + for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) { + if ( acceptData( elem ) ) { + if ( ( data = elem[ dataPriv.expando ] ) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataPriv.expando ] = undefined; + } + if ( elem[ dataUser.expando ] ) { + + // Support: Chrome <=35 - 45+ + // Assign undefined instead of using delete, see Data#remove + elem[ dataUser.expando ] = undefined; + } + } + } + } +} ); + +jQuery.fn.extend( { + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each( function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + } ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: Android <=4.0 only, PhantomJS 1 only + // .get() because push.apply(_, arraylike) throws on ancient WebKit + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); +var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" ); + +var getStyles = function( elem ) { + + // Support: IE <=11 only, Firefox <=30 (#15098, #14150) + // IE throws on elements created in popups + // FF meanwhile throws on frame elements through "defaultView.getComputedStyle" + var view = elem.ownerDocument.defaultView; + + if ( !view || !view.opener ) { + view = window; + } + + return view.getComputedStyle( elem ); + }; + +var swap = function( elem, options, callback ) { + var ret, name, + old = {}; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; +}; + + +var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); + + + +( function() { + + // Executing both pixelPosition & boxSizingReliable tests require only one layout + // so they're executed at the same time to save the second computation. + function computeStyleTests() { + + // This is a singleton, we need to execute it only once + if ( !div ) { + return; + } + + container.style.cssText = "position:absolute;left:-11111px;width:60px;" + + "margin-top:1px;padding:0;border:0"; + div.style.cssText = + "position:relative;display:block;box-sizing:border-box;overflow:scroll;" + + "margin:auto;border:1px;padding:1px;" + + "width:60%;top:1%"; + documentElement.appendChild( container ).appendChild( div ); + + var divStyle = window.getComputedStyle( div ); + pixelPositionVal = divStyle.top !== "1%"; + + // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44 + reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12; + + // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3 + // Some styles come back with percentage values, even though they shouldn't + div.style.right = "60%"; + pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36; + + // Support: IE 9 - 11 only + // Detect misreporting of content dimensions for box-sizing:border-box elements + boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36; + + // Support: IE 9 only + // Detect overflow:scroll screwiness (gh-3699) + // Support: Chrome <=64 + // Don't get tricked when zoom affects offsetWidth (gh-4029) + div.style.position = "absolute"; + scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12; + + documentElement.removeChild( container ); + + // Nullify the div so it wouldn't be stored in the memory and + // it will also be a sign that checks already performed + div = null; + } + + function roundPixelMeasures( measure ) { + return Math.round( parseFloat( measure ) ); + } + + var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal, + reliableTrDimensionsVal, reliableMarginLeftVal, + container = document.createElement( "div" ), + div = document.createElement( "div" ); + + // Finish early in limited (non-browser) environments + if ( !div.style ) { + return; + } + + // Support: IE <=9 - 11 only + // Style of cloned element affects source element cloned (#8908) + div.style.backgroundClip = "content-box"; + div.cloneNode( true ).style.backgroundClip = ""; + support.clearCloneStyle = div.style.backgroundClip === "content-box"; + + jQuery.extend( support, { + boxSizingReliable: function() { + computeStyleTests(); + return boxSizingReliableVal; + }, + pixelBoxStyles: function() { + computeStyleTests(); + return pixelBoxStylesVal; + }, + pixelPosition: function() { + computeStyleTests(); + return pixelPositionVal; + }, + reliableMarginLeft: function() { + computeStyleTests(); + return reliableMarginLeftVal; + }, + scrollboxSize: function() { + computeStyleTests(); + return scrollboxSizeVal; + }, + + // Support: IE 9 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Behavior in IE 9 is more subtle than in newer versions & it passes + // some versions of this test; make sure not to make it pass there! + reliableTrDimensions: function() { + var table, tr, trChild, trStyle; + if ( reliableTrDimensionsVal == null ) { + table = document.createElement( "table" ); + tr = document.createElement( "tr" ); + trChild = document.createElement( "div" ); + + table.style.cssText = "position:absolute;left:-11111px"; + tr.style.height = "1px"; + trChild.style.height = "9px"; + + documentElement + .appendChild( table ) + .appendChild( tr ) + .appendChild( trChild ); + + trStyle = window.getComputedStyle( tr ); + reliableTrDimensionsVal = parseInt( trStyle.height ) > 3; + + documentElement.removeChild( table ); + } + return reliableTrDimensionsVal; + } + } ); +} )(); + + +function curCSS( elem, name, computed ) { + var width, minWidth, maxWidth, ret, + + // Support: Firefox 51+ + // Retrieving style before computed somehow + // fixes an issue with getting wrong values + // on detached elements + style = elem.style; + + computed = computed || getStyles( elem ); + + // getPropertyValue is needed for: + // .css('filter') (IE 9 only, #12537) + // .css('--customProperty) (#3144) + if ( computed ) { + ret = computed.getPropertyValue( name ) || computed[ name ]; + + if ( ret === "" && !isAttached( elem ) ) { + ret = jQuery.style( elem, name ); + } + + // A tribute to the "awesome hack by Dean Edwards" + // Android Browser returns percentage for some values, + // but width seems to be reliably pixels. + // This is against the CSSOM draft spec: + // https://drafts.csswg.org/cssom/#resolved-values + if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) { + + // Remember the original values + width = style.width; + minWidth = style.minWidth; + maxWidth = style.maxWidth; + + // Put in the new values to get a computed value out + style.minWidth = style.maxWidth = style.width = ret; + ret = computed.width; + + // Revert the changed values + style.width = width; + style.minWidth = minWidth; + style.maxWidth = maxWidth; + } + } + + return ret !== undefined ? + + // Support: IE <=9 - 11 only + // IE returns zIndex value as an integer. + ret + "" : + ret; +} + + +function addGetHookIf( conditionFn, hookFn ) { + + // Define the hook, we'll check on the first run if it's really needed. + return { + get: function() { + if ( conditionFn() ) { + + // Hook not needed (or it's not possible to use it due + // to missing dependency), remove it. + delete this.get; + return; + } + + // Hook needed; redefine it so that the support test is not executed again. + return ( this.get = hookFn ).apply( this, arguments ); + } + }; +} + + +var cssPrefixes = [ "Webkit", "Moz", "ms" ], + emptyStyle = document.createElement( "div" ).style, + vendorProps = {}; + +// Return a vendor-prefixed property or undefined +function vendorPropName( name ) { + + // Check for vendor prefixed names + var capName = name[ 0 ].toUpperCase() + name.slice( 1 ), + i = cssPrefixes.length; + + while ( i-- ) { + name = cssPrefixes[ i ] + capName; + if ( name in emptyStyle ) { + return name; + } + } +} + +// Return a potentially-mapped jQuery.cssProps or vendor prefixed property +function finalPropName( name ) { + var final = jQuery.cssProps[ name ] || vendorProps[ name ]; + + if ( final ) { + return final; + } + if ( name in emptyStyle ) { + return name; + } + return vendorProps[ name ] = vendorPropName( name ) || name; +} + + +var + + // Swappable if display is none or starts with table + // except "table", "table-cell", or "table-caption" + // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display + rdisplayswap = /^(none|table(?!-c[ea]).+)/, + rcustomProp = /^--/, + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + cssNormalTransform = { + letterSpacing: "0", + fontWeight: "400" + }; + +function setPositiveNumber( _elem, value, subtract ) { + + // Any relative (+/-) values have already been + // normalized at this point + var matches = rcssNum.exec( value ); + return matches ? + + // Guard against undefined "subtract", e.g., when used as in cssHooks + Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) : + value; +} + +function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { + var i = dimension === "width" ? 1 : 0, + extra = 0, + delta = 0; + + // Adjustment may not be necessary + if ( box === ( isBorderBox ? "border" : "content" ) ) { + return 0; + } + + for ( ; i < 4; i += 2 ) { + + // Both box models exclude margin + if ( box === "margin" ) { + delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + } + + // If we get here with a content-box, we're seeking "padding" or "border" or "margin" + if ( !isBorderBox ) { + + // Add padding + delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + + // For "border" or "margin", add border + if ( box !== "padding" ) { + delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + + // But still keep track of it otherwise + } else { + extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + + // If we get here with a border-box (content + padding + border), we're seeking "content" or + // "padding" or "margin" + } else { + + // For "content", subtract padding + if ( box === "content" ) { + delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles ); + } + + // For "content" or "padding", subtract border + if ( box !== "margin" ) { + delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles ); + } + } + } + + // Account for positive content-box scroll gutter when requested by providing computedVal + if ( !isBorderBox && computedVal >= 0 ) { + + // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border + // Assuming integer scroll gutter, subtract the rest and round down + delta += Math.max( 0, Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + computedVal - + delta - + extra - + 0.5 + + // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter + // Use an explicit zero to avoid NaN (gh-3964) + ) ) || 0; + } + + return delta; +} + +function getWidthOrHeight( elem, dimension, extra ) { + + // Start with computed style + var styles = getStyles( elem ), + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322). + // Fake content-box until we know it's needed to know the true value. + boxSizingNeeded = !support.boxSizingReliable() || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + valueIsBorderBox = isBorderBox, + + val = curCSS( elem, dimension, styles ), + offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ); + + // Support: Firefox <=54 + // Return a confounding non-pixel value or feign ignorance, as appropriate. + if ( rnumnonpx.test( val ) ) { + if ( !extra ) { + return val; + } + val = "auto"; + } + + + // Support: IE 9 - 11 only + // Use offsetWidth/offsetHeight for when box sizing is unreliable. + // In those cases, the computed value can be trusted to be border-box. + if ( ( !support.boxSizingReliable() && isBorderBox || + + // Support: IE 10 - 11+, Edge 15 - 18+ + // IE/Edge misreport `getComputedStyle` of table rows with width/height + // set in CSS while `offset*` properties report correct values. + // Interestingly, in some cases IE 9 doesn't suffer from this issue. + !support.reliableTrDimensions() && nodeName( elem, "tr" ) || + + // Fall back to offsetWidth/offsetHeight when value is "auto" + // This happens for inline elements with no explicit setting (gh-3571) + val === "auto" || + + // Support: Android <=4.1 - 4.3 only + // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602) + !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) && + + // Make sure the element is visible & connected + elem.getClientRects().length ) { + + isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box"; + + // Where available, offsetWidth/offsetHeight approximate border box dimensions. + // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the + // retrieved value as a content box dimension. + valueIsBorderBox = offsetProp in elem; + if ( valueIsBorderBox ) { + val = elem[ offsetProp ]; + } + } + + // Normalize "" and auto + val = parseFloat( val ) || 0; + + // Adjust for the element's box model + return ( val + + boxModelAdjustment( + elem, + dimension, + extra || ( isBorderBox ? "border" : "content" ), + valueIsBorderBox, + styles, + + // Provide the current computed size to request scroll gutter calculation (gh-3589) + val + ) + ) + "px"; +} + +jQuery.extend( { + + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + } + } + } + }, + + // Don't automatically add "px" to these possibly-unitless properties + cssNumber: { + "animationIterationCount": true, + "columnCount": true, + "fillOpacity": true, + "flexGrow": true, + "flexShrink": true, + "fontWeight": true, + "gridArea": true, + "gridColumn": true, + "gridColumnEnd": true, + "gridColumnStart": true, + "gridRow": true, + "gridRowEnd": true, + "gridRowStart": true, + "lineHeight": true, + "opacity": true, + "order": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: {}, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ), + style = elem.style; + + // Make sure that we're working with the right name. We don't + // want to query the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Gets hook for the prefixed version, then unprefixed version + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // Convert "+=" or "-=" to relative numbers (#7345) + if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) { + value = adjustCSS( elem, name, ret ); + + // Fixes bug #9237 + type = "number"; + } + + // Make sure that null and NaN values aren't set (#7116) + if ( value == null || value !== value ) { + return; + } + + // If a number was passed in, add the unit (except for certain CSS properties) + // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append + // "px" to a few hardcoded values. + if ( type === "number" && !isCustomProp ) { + value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" ); + } + + // background-* props affect original clone's values + if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) { + style[ name ] = "inherit"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !( "set" in hooks ) || + ( value = hooks.set( elem, value, extra ) ) !== undefined ) { + + if ( isCustomProp ) { + style.setProperty( name, value ); + } else { + style[ name ] = value; + } + } + + } else { + + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && + ( ret = hooks.get( elem, false, extra ) ) !== undefined ) { + + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra, styles ) { + var val, num, hooks, + origName = camelCase( name ), + isCustomProp = rcustomProp.test( name ); + + // Make sure that we're working with the right name. We don't + // want to modify the value if it is a CSS custom property + // since they are user-defined. + if ( !isCustomProp ) { + name = finalPropName( origName ); + } + + // Try prefixed name followed by the unprefixed name + hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ]; + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks ) { + val = hooks.get( elem, true, extra ); + } + + // Otherwise, if a way to get the computed value exists, use that + if ( val === undefined ) { + val = curCSS( elem, name, styles ); + } + + // Convert "normal" to computed value + if ( val === "normal" && name in cssNormalTransform ) { + val = cssNormalTransform[ name ]; + } + + // Make numeric if forced or a qualifier was provided and val looks numeric + if ( extra === "" || extra ) { + num = parseFloat( val ); + return extra === true || isFinite( num ) ? num || 0 : val; + } + + return val; + } +} ); + +jQuery.each( [ "height", "width" ], function( _i, dimension ) { + jQuery.cssHooks[ dimension ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + + // Certain elements can have dimension info if we invisibly show them + // but it must have a current display style that would benefit + return rdisplayswap.test( jQuery.css( elem, "display" ) ) && + + // Support: Safari 8+ + // Table columns in Safari have non-zero offsetWidth & zero + // getBoundingClientRect().width unless display is changed. + // Support: IE <=11 only + // Running getBoundingClientRect on a disconnected node + // in IE throws an error. + ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ? + swap( elem, cssShow, function() { + return getWidthOrHeight( elem, dimension, extra ); + } ) : + getWidthOrHeight( elem, dimension, extra ); + } + }, + + set: function( elem, value, extra ) { + var matches, + styles = getStyles( elem ), + + // Only read styles.position if the test has a chance to fail + // to avoid forcing a reflow. + scrollboxSizeBuggy = !support.scrollboxSize() && + styles.position === "absolute", + + // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991) + boxSizingNeeded = scrollboxSizeBuggy || extra, + isBorderBox = boxSizingNeeded && + jQuery.css( elem, "boxSizing", false, styles ) === "border-box", + subtract = extra ? + boxModelAdjustment( + elem, + dimension, + extra, + isBorderBox, + styles + ) : + 0; + + // Account for unreliable border-box dimensions by comparing offset* to computed and + // faking a content-box to get border and padding (gh-3699) + if ( isBorderBox && scrollboxSizeBuggy ) { + subtract -= Math.ceil( + elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] - + parseFloat( styles[ dimension ] ) - + boxModelAdjustment( elem, dimension, "border", false, styles ) - + 0.5 + ); + } + + // Convert to pixels if value adjustment is needed + if ( subtract && ( matches = rcssNum.exec( value ) ) && + ( matches[ 3 ] || "px" ) !== "px" ) { + + elem.style[ dimension ] = value; + value = jQuery.css( elem, dimension ); + } + + return setPositiveNumber( elem, value, subtract ); + } + }; +} ); + +jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft, + function( elem, computed ) { + if ( computed ) { + return ( parseFloat( curCSS( elem, "marginLeft" ) ) || + elem.getBoundingClientRect().left - + swap( elem, { marginLeft: 0 }, function() { + return elem.getBoundingClientRect().left; + } ) + ) + "px"; + } + } +); + +// These hooks are used by animate to expand properties +jQuery.each( { + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i = 0, + expanded = {}, + + // Assumes a single number if not a string + parts = typeof value === "string" ? value.split( " " ) : [ value ]; + + for ( ; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; + + if ( prefix !== "margin" ) { + jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber; + } +} ); + +jQuery.fn.extend( { + css: function( name, value ) { + return access( this, function( elem, name, value ) { + var styles, len, + map = {}, + i = 0; + + if ( Array.isArray( name ) ) { + styles = getStyles( elem ); + len = name.length; + + for ( ; i < len; i++ ) { + map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); + } + + return map; + } + + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); + } +} ); + + +function Tween( elem, options, prop, end, easing ) { + return new Tween.prototype.init( elem, options, prop, end, easing ); +} +jQuery.Tween = Tween; + +Tween.prototype = { + constructor: Tween, + init: function( elem, options, prop, end, easing, unit ) { + this.elem = elem; + this.prop = prop; + this.easing = easing || jQuery.easing._default; + this.options = options; + this.start = this.now = this.cur(); + this.end = end; + this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" ); + }, + cur: function() { + var hooks = Tween.propHooks[ this.prop ]; + + return hooks && hooks.get ? + hooks.get( this ) : + Tween.propHooks._default.get( this ); + }, + run: function( percent ) { + var eased, + hooks = Tween.propHooks[ this.prop ]; + + if ( this.options.duration ) { + this.pos = eased = jQuery.easing[ this.easing ]( + percent, this.options.duration * percent, 0, 1, this.options.duration + ); + } else { + this.pos = eased = percent; + } + this.now = ( this.end - this.start ) * eased + this.start; + + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + if ( hooks && hooks.set ) { + hooks.set( this ); + } else { + Tween.propHooks._default.set( this ); + } + return this; + } +}; + +Tween.prototype.init.prototype = Tween.prototype; + +Tween.propHooks = { + _default: { + get: function( tween ) { + var result; + + // Use a property on the element directly when it is not a DOM element, + // or when there is no matching style property that exists. + if ( tween.elem.nodeType !== 1 || + tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) { + return tween.elem[ tween.prop ]; + } + + // Passing an empty string as a 3rd parameter to .css will automatically + // attempt a parseFloat and fallback to a string if the parse fails. + // Simple values such as "10px" are parsed to Float; + // complex values such as "rotate(1rad)" are returned as-is. + result = jQuery.css( tween.elem, tween.prop, "" ); + + // Empty strings, null, undefined and "auto" are converted to 0. + return !result || result === "auto" ? 0 : result; + }, + set: function( tween ) { + + // Use step hook for back compat. + // Use cssHook if its there. + // Use .style if available and use plain properties where available. + if ( jQuery.fx.step[ tween.prop ] ) { + jQuery.fx.step[ tween.prop ]( tween ); + } else if ( tween.elem.nodeType === 1 && ( + jQuery.cssHooks[ tween.prop ] || + tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) { + jQuery.style( tween.elem, tween.prop, tween.now + tween.unit ); + } else { + tween.elem[ tween.prop ] = tween.now; + } + } + } +}; + +// Support: IE <=9 only +// Panic based approach to setting things on disconnected nodes +Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = { + set: function( tween ) { + if ( tween.elem.nodeType && tween.elem.parentNode ) { + tween.elem[ tween.prop ] = tween.now; + } + } +}; + +jQuery.easing = { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return 0.5 - Math.cos( p * Math.PI ) / 2; + }, + _default: "swing" +}; + +jQuery.fx = Tween.prototype.init; + +// Back compat <1.8 extension point +jQuery.fx.step = {}; + + + + +var + fxNow, inProgress, + rfxtypes = /^(?:toggle|show|hide)$/, + rrun = /queueHooks$/; + +function schedule() { + if ( inProgress ) { + if ( document.hidden === false && window.requestAnimationFrame ) { + window.requestAnimationFrame( schedule ); + } else { + window.setTimeout( schedule, jQuery.fx.interval ); + } + + jQuery.fx.tick(); + } +} + +// Animations created synchronously will run synchronously +function createFxNow() { + window.setTimeout( function() { + fxNow = undefined; + } ); + return ( fxNow = Date.now() ); +} + +// Generate parameters to create a standard animation +function genFx( type, includeWidth ) { + var which, + i = 0, + attrs = { height: type }; + + // If we include width, step value is 1 to do all cssExpand values, + // otherwise step value is 2 to skip over Left and Right + includeWidth = includeWidth ? 1 : 0; + for ( ; i < 4; i += 2 - includeWidth ) { + which = cssExpand[ i ]; + attrs[ "margin" + which ] = attrs[ "padding" + which ] = type; + } + + if ( includeWidth ) { + attrs.opacity = attrs.width = type; + } + + return attrs; +} + +function createTween( value, prop, animation ) { + var tween, + collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ), + index = 0, + length = collection.length; + for ( ; index < length; index++ ) { + if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) { + + // We're done with this property + return tween; + } + } +} + +function defaultPrefilter( elem, props, opts ) { + var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display, + isBox = "width" in props || "height" in props, + anim = this, + orig = {}, + style = elem.style, + hidden = elem.nodeType && isHiddenWithinTree( elem ), + dataShow = dataPriv.get( elem, "fxshow" ); + + // Queue-skipping animations hijack the fx hooks + if ( !opts.queue ) { + hooks = jQuery._queueHooks( elem, "fx" ); + if ( hooks.unqueued == null ) { + hooks.unqueued = 0; + oldfire = hooks.empty.fire; + hooks.empty.fire = function() { + if ( !hooks.unqueued ) { + oldfire(); + } + }; + } + hooks.unqueued++; + + anim.always( function() { + + // Ensure the complete handler is called before this completes + anim.always( function() { + hooks.unqueued--; + if ( !jQuery.queue( elem, "fx" ).length ) { + hooks.empty.fire(); + } + } ); + } ); + } + + // Detect show/hide animations + for ( prop in props ) { + value = props[ prop ]; + if ( rfxtypes.test( value ) ) { + delete props[ prop ]; + toggle = toggle || value === "toggle"; + if ( value === ( hidden ? "hide" : "show" ) ) { + + // Pretend to be hidden if this is a "show" and + // there is still data from a stopped show/hide + if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) { + hidden = true; + + // Ignore all other no-op show/hide data + } else { + continue; + } + } + orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop ); + } + } + + // Bail out if this is a no-op like .hide().hide() + propTween = !jQuery.isEmptyObject( props ); + if ( !propTween && jQuery.isEmptyObject( orig ) ) { + return; + } + + // Restrict "overflow" and "display" styles during box animations + if ( isBox && elem.nodeType === 1 ) { + + // Support: IE <=9 - 11, Edge 12 - 15 + // Record all 3 overflow attributes because IE does not infer the shorthand + // from identically-valued overflowX and overflowY and Edge just mirrors + // the overflowX value there. + opts.overflow = [ style.overflow, style.overflowX, style.overflowY ]; + + // Identify a display type, preferring old show/hide data over the CSS cascade + restoreDisplay = dataShow && dataShow.display; + if ( restoreDisplay == null ) { + restoreDisplay = dataPriv.get( elem, "display" ); + } + display = jQuery.css( elem, "display" ); + if ( display === "none" ) { + if ( restoreDisplay ) { + display = restoreDisplay; + } else { + + // Get nonempty value(s) by temporarily forcing visibility + showHide( [ elem ], true ); + restoreDisplay = elem.style.display || restoreDisplay; + display = jQuery.css( elem, "display" ); + showHide( [ elem ] ); + } + } + + // Animate inline elements as inline-block + if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) { + if ( jQuery.css( elem, "float" ) === "none" ) { + + // Restore the original display value at the end of pure show/hide animations + if ( !propTween ) { + anim.done( function() { + style.display = restoreDisplay; + } ); + if ( restoreDisplay == null ) { + display = style.display; + restoreDisplay = display === "none" ? "" : display; + } + } + style.display = "inline-block"; + } + } + } + + if ( opts.overflow ) { + style.overflow = "hidden"; + anim.always( function() { + style.overflow = opts.overflow[ 0 ]; + style.overflowX = opts.overflow[ 1 ]; + style.overflowY = opts.overflow[ 2 ]; + } ); + } + + // Implement show/hide animations + propTween = false; + for ( prop in orig ) { + + // General show/hide setup for this element animation + if ( !propTween ) { + if ( dataShow ) { + if ( "hidden" in dataShow ) { + hidden = dataShow.hidden; + } + } else { + dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } ); + } + + // Store hidden/visible for toggle so `.stop().toggle()` "reverses" + if ( toggle ) { + dataShow.hidden = !hidden; + } + + // Show elements before animating them + if ( hidden ) { + showHide( [ elem ], true ); + } + + /* eslint-disable no-loop-func */ + + anim.done( function() { + + /* eslint-enable no-loop-func */ + + // The final step of a "hide" animation is actually hiding the element + if ( !hidden ) { + showHide( [ elem ] ); + } + dataPriv.remove( elem, "fxshow" ); + for ( prop in orig ) { + jQuery.style( elem, prop, orig[ prop ] ); + } + } ); + } + + // Per-property setup + propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim ); + if ( !( prop in dataShow ) ) { + dataShow[ prop ] = propTween.start; + if ( hidden ) { + propTween.end = propTween.start; + propTween.start = 0; + } + } + } +} + +function propFilter( props, specialEasing ) { + var index, name, easing, value, hooks; + + // camelCase, specialEasing and expand cssHook pass + for ( index in props ) { + name = camelCase( index ); + easing = specialEasing[ name ]; + value = props[ index ]; + if ( Array.isArray( value ) ) { + easing = value[ 1 ]; + value = props[ index ] = value[ 0 ]; + } + + if ( index !== name ) { + props[ name ] = value; + delete props[ index ]; + } + + hooks = jQuery.cssHooks[ name ]; + if ( hooks && "expand" in hooks ) { + value = hooks.expand( value ); + delete props[ name ]; + + // Not quite $.extend, this won't overwrite existing keys. + // Reusing 'index' because we have the correct "name" + for ( index in value ) { + if ( !( index in props ) ) { + props[ index ] = value[ index ]; + specialEasing[ index ] = easing; + } + } + } else { + specialEasing[ name ] = easing; + } + } +} + +function Animation( elem, properties, options ) { + var result, + stopped, + index = 0, + length = Animation.prefilters.length, + deferred = jQuery.Deferred().always( function() { + + // Don't match elem in the :animated selector + delete tick.elem; + } ), + tick = function() { + if ( stopped ) { + return false; + } + var currentTime = fxNow || createFxNow(), + remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ), + + // Support: Android 2.3 only + // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497) + temp = remaining / animation.duration || 0, + percent = 1 - temp, + index = 0, + length = animation.tweens.length; + + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( percent ); + } + + deferred.notifyWith( elem, [ animation, percent, remaining ] ); + + // If there's more to do, yield + if ( percent < 1 && length ) { + return remaining; + } + + // If this was an empty animation, synthesize a final progress notification + if ( !length ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + } + + // Resolve the animation and report its conclusion + deferred.resolveWith( elem, [ animation ] ); + return false; + }, + animation = deferred.promise( { + elem: elem, + props: jQuery.extend( {}, properties ), + opts: jQuery.extend( true, { + specialEasing: {}, + easing: jQuery.easing._default + }, options ), + originalProperties: properties, + originalOptions: options, + startTime: fxNow || createFxNow(), + duration: options.duration, + tweens: [], + createTween: function( prop, end ) { + var tween = jQuery.Tween( elem, animation.opts, prop, end, + animation.opts.specialEasing[ prop ] || animation.opts.easing ); + animation.tweens.push( tween ); + return tween; + }, + stop: function( gotoEnd ) { + var index = 0, + + // If we are going to the end, we want to run all the tweens + // otherwise we skip this part + length = gotoEnd ? animation.tweens.length : 0; + if ( stopped ) { + return this; + } + stopped = true; + for ( ; index < length; index++ ) { + animation.tweens[ index ].run( 1 ); + } + + // Resolve when we played the last frame; otherwise, reject + if ( gotoEnd ) { + deferred.notifyWith( elem, [ animation, 1, 0 ] ); + deferred.resolveWith( elem, [ animation, gotoEnd ] ); + } else { + deferred.rejectWith( elem, [ animation, gotoEnd ] ); + } + return this; + } + } ), + props = animation.props; + + propFilter( props, animation.opts.specialEasing ); + + for ( ; index < length; index++ ) { + result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts ); + if ( result ) { + if ( isFunction( result.stop ) ) { + jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = + result.stop.bind( result ); + } + return result; + } + } + + jQuery.map( props, createTween, animation ); + + if ( isFunction( animation.opts.start ) ) { + animation.opts.start.call( elem, animation ); + } + + // Attach callbacks from options + animation + .progress( animation.opts.progress ) + .done( animation.opts.done, animation.opts.complete ) + .fail( animation.opts.fail ) + .always( animation.opts.always ); + + jQuery.fx.timer( + jQuery.extend( tick, { + elem: elem, + anim: animation, + queue: animation.opts.queue + } ) + ); + + return animation; +} + +jQuery.Animation = jQuery.extend( Animation, { + + tweeners: { + "*": [ function( prop, value ) { + var tween = this.createTween( prop, value ); + adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween ); + return tween; + } ] + }, + + tweener: function( props, callback ) { + if ( isFunction( props ) ) { + callback = props; + props = [ "*" ]; + } else { + props = props.match( rnothtmlwhite ); + } + + var prop, + index = 0, + length = props.length; + + for ( ; index < length; index++ ) { + prop = props[ index ]; + Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || []; + Animation.tweeners[ prop ].unshift( callback ); + } + }, + + prefilters: [ defaultPrefilter ], + + prefilter: function( callback, prepend ) { + if ( prepend ) { + Animation.prefilters.unshift( callback ); + } else { + Animation.prefilters.push( callback ); + } + } +} ); + +jQuery.speed = function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !isFunction( easing ) && easing + }; + + // Go to the end state if fx are off + if ( jQuery.fx.off ) { + opt.duration = 0; + + } else { + if ( typeof opt.duration !== "number" ) { + if ( opt.duration in jQuery.fx.speeds ) { + opt.duration = jQuery.fx.speeds[ opt.duration ]; + + } else { + opt.duration = jQuery.fx.speeds._default; + } + } + } + + // Normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function() { + if ( isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } + }; + + return opt; +}; + +jQuery.fn.extend( { + fadeTo: function( speed, to, easing, callback ) { + + // Show any hidden elements after setting opacity to 0 + return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show() + + // Animate to the value specified + .end().animate( { opacity: to }, speed, easing, callback ); + }, + animate: function( prop, speed, easing, callback ) { + var empty = jQuery.isEmptyObject( prop ), + optall = jQuery.speed( speed, easing, callback ), + doAnimation = function() { + + // Operate on a copy of prop so per-property easing won't be lost + var anim = Animation( this, jQuery.extend( {}, prop ), optall ); + + // Empty animations, or finishing resolves immediately + if ( empty || dataPriv.get( this, "finish" ) ) { + anim.stop( true ); + } + }; + doAnimation.finish = doAnimation; + + return empty || optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + stop: function( type, clearQueue, gotoEnd ) { + var stopQueue = function( hooks ) { + var stop = hooks.stop; + delete hooks.stop; + stop( gotoEnd ); + }; + + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue ) { + this.queue( type || "fx", [] ); + } + + return this.each( function() { + var dequeue = true, + index = type != null && type + "queueHooks", + timers = jQuery.timers, + data = dataPriv.get( this ); + + if ( index ) { + if ( data[ index ] && data[ index ].stop ) { + stopQueue( data[ index ] ); + } + } else { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) { + stopQueue( data[ index ] ); + } + } + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && + ( type == null || timers[ index ].queue === type ) ) { + + timers[ index ].anim.stop( gotoEnd ); + dequeue = false; + timers.splice( index, 1 ); + } + } + + // Start the next in the queue if the last step wasn't forced. + // Timers currently will call their complete callbacks, which + // will dequeue but only if they were gotoEnd. + if ( dequeue || !gotoEnd ) { + jQuery.dequeue( this, type ); + } + } ); + }, + finish: function( type ) { + if ( type !== false ) { + type = type || "fx"; + } + return this.each( function() { + var index, + data = dataPriv.get( this ), + queue = data[ type + "queue" ], + hooks = data[ type + "queueHooks" ], + timers = jQuery.timers, + length = queue ? queue.length : 0; + + // Enable finishing flag on private data + data.finish = true; + + // Empty the queue first + jQuery.queue( this, type, [] ); + + if ( hooks && hooks.stop ) { + hooks.stop.call( this, true ); + } + + // Look for any active animations, and finish them + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && timers[ index ].queue === type ) { + timers[ index ].anim.stop( true ); + timers.splice( index, 1 ); + } + } + + // Look for any animations in the old queue and finish them + for ( index = 0; index < length; index++ ) { + if ( queue[ index ] && queue[ index ].finish ) { + queue[ index ].finish.call( this ); + } + } + + // Turn off finishing flag + delete data.finish; + } ); + } +} ); + +jQuery.each( [ "toggle", "show", "hide" ], function( _i, name ) { + var cssFn = jQuery.fn[ name ]; + jQuery.fn[ name ] = function( speed, easing, callback ) { + return speed == null || typeof speed === "boolean" ? + cssFn.apply( this, arguments ) : + this.animate( genFx( name, true ), speed, easing, callback ); + }; +} ); + +// Generate shortcuts for custom animations +jQuery.each( { + slideDown: genFx( "show" ), + slideUp: genFx( "hide" ), + slideToggle: genFx( "toggle" ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +} ); + +jQuery.timers = []; +jQuery.fx.tick = function() { + var timer, + i = 0, + timers = jQuery.timers; + + fxNow = Date.now(); + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + + // Run the timer and safely remove it when done (allowing for external removal) + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + fxNow = undefined; +}; + +jQuery.fx.timer = function( timer ) { + jQuery.timers.push( timer ); + jQuery.fx.start(); +}; + +jQuery.fx.interval = 13; +jQuery.fx.start = function() { + if ( inProgress ) { + return; + } + + inProgress = true; + schedule(); +}; + +jQuery.fx.stop = function() { + inProgress = null; +}; + +jQuery.fx.speeds = { + slow: 600, + fast: 200, + + // Default speed + _default: 400 +}; + + +// Based off of the plugin by Clint Helfers, with permission. +// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/ +jQuery.fn.delay = function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = window.setTimeout( next, time ); + hooks.stop = function() { + window.clearTimeout( timeout ); + }; + } ); +}; + + +( function() { + var input = document.createElement( "input" ), + select = document.createElement( "select" ), + opt = select.appendChild( document.createElement( "option" ) ); + + input.type = "checkbox"; + + // Support: Android <=4.3 only + // Default value for a checkbox should be "on" + support.checkOn = input.value !== ""; + + // Support: IE <=11 only + // Must access selectedIndex to make default options select + support.optSelected = opt.selected; + + // Support: IE <=11 only + // An input loses its value after becoming a radio + input = document.createElement( "input" ); + input.value = "t"; + input.type = "radio"; + support.radioValue = input.value === "t"; +} )(); + + +var boolHook, + attrHandle = jQuery.expr.attrHandle; + +jQuery.fn.extend( { + attr: function( name, value ) { + return access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each( function() { + jQuery.removeAttr( this, name ); + } ); + } +} ); + +jQuery.extend( { + attr: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set attributes on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + // Attribute hooks are determined by the lowercase version + // Grab necessary hook if one is defined + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + hooks = jQuery.attrHooks[ name.toLowerCase() ] || + ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined ); + } + + if ( value !== undefined ) { + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + } + + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + elem.setAttribute( name, value + "" ); + return value; + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + ret = jQuery.find.attr( elem, name ); + + // Non-existent attributes return null, we normalize to undefined + return ret == null ? undefined : ret; + }, + + attrHooks: { + type: { + set: function( elem, value ) { + if ( !support.radioValue && value === "radio" && + nodeName( elem, "input" ) ) { + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + } + }, + + removeAttr: function( elem, value ) { + var name, + i = 0, + + // Attribute names can contain non-HTML whitespace characters + // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2 + attrNames = value && value.match( rnothtmlwhite ); + + if ( attrNames && elem.nodeType === 1 ) { + while ( ( name = attrNames[ i++ ] ) ) { + elem.removeAttribute( name ); + } + } + } +} ); + +// Hooks for boolean attributes +boolHook = { + set: function( elem, value, name ) { + if ( value === false ) { + + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + elem.setAttribute( name, name ); + } + return name; + } +}; + +jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( _i, name ) { + var getter = attrHandle[ name ] || jQuery.find.attr; + + attrHandle[ name ] = function( elem, name, isXML ) { + var ret, handle, + lowercaseName = name.toLowerCase(); + + if ( !isXML ) { + + // Avoid an infinite loop by temporarily removing this function from the getter + handle = attrHandle[ lowercaseName ]; + attrHandle[ lowercaseName ] = ret; + ret = getter( elem, name, isXML ) != null ? + lowercaseName : + null; + attrHandle[ lowercaseName ] = handle; + } + return ret; + }; +} ); + + + + +var rfocusable = /^(?:input|select|textarea|button)$/i, + rclickable = /^(?:a|area)$/i; + +jQuery.fn.extend( { + prop: function( name, value ) { + return access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + return this.each( function() { + delete this[ jQuery.propFix[ name ] || name ]; + } ); + } +} ); + +jQuery.extend( { + prop: function( elem, name, value ) { + var ret, hooks, + nType = elem.nodeType; + + // Don't get/set properties on text, comment and attribute nodes + if ( nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { + + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && + ( ret = hooks.set( elem, value, name ) ) !== undefined ) { + return ret; + } + + return ( elem[ name ] = value ); + } + + if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) { + return ret; + } + + return elem[ name ]; + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + + // Support: IE <=9 - 11 only + // elem.tabIndex doesn't always return the + // correct value when it hasn't been explicitly set + // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + // Use proper attribute retrieval(#12072) + var tabindex = jQuery.find.attr( elem, "tabindex" ); + + if ( tabindex ) { + return parseInt( tabindex, 10 ); + } + + if ( + rfocusable.test( elem.nodeName ) || + rclickable.test( elem.nodeName ) && + elem.href + ) { + return 0; + } + + return -1; + } + } + }, + + propFix: { + "for": "htmlFor", + "class": "className" + } +} ); + +// Support: IE <=11 only +// Accessing the selectedIndex property +// forces the browser to respect setting selected +// on the option +// The getter ensures a default option is selected +// when in an optgroup +// eslint rule "no-unused-expressions" is disabled for this code +// since it considers such accessions noop +if ( !support.optSelected ) { + jQuery.propHooks.selected = { + get: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent && parent.parentNode ) { + parent.parentNode.selectedIndex; + } + return null; + }, + set: function( elem ) { + + /* eslint no-unused-expressions: "off" */ + + var parent = elem.parentNode; + if ( parent ) { + parent.selectedIndex; + + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + } + }; +} + +jQuery.each( [ + "tabIndex", + "readOnly", + "maxLength", + "cellSpacing", + "cellPadding", + "rowSpan", + "colSpan", + "useMap", + "frameBorder", + "contentEditable" +], function() { + jQuery.propFix[ this.toLowerCase() ] = this; +} ); + + + + + // Strip and collapse whitespace according to HTML spec + // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace + function stripAndCollapse( value ) { + var tokens = value.match( rnothtmlwhite ) || []; + return tokens.join( " " ); + } + + +function getClass( elem ) { + return elem.getAttribute && elem.getAttribute( "class" ) || ""; +} + +function classesToArray( value ) { + if ( Array.isArray( value ) ) { + return value; + } + if ( typeof value === "string" ) { + return value.match( rnothtmlwhite ) || []; + } + return []; +} + +jQuery.fn.extend( { + addClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).addClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + if ( cur.indexOf( " " + clazz + " " ) < 0 ) { + cur += clazz + " "; + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classes, elem, cur, curValue, clazz, j, finalValue, + i = 0; + + if ( isFunction( value ) ) { + return this.each( function( j ) { + jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) ); + } ); + } + + if ( !arguments.length ) { + return this.attr( "class", "" ); + } + + classes = classesToArray( value ); + + if ( classes.length ) { + while ( ( elem = this[ i++ ] ) ) { + curValue = getClass( elem ); + + // This expression is here for better compressibility (see addClass) + cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " ); + + if ( cur ) { + j = 0; + while ( ( clazz = classes[ j++ ] ) ) { + + // Remove *all* instances + while ( cur.indexOf( " " + clazz + " " ) > -1 ) { + cur = cur.replace( " " + clazz + " ", " " ); + } + } + + // Only assign if different to avoid unneeded rendering. + finalValue = stripAndCollapse( cur ); + if ( curValue !== finalValue ) { + elem.setAttribute( "class", finalValue ); + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isValidValue = type === "string" || Array.isArray( value ); + + if ( typeof stateVal === "boolean" && isValidValue ) { + return stateVal ? this.addClass( value ) : this.removeClass( value ); + } + + if ( isFunction( value ) ) { + return this.each( function( i ) { + jQuery( this ).toggleClass( + value.call( this, i, getClass( this ), stateVal ), + stateVal + ); + } ); + } + + return this.each( function() { + var className, i, self, classNames; + + if ( isValidValue ) { + + // Toggle individual class names + i = 0; + self = jQuery( this ); + classNames = classesToArray( value ); + + while ( ( className = classNames[ i++ ] ) ) { + + // Check each className given, space separated list + if ( self.hasClass( className ) ) { + self.removeClass( className ); + } else { + self.addClass( className ); + } + } + + // Toggle whole class name + } else if ( value === undefined || type === "boolean" ) { + className = getClass( this ); + if ( className ) { + + // Store className if set + dataPriv.set( this, "__className__", className ); + } + + // If the element has a class name or if we're passed `false`, + // then remove the whole classname (if there was one, the above saved it). + // Otherwise bring back whatever was previously saved (if anything), + // falling back to the empty string if nothing was stored. + if ( this.setAttribute ) { + this.setAttribute( "class", + className || value === false ? + "" : + dataPriv.get( this, "__className__" ) || "" + ); + } + } + } ); + }, + + hasClass: function( selector ) { + var className, elem, + i = 0; + + className = " " + selector + " "; + while ( ( elem = this[ i++ ] ) ) { + if ( elem.nodeType === 1 && + ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) { + return true; + } + } + + return false; + } +} ); + + + + +var rreturn = /\r/g; + +jQuery.fn.extend( { + val: function( value ) { + var hooks, ret, valueIsFunction, + elem = this[ 0 ]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || + jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && + "get" in hooks && + ( ret = hooks.get( elem, "value" ) ) !== undefined + ) { + return ret; + } + + ret = elem.value; + + // Handle most common string cases + if ( typeof ret === "string" ) { + return ret.replace( rreturn, "" ); + } + + // Handle cases where value is null/undef or number + return ret == null ? "" : ret; + } + + return; + } + + valueIsFunction = isFunction( value ); + + return this.each( function( i ) { + var val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( valueIsFunction ) { + val = value.call( this, i, jQuery( this ).val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + + } else if ( typeof val === "number" ) { + val += ""; + + } else if ( Array.isArray( val ) ) { + val = jQuery.map( val, function( value ) { + return value == null ? "" : value + ""; + } ); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + } ); + } +} ); + +jQuery.extend( { + valHooks: { + option: { + get: function( elem ) { + + var val = jQuery.find.attr( elem, "value" ); + return val != null ? + val : + + // Support: IE <=10 - 11 only + // option.text throws exceptions (#14686, #14858) + // Strip and collapse whitespace + // https://html.spec.whatwg.org/#strip-and-collapse-whitespace + stripAndCollapse( jQuery.text( elem ) ); + } + }, + select: { + get: function( elem ) { + var value, option, i, + options = elem.options, + index = elem.selectedIndex, + one = elem.type === "select-one", + values = one ? null : [], + max = one ? index + 1 : options.length; + + if ( index < 0 ) { + i = max; + + } else { + i = one ? index : 0; + } + + // Loop through all the selected options + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Support: IE <=9 only + // IE8-9 doesn't update selected after form reset (#2551) + if ( ( option.selected || i === index ) && + + // Don't return options that are disabled or in a disabled optgroup + !option.disabled && + ( !option.parentNode.disabled || + !nodeName( option.parentNode, "optgroup" ) ) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + }, + + set: function( elem, value ) { + var optionSet, option, + options = elem.options, + values = jQuery.makeArray( value ), + i = options.length; + + while ( i-- ) { + option = options[ i ]; + + /* eslint-disable no-cond-assign */ + + if ( option.selected = + jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1 + ) { + optionSet = true; + } + + /* eslint-enable no-cond-assign */ + } + + // Force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + elem.selectedIndex = -1; + } + return values; + } + } + } +} ); + +// Radios and checkboxes getter/setter +jQuery.each( [ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + set: function( elem, value ) { + if ( Array.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 ); + } + } + }; + if ( !support.checkOn ) { + jQuery.valHooks[ this ].get = function( elem ) { + return elem.getAttribute( "value" ) === null ? "on" : elem.value; + }; + } +} ); + + + + +// Return jQuery for attributes-only inclusion + + +support.focusin = "onfocusin" in window; + + +var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + stopPropagationCallback = function( e ) { + e.stopPropagation(); + }; + +jQuery.extend( jQuery.event, { + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, lastElement, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = lastElement = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + lastElement = cur; + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( + dataPriv.get( cur, "events" ) || Object.create( null ) + )[ event.type ] && + dataPriv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( ( !special._default || + special._default.apply( eventPath.pop(), data ) === false ) && + acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + + if ( event.isPropagationStopped() ) { + lastElement.addEventListener( type, stopPropagationCallback ); + } + + elem[ type ](); + + if ( event.isPropagationStopped() ) { + lastElement.removeEventListener( type, stopPropagationCallback ); + } + + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + // Piggyback on a donor event to simulate a different one + // Used only for `focus(in | out)` events + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + } + ); + + jQuery.event.trigger( e, null, elem ); + } + +} ); + +jQuery.fn.extend( { + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +// Support: Firefox <=44 +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + attaches = dataPriv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + dataPriv.remove( doc, fix ); + + } else { + dataPriv.access( doc, fix, attaches ); + } + } + }; + } ); +} +var location = window.location; + +var nonce = { guid: Date.now() }; + +var rquery = ( /\?/ ); + + + +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) { + xml = undefined; + } + + if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; +}; + + +var + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i, + rsubmittable = /^(?:input|select|textarea|keygen)/i; + +function buildParams( prefix, obj, traditional, add ) { + var name; + + if ( Array.isArray( obj ) ) { + + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + + // Item is non-scalar (array or object), encode its numeric index. + buildParams( + prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]", + v, + traditional, + add + ); + } + } ); + + } else if ( !traditional && toType( obj ) === "object" ) { + + // Serialize object item. + for ( name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + + // Serialize scalar item. + add( prefix, obj ); + } +} + +// Serialize an array of form elements or a set of +// key/values into a query string +jQuery.param = function( a, traditional ) { + var prefix, + s = [], + add = function( key, valueOrFunction ) { + + // If value is a function, invoke it and use its return value + var value = isFunction( valueOrFunction ) ? + valueOrFunction() : + valueOrFunction; + + s[ s.length ] = encodeURIComponent( key ) + "=" + + encodeURIComponent( value == null ? "" : value ); + }; + + if ( a == null ) { + return ""; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + } ); + + } else { + + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ); +}; + +jQuery.fn.extend( { + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + serializeArray: function() { + return this.map( function() { + + // Can add propHook for "elements" to filter or add form elements + var elements = jQuery.prop( this, "elements" ); + return elements ? jQuery.makeArray( elements ) : this; + } ) + .filter( function() { + var type = this.type; + + // Use .is( ":disabled" ) so that fieldset[disabled] works + return this.name && !jQuery( this ).is( ":disabled" ) && + rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) && + ( this.checked || !rcheckableType.test( type ) ); + } ) + .map( function( _i, elem ) { + var val = jQuery( this ).val(); + + if ( val == null ) { + return null; + } + + if ( Array.isArray( val ) ) { + return jQuery.map( val, function( val ) { + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ); + } + + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + } ).get(); + } +} ); + + +var + r20 = /%20/g, + rhash = /#.*$/, + rantiCache = /([?&])_=[^&]*/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg, + + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = "*/".concat( "*" ), + + // Anchor tag for parsing the document origin + originAnchor = document.createElement( "a" ); + originAnchor.href = location.href; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + var dataType, + i = 0, + dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || []; + + if ( isFunction( func ) ) { + + // For each dataType in the dataTypeExpression + while ( ( dataType = dataTypes[ i++ ] ) ) { + + // Prepend if requested + if ( dataType[ 0 ] === "+" ) { + dataType = dataType.slice( 1 ) || "*"; + ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func ); + + // Otherwise append + } else { + ( structure[ dataType ] = structure[ dataType ] || [] ).push( func ); + } + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) { + + var inspected = {}, + seekingTransport = ( structure === transports ); + + function inspect( dataType ) { + var selected; + inspected[ dataType ] = true; + jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) { + var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR ); + if ( typeof dataTypeOrTransport === "string" && + !seekingTransport && !inspected[ dataTypeOrTransport ] ) { + + options.dataTypes.unshift( dataTypeOrTransport ); + inspect( dataTypeOrTransport ); + return false; + } else if ( seekingTransport ) { + return !( selected = dataTypeOrTransport ); + } + } ); + return selected; + } + + return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" ); +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } + + return target; +} + +/* Handles responses to an ajax request: + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var ct, type, finalDataType, firstDataType, + contents = s.contents, + dataTypes = s.dataTypes; + + // Remove auto dataType and get content-type in the process + while ( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +/* Chain conversions given the request and the original response + * Also sets the responseXXX fields on the jqXHR instance + */ +function ajaxConvert( s, response, jqXHR, isSuccess ) { + var conv2, current, conv, tmp, prev, + converters = {}, + + // Work with a copy of dataTypes in case we need to modify it for conversion + dataTypes = s.dataTypes.slice(); + + // Create converters map with lowercased keys + if ( dataTypes[ 1 ] ) { + for ( conv in s.converters ) { + converters[ conv.toLowerCase() ] = s.converters[ conv ]; + } + } + + current = dataTypes.shift(); + + // Convert to each sequential dataType + while ( current ) { + + if ( s.responseFields[ current ] ) { + jqXHR[ s.responseFields[ current ] ] = response; + } + + // Apply the dataFilter if provided + if ( !prev && isSuccess && s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + prev = current; + current = dataTypes.shift(); + + if ( current ) { + + // There's only work to do if current dataType is non-auto + if ( current === "*" ) { + + current = prev; + + // Convert response if prev dataType is non-auto and differs from current + } else if ( prev !== "*" && prev !== current ) { + + // Seek a direct converter + conv = converters[ prev + " " + current ] || converters[ "* " + current ]; + + // If none found, seek a pair + if ( !conv ) { + for ( conv2 in converters ) { + + // If conv2 outputs current + tmp = conv2.split( " " ); + if ( tmp[ 1 ] === current ) { + + // If prev can be converted to accepted input + conv = converters[ prev + " " + tmp[ 0 ] ] || + converters[ "* " + tmp[ 0 ] ]; + if ( conv ) { + + // Condense equivalence converters + if ( conv === true ) { + conv = converters[ conv2 ]; + + // Otherwise, insert the intermediate dataType + } else if ( converters[ conv2 ] !== true ) { + current = tmp[ 0 ]; + dataTypes.unshift( tmp[ 1 ] ); + } + break; + } + } + } + } + + // Apply converter (if not an equivalence) + if ( conv !== true ) { + + // Unless errors are allowed to bubble, catch and return them + if ( conv && s.throws ) { + response = conv( response ); + } else { + try { + response = conv( response ); + } catch ( e ) { + return { + state: "parsererror", + error: conv ? e : "No conversion from " + prev + " to " + current + }; + } + } + } + } + } + } + + return { state: "success", data: response }; +} + +jQuery.extend( { + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {}, + + ajaxSettings: { + url: location.href, + type: "GET", + isLocal: rlocalProtocol.test( location.protocol ), + global: true, + processData: true, + async: true, + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + throws: false, + traditional: false, + headers: {}, + */ + + accepts: { + "*": allTypes, + text: "text/plain", + html: "text/html", + xml: "application/xml, text/xml", + json: "application/json, text/javascript" + }, + + contents: { + xml: /\bxml\b/, + html: /\bhtml/, + json: /\bjson\b/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText", + json: "responseJSON" + }, + + // Data converters + // Keys separate source (or catchall "*") and destination types with a single space + converters: { + + // Convert anything to text + "* text": String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": JSON.parse, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + url: true, + context: true + } + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + return settings ? + + // Building a settings object + ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) : + + // Extending ajaxSettings + ajaxExtend( jQuery.ajaxSettings, target ); + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var transport, + + // URL without anti-cache param + cacheURL, + + // Response headers + responseHeadersString, + responseHeaders, + + // timeout handle + timeoutTimer, + + // Url cleanup var + urlAnchor, + + // Request state (becomes false upon send and true upon completion) + completed, + + // To know if global events are to be dispatched + fireGlobals, + + // Loop variable + i, + + // uncached part of the url + uncached, + + // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + + // Callbacks context + callbackContext = s.context || s, + + // Context for global events is callbackContext if it is a DOM node or jQuery collection + globalEventContext = s.context && + ( callbackContext.nodeType || callbackContext.jquery ) ? + jQuery( callbackContext ) : + jQuery.event, + + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + + // Status-dependent callbacks + statusCode = s.statusCode || {}, + + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + + // Default abort message + strAbort = "canceled", + + // Fake xhr + jqXHR = { + readyState: 0, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( completed ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while ( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[ 1 ].toLowerCase() + " " ] = + ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] ) + .concat( match[ 2 ] ); + } + } + match = responseHeaders[ key.toLowerCase() + " " ]; + } + return match == null ? null : match.join( ", " ); + }, + + // Raw string + getAllResponseHeaders: function() { + return completed ? responseHeadersString : null; + }, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( completed == null ) { + name = requestHeadersNames[ name.toLowerCase() ] = + requestHeadersNames[ name.toLowerCase() ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( completed == null ) { + s.mimeType = type; + } + return this; + }, + + // Status-dependent callbacks + statusCode: function( map ) { + var code; + if ( map ) { + if ( completed ) { + + // Execute the appropriate callbacks + jqXHR.always( map[ jqXHR.status ] ); + } else { + + // Lazy-add the new callbacks in a way that preserves old ones + for ( code in map ) { + statusCode[ code ] = [ statusCode[ code ], map[ code ] ]; + } + } + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + var finalText = statusText || strAbort; + if ( transport ) { + transport.abort( finalText ); + } + done( 0, finalText ); + return this; + } + }; + + // Attach deferreds + deferred.promise( jqXHR ); + + // Add protocol if not provided (prefilters might expect it) + // Handle falsy url in the settings object (#10093: consistency with old signature) + // We also use the url parameter if available + s.url = ( ( url || s.url || location.href ) + "" ) + .replace( rprotocol, location.protocol + "//" ); + + // Alias method option to type as per ticket #12004 + s.type = options.method || options.type || s.method || s.type; + + // Extract dataTypes list + s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ]; + + // A cross-domain request is in order when the origin doesn't match the current origin. + if ( s.crossDomain == null ) { + urlAnchor = document.createElement( "a" ); + + // Support: IE <=8 - 11, Edge 12 - 15 + // IE throws exception on accessing the href property if url is malformed, + // e.g. http://example.com:80x/ + try { + urlAnchor.href = s.url; + + // Support: IE <=8 - 11 only + // Anchor's host property isn't correctly set when s.url is relative + urlAnchor.href = urlAnchor.href; + s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !== + urlAnchor.protocol + "//" + urlAnchor.host; + } catch ( e ) { + + // If there is an error parsing the URL, assume it is crossDomain, + // it can be rejected by the transport if it is invalid + s.crossDomain = true; + } + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( completed ) { + return jqXHR; + } + + // We can fire global events as of now if asked to + // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118) + fireGlobals = jQuery.event && s.global; + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Save the URL in case we're toying with the If-Modified-Since + // and/or If-None-Match header later on + // Remove hash to simplify url manipulation + cacheURL = s.url.replace( rhash, "" ); + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // Remember the hash so we can put it back + uncached = s.url.slice( cacheURL.length ); + + // If data is available and should be processed, append data to url + if ( s.data && ( s.processData || typeof s.data === "string" ) ) { + cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data; + + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Add or update anti-cache param if needed + if ( s.cache === false ) { + cacheURL = cacheURL.replace( rantiCache, "$1" ); + uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce.guid++ ) + + uncached; + } + + // Put hash and anti-cache on the URL that will be requested (gh-1732) + s.url = cacheURL + uncached; + + // Change '%20' to '+' if this is encoded form body content (gh-2658) + } else if ( s.data && s.processData && + ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) { + s.data = s.data.replace( r20, "+" ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + if ( jQuery.lastModified[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] ); + } + if ( jQuery.etag[ cacheURL ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ? + s.accepts[ s.dataTypes[ 0 ] ] + + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && + ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) { + + // Abort if not done already and return + return jqXHR.abort(); + } + + // Aborting is no longer a cancellation + strAbort = "abort"; + + // Install callbacks on deferreds + completeDeferred.add( s.complete ); + jqXHR.done( s.success ); + jqXHR.fail( s.error ); + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + + // If request was aborted inside ajaxSend, stop there + if ( completed ) { + return jqXHR; + } + + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = window.setTimeout( function() { + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + completed = false; + transport.send( requestHeaders, done ); + } catch ( e ) { + + // Rethrow post-completion exceptions + if ( completed ) { + throw e; + } + + // Propagate others as results + done( -1, e ); + } + } + + // Callback for when everything is done + function done( status, nativeStatusText, responses, headers ) { + var isSuccess, success, error, response, modified, + statusText = nativeStatusText; + + // Ignore repeat invocations + if ( completed ) { + return; + } + + completed = true; + + // Clear timeout if it exists + if ( timeoutTimer ) { + window.clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + // Determine if successful + isSuccess = status >= 200 && status < 300 || status === 304; + + // Get response data + if ( responses ) { + response = ajaxHandleResponses( s, jqXHR, responses ); + } + + // Use a noop converter for missing script + if ( !isSuccess && jQuery.inArray( "script", s.dataTypes ) > -1 ) { + s.converters[ "text script" ] = function() {}; + } + + // Convert no matter what (that way responseXXX fields are always set) + response = ajaxConvert( s, response, jqXHR, isSuccess ); + + // If successful, handle type chaining + if ( isSuccess ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + modified = jqXHR.getResponseHeader( "Last-Modified" ); + if ( modified ) { + jQuery.lastModified[ cacheURL ] = modified; + } + modified = jqXHR.getResponseHeader( "etag" ); + if ( modified ) { + jQuery.etag[ cacheURL ] = modified; + } + } + + // if no content + if ( status === 204 || s.type === "HEAD" ) { + statusText = "nocontent"; + + // if not modified + } else if ( status === 304 ) { + statusText = "notmodified"; + + // If we have data, let's convert it + } else { + statusText = response.state; + success = response.data; + error = response.error; + isSuccess = !error; + } + } else { + + // Extract error from statusText and normalize for non-aborts + error = statusText; + if ( status || !statusText ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = ( nativeStatusText || statusText ) + ""; + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError", + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + return jqXHR; + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + } +} ); + +jQuery.each( [ "get", "post" ], function( _i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + + // Shift arguments if data argument was omitted + if ( isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + // The url can be an options object (which then must have .url) + return jQuery.ajax( jQuery.extend( { + url: url, + type: method, + dataType: type, + data: data, + success: callback + }, jQuery.isPlainObject( url ) && url ) ); + }; +} ); + +jQuery.ajaxPrefilter( function( s ) { + var i; + for ( i in s.headers ) { + if ( i.toLowerCase() === "content-type" ) { + s.contentType = s.headers[ i ] || ""; + } + } +} ); + + +jQuery._evalUrl = function( url, options, doc ) { + return jQuery.ajax( { + url: url, + + // Make this explicit, since user can override this through ajaxSetup (#11264) + type: "GET", + dataType: "script", + cache: true, + async: false, + global: false, + + // Only evaluate the response if it is successful (gh-4126) + // dataFilter is not invoked for failure responses, so using it instead + // of the default converter is kludgy but it works. + converters: { + "text script": function() {} + }, + dataFilter: function( response ) { + jQuery.globalEval( response, options, doc ); + } + } ); +}; + + +jQuery.fn.extend( { + wrapAll: function( html ) { + var wrap; + + if ( this[ 0 ] ) { + if ( isFunction( html ) ) { + html = html.call( this[ 0 ] ); + } + + // The elements to wrap the target around + wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + + if ( this[ 0 ].parentNode ) { + wrap.insertBefore( this[ 0 ] ); + } + + wrap.map( function() { + var elem = this; + + while ( elem.firstElementChild ) { + elem = elem.firstElementChild; + } + + return elem; + } ).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( isFunction( html ) ) { + return this.each( function( i ) { + jQuery( this ).wrapInner( html.call( this, i ) ); + } ); + } + + return this.each( function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + } ); + }, + + wrap: function( html ) { + var htmlIsFunction = isFunction( html ); + + return this.each( function( i ) { + jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html ); + } ); + }, + + unwrap: function( selector ) { + this.parent( selector ).not( "body" ).each( function() { + jQuery( this ).replaceWith( this.childNodes ); + } ); + return this; + } +} ); + + +jQuery.expr.pseudos.hidden = function( elem ) { + return !jQuery.expr.pseudos.visible( elem ); +}; +jQuery.expr.pseudos.visible = function( elem ) { + return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); +}; + + + + +jQuery.ajaxSettings.xhr = function() { + try { + return new window.XMLHttpRequest(); + } catch ( e ) {} +}; + +var xhrSuccessStatus = { + + // File protocol always yields status code 0, assume 200 + 0: 200, + + // Support: IE <=9 only + // #1450: sometimes IE returns 1223 when it should be 204 + 1223: 204 + }, + xhrSupported = jQuery.ajaxSettings.xhr(); + +support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported ); +support.ajax = xhrSupported = !!xhrSupported; + +jQuery.ajaxTransport( function( options ) { + var callback, errorCallback; + + // Cross domain only allowed if supported through XMLHttpRequest + if ( support.cors || xhrSupported && !options.crossDomain ) { + return { + send: function( headers, complete ) { + var i, + xhr = options.xhr(); + + xhr.open( + options.type, + options.url, + options.async, + options.username, + options.password + ); + + // Apply custom fields if provided + if ( options.xhrFields ) { + for ( i in options.xhrFields ) { + xhr[ i ] = options.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( options.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( options.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Set headers + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + + // Callback + callback = function( type ) { + return function() { + if ( callback ) { + callback = errorCallback = xhr.onload = + xhr.onerror = xhr.onabort = xhr.ontimeout = + xhr.onreadystatechange = null; + + if ( type === "abort" ) { + xhr.abort(); + } else if ( type === "error" ) { + + // Support: IE <=9 only + // On a manual native abort, IE9 throws + // errors on any property access that is not readyState + if ( typeof xhr.status !== "number" ) { + complete( 0, "error" ); + } else { + complete( + + // File: protocol always yields status 0; see #8605, #14207 + xhr.status, + xhr.statusText + ); + } + } else { + complete( + xhrSuccessStatus[ xhr.status ] || xhr.status, + xhr.statusText, + + // Support: IE <=9 only + // IE9 has no XHR2 but throws on binary (trac-11426) + // For XHR2 non-text, let the caller handle it (gh-2498) + ( xhr.responseType || "text" ) !== "text" || + typeof xhr.responseText !== "string" ? + { binary: xhr.response } : + { text: xhr.responseText }, + xhr.getAllResponseHeaders() + ); + } + } + }; + }; + + // Listen to events + xhr.onload = callback(); + errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" ); + + // Support: IE 9 only + // Use onreadystatechange to replace onabort + // to handle uncaught aborts + if ( xhr.onabort !== undefined ) { + xhr.onabort = errorCallback; + } else { + xhr.onreadystatechange = function() { + + // Check readyState before timeout as it changes + if ( xhr.readyState === 4 ) { + + // Allow onerror to be called first, + // but that will not handle a native abort + // Also, save errorCallback to a variable + // as xhr.onerror cannot be accessed + window.setTimeout( function() { + if ( callback ) { + errorCallback(); + } + } ); + } + }; + } + + // Create the abort callback + callback = callback( "abort" ); + + try { + + // Do send the request (this may raise an exception) + xhr.send( options.hasContent && options.data || null ); + } catch ( e ) { + + // #14683: Only rethrow if this hasn't been notified as an error yet + if ( callback ) { + throw e; + } + } + }, + + abort: function() { + if ( callback ) { + callback(); + } + } + }; + } +} ); + + + + +// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432) +jQuery.ajaxPrefilter( function( s ) { + if ( s.crossDomain ) { + s.contents.script = false; + } +} ); + +// Install script dataType +jQuery.ajaxSetup( { + accepts: { + script: "text/javascript, application/javascript, " + + "application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /\b(?:java|ecma)script\b/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +} ); + +// Handle cache's special case and crossDomain +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + } +} ); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function( s ) { + + // This transport only deals with cross domain or forced-by-attrs requests + if ( s.crossDomain || s.scriptAttrs ) { + var script, callback; + return { + send: function( _, complete ) { + script = jQuery( " +{% endmacro %} diff --git a/_static/scripts/pydata-sphinx-theme.js b/_static/scripts/pydata-sphinx-theme.js new file mode 100644 index 00000000..0e00c4ca --- /dev/null +++ b/_static/scripts/pydata-sphinx-theme.js @@ -0,0 +1,32 @@ +!function(t){var e={};function n(i){if(e[i])return e[i].exports;var o=e[i]={i:i,l:!1,exports:{}};return t[i].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=t,n.c=e,n.d=function(t,e,i){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:i})},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var i=Object.create(null);if(n.r(i),Object.defineProperty(i,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(i,o,function(e){return t[e]}.bind(null,o));return i},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=2)}([function(t,e){t.exports=jQuery},function(t,e,n){"use strict";n.r(e),function(t){ +/**! + * @fileOverview Kickass library to create and place poppers near their reference elements. + * @version 1.16.1 + * @license + * Copyright (c) 2016 Federico Zivolo and 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. + */ +var n="undefined"!=typeof window&&"undefined"!=typeof document&&"undefined"!=typeof navigator,i=function(){for(var t=["Edge","Trident","Firefox"],e=0;e=0)return 1;return 0}();var o=n&&window.Promise?function(t){var e=!1;return function(){e||(e=!0,window.Promise.resolve().then((function(){e=!1,t()})))}}:function(t){var e=!1;return function(){e||(e=!0,setTimeout((function(){e=!1,t()}),i))}};function r(t){return t&&"[object Function]"==={}.toString.call(t)}function a(t,e){if(1!==t.nodeType)return[];var n=t.ownerDocument.defaultView.getComputedStyle(t,null);return e?n[e]:n}function s(t){return"HTML"===t.nodeName?t:t.parentNode||t.host}function l(t){if(!t)return document.body;switch(t.nodeName){case"HTML":case"BODY":return t.ownerDocument.body;case"#document":return t.body}var e=a(t),n=e.overflow,i=e.overflowX,o=e.overflowY;return/(auto|scroll|overlay)/.test(n+o+i)?t:l(s(t))}function u(t){return t&&t.referenceNode?t.referenceNode:t}var f=n&&!(!window.MSInputMethodContext||!document.documentMode),d=n&&/MSIE 10/.test(navigator.userAgent);function c(t){return 11===t?f:10===t?d:f||d}function h(t){if(!t)return document.documentElement;for(var e=c(10)?document.body:null,n=t.offsetParent||null;n===e&&t.nextElementSibling;)n=(t=t.nextElementSibling).offsetParent;var i=n&&n.nodeName;return i&&"BODY"!==i&&"HTML"!==i?-1!==["TH","TD","TABLE"].indexOf(n.nodeName)&&"static"===a(n,"position")?h(n):n:t?t.ownerDocument.documentElement:document.documentElement}function p(t){return null!==t.parentNode?p(t.parentNode):t}function m(t,e){if(!(t&&t.nodeType&&e&&e.nodeType))return document.documentElement;var n=t.compareDocumentPosition(e)&Node.DOCUMENT_POSITION_FOLLOWING,i=n?t:e,o=n?e:t,r=document.createRange();r.setStart(i,0),r.setEnd(o,0);var a,s,l=r.commonAncestorContainer;if(t!==l&&e!==l||i.contains(o))return"BODY"===(s=(a=l).nodeName)||"HTML"!==s&&h(a.firstElementChild)!==a?h(l):l;var u=p(t);return u.host?m(u.host,e):m(t,p(e).host)}function g(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"top",n="top"===e?"scrollTop":"scrollLeft",i=t.nodeName;if("BODY"===i||"HTML"===i){var o=t.ownerDocument.documentElement,r=t.ownerDocument.scrollingElement||o;return r[n]}return t[n]}function v(t,e){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],i=g(e,"top"),o=g(e,"left"),r=n?-1:1;return t.top+=i*r,t.bottom+=i*r,t.left+=o*r,t.right+=o*r,t}function _(t,e){var n="x"===e?"Left":"Top",i="Left"===n?"Right":"Bottom";return parseFloat(t["border"+n+"Width"])+parseFloat(t["border"+i+"Width"])}function b(t,e,n,i){return Math.max(e["offset"+t],e["scroll"+t],n["client"+t],n["offset"+t],n["scroll"+t],c(10)?parseInt(n["offset"+t])+parseInt(i["margin"+("Height"===t?"Top":"Left")])+parseInt(i["margin"+("Height"===t?"Bottom":"Right")]):0)}function y(t){var e=t.body,n=t.documentElement,i=c(10)&&getComputedStyle(n);return{height:b("Height",e,n,i),width:b("Width",e,n,i)}}var w=function(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")},E=function(){function t(t,e){for(var n=0;n2&&void 0!==arguments[2]&&arguments[2],i=c(10),o="HTML"===e.nodeName,r=N(t),s=N(e),u=l(t),f=a(e),d=parseFloat(f.borderTopWidth),h=parseFloat(f.borderLeftWidth);n&&o&&(s.top=Math.max(s.top,0),s.left=Math.max(s.left,0));var p=S({top:r.top-s.top-d,left:r.left-s.left-h,width:r.width,height:r.height});if(p.marginTop=0,p.marginLeft=0,!i&&o){var m=parseFloat(f.marginTop),g=parseFloat(f.marginLeft);p.top-=d-m,p.bottom-=d-m,p.left-=h-g,p.right-=h-g,p.marginTop=m,p.marginLeft=g}return(i&&!n?e.contains(u):e===u&&"BODY"!==u.nodeName)&&(p=v(p,e)),p}function k(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=t.ownerDocument.documentElement,i=D(t,n),o=Math.max(n.clientWidth,window.innerWidth||0),r=Math.max(n.clientHeight,window.innerHeight||0),a=e?0:g(n),s=e?0:g(n,"left"),l={top:a-i.top+i.marginTop,left:s-i.left+i.marginLeft,width:o,height:r};return S(l)}function A(t){var e=t.nodeName;if("BODY"===e||"HTML"===e)return!1;if("fixed"===a(t,"position"))return!0;var n=s(t);return!!n&&A(n)}function I(t){if(!t||!t.parentElement||c())return document.documentElement;for(var e=t.parentElement;e&&"none"===a(e,"transform");)e=e.parentElement;return e||document.documentElement}function O(t,e,n,i){var o=arguments.length>4&&void 0!==arguments[4]&&arguments[4],r={top:0,left:0},a=o?I(t):m(t,u(e));if("viewport"===i)r=k(a,o);else{var f=void 0;"scrollParent"===i?"BODY"===(f=l(s(e))).nodeName&&(f=t.ownerDocument.documentElement):f="window"===i?t.ownerDocument.documentElement:i;var d=D(f,a,o);if("HTML"!==f.nodeName||A(a))r=d;else{var c=y(t.ownerDocument),h=c.height,p=c.width;r.top+=d.top-d.marginTop,r.bottom=h+d.top,r.left+=d.left-d.marginLeft,r.right=p+d.left}}var g="number"==typeof(n=n||0);return r.left+=g?n:n.left||0,r.top+=g?n:n.top||0,r.right-=g?n:n.right||0,r.bottom-=g?n:n.bottom||0,r}function x(t){return t.width*t.height}function j(t,e,n,i,o){var r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:0;if(-1===t.indexOf("auto"))return t;var a=O(n,i,r,o),s={top:{width:a.width,height:e.top-a.top},right:{width:a.right-e.right,height:a.height},bottom:{width:a.width,height:a.bottom-e.bottom},left:{width:e.left-a.left,height:a.height}},l=Object.keys(s).map((function(t){return C({key:t},s[t],{area:x(s[t])})})).sort((function(t,e){return e.area-t.area})),u=l.filter((function(t){var e=t.width,i=t.height;return e>=n.clientWidth&&i>=n.clientHeight})),f=u.length>0?u[0].key:l[0].key,d=t.split("-")[1];return f+(d?"-"+d:"")}function L(t,e,n){var i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null,o=i?I(e):m(e,u(n));return D(n,o,i)}function P(t){var e=t.ownerDocument.defaultView.getComputedStyle(t),n=parseFloat(e.marginTop||0)+parseFloat(e.marginBottom||0),i=parseFloat(e.marginLeft||0)+parseFloat(e.marginRight||0);return{width:t.offsetWidth+i,height:t.offsetHeight+n}}function F(t){var e={left:"right",right:"left",bottom:"top",top:"bottom"};return t.replace(/left|right|bottom|top/g,(function(t){return e[t]}))}function R(t,e,n){n=n.split("-")[0];var i=P(t),o={width:i.width,height:i.height},r=-1!==["right","left"].indexOf(n),a=r?"top":"left",s=r?"left":"top",l=r?"height":"width",u=r?"width":"height";return o[a]=e[a]+e[l]/2-i[l]/2,o[s]=n===s?e[s]-i[u]:e[F(s)],o}function M(t,e){return Array.prototype.find?t.find(e):t.filter(e)[0]}function B(t,e,n){return(void 0===n?t:t.slice(0,function(t,e,n){if(Array.prototype.findIndex)return t.findIndex((function(t){return t[e]===n}));var i=M(t,(function(t){return t[e]===n}));return t.indexOf(i)}(t,"name",n))).forEach((function(t){t.function&&console.warn("`modifier.function` is deprecated, use `modifier.fn`!");var n=t.function||t.fn;t.enabled&&r(n)&&(e.offsets.popper=S(e.offsets.popper),e.offsets.reference=S(e.offsets.reference),e=n(e,t))})),e}function H(){if(!this.state.isDestroyed){var t={instance:this,styles:{},arrowStyles:{},attributes:{},flipped:!1,offsets:{}};t.offsets.reference=L(this.state,this.popper,this.reference,this.options.positionFixed),t.placement=j(this.options.placement,t.offsets.reference,this.popper,this.reference,this.options.modifiers.flip.boundariesElement,this.options.modifiers.flip.padding),t.originalPlacement=t.placement,t.positionFixed=this.options.positionFixed,t.offsets.popper=R(this.popper,t.offsets.reference,t.placement),t.offsets.popper.position=this.options.positionFixed?"fixed":"absolute",t=B(this.modifiers,t),this.state.isCreated?this.options.onUpdate(t):(this.state.isCreated=!0,this.options.onCreate(t))}}function q(t,e){return t.some((function(t){var n=t.name;return t.enabled&&n===e}))}function Q(t){for(var e=[!1,"ms","Webkit","Moz","O"],n=t.charAt(0).toUpperCase()+t.slice(1),i=0;i1&&void 0!==arguments[1]&&arguments[1],n=Z.indexOf(t),i=Z.slice(n+1).concat(Z.slice(0,n));return e?i.reverse():i}var et="flip",nt="clockwise",it="counterclockwise";function ot(t,e,n,i){var o=[0,0],r=-1!==["right","left"].indexOf(i),a=t.split(/(\+|\-)/).map((function(t){return t.trim()})),s=a.indexOf(M(a,(function(t){return-1!==t.search(/,|\s/)})));a[s]&&-1===a[s].indexOf(",")&&console.warn("Offsets separated by white space(s) are deprecated, use a comma (,) instead.");var l=/\s*,\s*|\s+/,u=-1!==s?[a.slice(0,s).concat([a[s].split(l)[0]]),[a[s].split(l)[1]].concat(a.slice(s+1))]:[a];return(u=u.map((function(t,i){var o=(1===i?!r:r)?"height":"width",a=!1;return t.reduce((function(t,e){return""===t[t.length-1]&&-1!==["+","-"].indexOf(e)?(t[t.length-1]=e,a=!0,t):a?(t[t.length-1]+=e,a=!1,t):t.concat(e)}),[]).map((function(t){return function(t,e,n,i){var o=t.match(/((?:\-|\+)?\d*\.?\d*)(.*)/),r=+o[1],a=o[2];if(!r)return t;if(0===a.indexOf("%")){var s=void 0;switch(a){case"%p":s=n;break;case"%":case"%r":default:s=i}return S(s)[e]/100*r}if("vh"===a||"vw"===a){return("vh"===a?Math.max(document.documentElement.clientHeight,window.innerHeight||0):Math.max(document.documentElement.clientWidth,window.innerWidth||0))/100*r}return r}(t,o,e,n)}))}))).forEach((function(t,e){t.forEach((function(n,i){K(n)&&(o[e]+=n*("-"===t[i-1]?-1:1))}))})),o}var rt={placement:"bottom",positionFixed:!1,eventsEnabled:!0,removeOnDestroy:!1,onCreate:function(){},onUpdate:function(){},modifiers:{shift:{order:100,enabled:!0,fn:function(t){var e=t.placement,n=e.split("-")[0],i=e.split("-")[1];if(i){var o=t.offsets,r=o.reference,a=o.popper,s=-1!==["bottom","top"].indexOf(n),l=s?"left":"top",u=s?"width":"height",f={start:T({},l,r[l]),end:T({},l,r[l]+r[u]-a[u])};t.offsets.popper=C({},a,f[i])}return t}},offset:{order:200,enabled:!0,fn:function(t,e){var n=e.offset,i=t.placement,o=t.offsets,r=o.popper,a=o.reference,s=i.split("-")[0],l=void 0;return l=K(+n)?[+n,0]:ot(n,r,a,s),"left"===s?(r.top+=l[0],r.left-=l[1]):"right"===s?(r.top+=l[0],r.left+=l[1]):"top"===s?(r.left+=l[0],r.top-=l[1]):"bottom"===s&&(r.left+=l[0],r.top+=l[1]),t.popper=r,t},offset:0},preventOverflow:{order:300,enabled:!0,fn:function(t,e){var n=e.boundariesElement||h(t.instance.popper);t.instance.reference===n&&(n=h(n));var i=Q("transform"),o=t.instance.popper.style,r=o.top,a=o.left,s=o[i];o.top="",o.left="",o[i]="";var l=O(t.instance.popper,t.instance.reference,e.padding,n,t.positionFixed);o.top=r,o.left=a,o[i]=s,e.boundaries=l;var u=e.priority,f=t.offsets.popper,d={primary:function(t){var n=f[t];return f[t]l[t]&&!e.escapeWithReference&&(i=Math.min(f[n],l[t]-("right"===t?f.width:f.height))),T({},n,i)}};return u.forEach((function(t){var e=-1!==["left","top"].indexOf(t)?"primary":"secondary";f=C({},f,d[e](t))})),t.offsets.popper=f,t},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(t){var e=t.offsets,n=e.popper,i=e.reference,o=t.placement.split("-")[0],r=Math.floor,a=-1!==["top","bottom"].indexOf(o),s=a?"right":"bottom",l=a?"left":"top",u=a?"width":"height";return n[s]r(i[s])&&(t.offsets.popper[l]=r(i[s])),t}},arrow:{order:500,enabled:!0,fn:function(t,e){var n;if(!G(t.instance.modifiers,"arrow","keepTogether"))return t;var i=e.element;if("string"==typeof i){if(!(i=t.instance.popper.querySelector(i)))return t}else if(!t.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),t;var o=t.placement.split("-")[0],r=t.offsets,s=r.popper,l=r.reference,u=-1!==["left","right"].indexOf(o),f=u?"height":"width",d=u?"Top":"Left",c=d.toLowerCase(),h=u?"left":"top",p=u?"bottom":"right",m=P(i)[f];l[p]-ms[p]&&(t.offsets.popper[c]+=l[c]+m-s[p]),t.offsets.popper=S(t.offsets.popper);var g=l[c]+l[f]/2-m/2,v=a(t.instance.popper),_=parseFloat(v["margin"+d]),b=parseFloat(v["border"+d+"Width"]),y=g-t.offsets.popper[c]-_-b;return y=Math.max(Math.min(s[f]-m,y),0),t.arrowElement=i,t.offsets.arrow=(T(n={},c,Math.round(y)),T(n,h,""),n),t},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(t,e){if(q(t.instance.modifiers,"inner"))return t;if(t.flipped&&t.placement===t.originalPlacement)return t;var n=O(t.instance.popper,t.instance.reference,e.padding,e.boundariesElement,t.positionFixed),i=t.placement.split("-")[0],o=F(i),r=t.placement.split("-")[1]||"",a=[];switch(e.behavior){case et:a=[i,o];break;case nt:a=tt(i);break;case it:a=tt(i,!0);break;default:a=e.behavior}return a.forEach((function(s,l){if(i!==s||a.length===l+1)return t;i=t.placement.split("-")[0],o=F(i);var u=t.offsets.popper,f=t.offsets.reference,d=Math.floor,c="left"===i&&d(u.right)>d(f.left)||"right"===i&&d(u.left)d(f.top)||"bottom"===i&&d(u.top)d(n.right),m=d(u.top)d(n.bottom),v="left"===i&&h||"right"===i&&p||"top"===i&&m||"bottom"===i&&g,_=-1!==["top","bottom"].indexOf(i),b=!!e.flipVariations&&(_&&"start"===r&&h||_&&"end"===r&&p||!_&&"start"===r&&m||!_&&"end"===r&&g),y=!!e.flipVariationsByContent&&(_&&"start"===r&&p||_&&"end"===r&&h||!_&&"start"===r&&g||!_&&"end"===r&&m),w=b||y;(c||v||w)&&(t.flipped=!0,(c||v)&&(i=a[l+1]),w&&(r=function(t){return"end"===t?"start":"start"===t?"end":t}(r)),t.placement=i+(r?"-"+r:""),t.offsets.popper=C({},t.offsets.popper,R(t.instance.popper,t.offsets.reference,t.placement)),t=B(t.instance.modifiers,t,"flip"))})),t},behavior:"flip",padding:5,boundariesElement:"viewport",flipVariations:!1,flipVariationsByContent:!1},inner:{order:700,enabled:!1,fn:function(t){var e=t.placement,n=e.split("-")[0],i=t.offsets,o=i.popper,r=i.reference,a=-1!==["left","right"].indexOf(n),s=-1===["top","left"].indexOf(n);return o[a?"left":"top"]=r[n]-(s?o[a?"width":"height"]:0),t.placement=F(e),t.offsets.popper=S(o),t}},hide:{order:800,enabled:!0,fn:function(t){if(!G(t.instance.modifiers,"hide","preventOverflow"))return t;var e=t.offsets.reference,n=M(t.instance.modifiers,(function(t){return"preventOverflow"===t.name})).boundaries;if(e.bottomn.right||e.top>n.bottom||e.right2&&void 0!==arguments[2]?arguments[2]:{};w(this,t),this.scheduleUpdate=function(){return requestAnimationFrame(i.update)},this.update=o(this.update.bind(this)),this.options=C({},t.Defaults,a),this.state={isDestroyed:!1,isCreated:!1,scrollParents:[]},this.reference=e&&e.jquery?e[0]:e,this.popper=n&&n.jquery?n[0]:n,this.options.modifiers={},Object.keys(C({},t.Defaults.modifiers,a.modifiers)).forEach((function(e){i.options.modifiers[e]=C({},t.Defaults.modifiers[e]||{},a.modifiers?a.modifiers[e]:{})})),this.modifiers=Object.keys(this.options.modifiers).map((function(t){return C({name:t},i.options.modifiers[t])})).sort((function(t,e){return t.order-e.order})),this.modifiers.forEach((function(t){t.enabled&&r(t.onLoad)&&t.onLoad(i.reference,i.popper,i.options,t,i.state)})),this.update();var s=this.options.eventsEnabled;s&&this.enableEventListeners(),this.state.eventsEnabled=s}return E(t,[{key:"update",value:function(){return H.call(this)}},{key:"destroy",value:function(){return W.call(this)}},{key:"enableEventListeners",value:function(){return Y.call(this)}},{key:"disableEventListeners",value:function(){return z.call(this)}}]),t}();at.Utils=("undefined"!=typeof window?window:t).PopperUtils,at.placements=J,at.Defaults=rt,e.default=at}.call(this,n(4))},function(t,e,n){t.exports=n(5)},function(t,e,n){ +/*! + * Bootstrap v4.6.1 (https://getbootstrap.com/) + * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +!function(t,e,n){"use strict";function i(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var o=i(e),r=i(n);function a(t,e){for(var n=0;n=4)throw new Error("Bootstrap's JavaScript requires at least jQuery v1.9.1 but less than v4.0.0")}};d.jQueryDetection(),o.default.fn.emulateTransitionEnd=f,o.default.event.special[d.TRANSITION_END]={bindType:"transitionend",delegateType:"transitionend",handle:function(t){if(o.default(t.target).is(this))return t.handleObj.handler.apply(this,arguments)}};var c=o.default.fn.alert,h=function(){function t(t){this._element=t}var e=t.prototype;return e.close=function(t){var e=this._element;t&&(e=this._getRootElement(t)),this._triggerCloseEvent(e).isDefaultPrevented()||this._removeElement(e)},e.dispose=function(){o.default.removeData(this._element,"bs.alert"),this._element=null},e._getRootElement=function(t){var e=d.getSelectorFromElement(t),n=!1;return e&&(n=document.querySelector(e)),n||(n=o.default(t).closest(".alert")[0]),n},e._triggerCloseEvent=function(t){var e=o.default.Event("close.bs.alert");return o.default(t).trigger(e),e},e._removeElement=function(t){var e=this;if(o.default(t).removeClass("show"),o.default(t).hasClass("fade")){var n=d.getTransitionDurationFromElement(t);o.default(t).one(d.TRANSITION_END,(function(n){return e._destroyElement(t,n)})).emulateTransitionEnd(n)}else this._destroyElement(t)},e._destroyElement=function(t){o.default(t).detach().trigger("closed.bs.alert").remove()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.alert");i||(i=new t(this),n.data("bs.alert",i)),"close"===e&&i[e](this)}))},t._handleDismiss=function(t){return function(e){e&&e.preventDefault(),t.close(this)}},s(t,null,[{key:"VERSION",get:function(){return"4.6.1"}}]),t}();o.default(document).on("click.bs.alert.data-api",'[data-dismiss="alert"]',h._handleDismiss(new h)),o.default.fn.alert=h._jQueryInterface,o.default.fn.alert.Constructor=h,o.default.fn.alert.noConflict=function(){return o.default.fn.alert=c,h._jQueryInterface};var p=o.default.fn.button,m=function(){function t(t){this._element=t,this.shouldAvoidTriggerChange=!1}var e=t.prototype;return e.toggle=function(){var t=!0,e=!0,n=o.default(this._element).closest('[data-toggle="buttons"]')[0];if(n){var i=this._element.querySelector('input:not([type="hidden"])');if(i){if("radio"===i.type)if(i.checked&&this._element.classList.contains("active"))t=!1;else{var r=n.querySelector(".active");r&&o.default(r).removeClass("active")}t&&("checkbox"!==i.type&&"radio"!==i.type||(i.checked=!this._element.classList.contains("active")),this.shouldAvoidTriggerChange||o.default(i).trigger("change")),i.focus(),e=!1}}this._element.hasAttribute("disabled")||this._element.classList.contains("disabled")||(e&&this._element.setAttribute("aria-pressed",!this._element.classList.contains("active")),t&&o.default(this._element).toggleClass("active"))},e.dispose=function(){o.default.removeData(this._element,"bs.button"),this._element=null},t._jQueryInterface=function(e,n){return this.each((function(){var i=o.default(this),r=i.data("bs.button");r||(r=new t(this),i.data("bs.button",r)),r.shouldAvoidTriggerChange=n,"toggle"===e&&r[e]()}))},s(t,null,[{key:"VERSION",get:function(){return"4.6.1"}}]),t}();o.default(document).on("click.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=t.target,n=e;if(o.default(e).hasClass("btn")||(e=o.default(e).closest(".btn")[0]),!e||e.hasAttribute("disabled")||e.classList.contains("disabled"))t.preventDefault();else{var i=e.querySelector('input:not([type="hidden"])');if(i&&(i.hasAttribute("disabled")||i.classList.contains("disabled")))return void t.preventDefault();"INPUT"!==n.tagName&&"LABEL"===e.tagName||m._jQueryInterface.call(o.default(e),"toggle","INPUT"===n.tagName)}})).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',(function(t){var e=o.default(t.target).closest(".btn")[0];o.default(e).toggleClass("focus",/^focus(in)?$/.test(t.type))})),o.default(window).on("load.bs.button.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-toggle="buttons"] .btn')),e=0,n=t.length;e0,this._pointerEvent=Boolean(window.PointerEvent||window.MSPointerEvent),this._addEventListeners()}var e=t.prototype;return e.next=function(){this._isSliding||this._slide("next")},e.nextWhenVisible=function(){var t=o.default(this._element);!document.hidden&&t.is(":visible")&&"hidden"!==t.css("visibility")&&this.next()},e.prev=function(){this._isSliding||this._slide("prev")},e.pause=function(t){t||(this._isPaused=!0),this._element.querySelector(".carousel-item-next, .carousel-item-prev")&&(d.triggerTransitionEnd(this._element),this.cycle(!0)),clearInterval(this._interval),this._interval=null},e.cycle=function(t){t||(this._isPaused=!1),this._interval&&(clearInterval(this._interval),this._interval=null),this._config.interval&&!this._isPaused&&(this._updateInterval(),this._interval=setInterval((document.visibilityState?this.nextWhenVisible:this.next).bind(this),this._config.interval))},e.to=function(t){var e=this;this._activeElement=this._element.querySelector(".active.carousel-item");var n=this._getItemIndex(this._activeElement);if(!(t>this._items.length-1||t<0))if(this._isSliding)o.default(this._element).one("slid.bs.carousel",(function(){return e.to(t)}));else{if(n===t)return this.pause(),void this.cycle();var i=t>n?"next":"prev";this._slide(i,this._items[t])}},e.dispose=function(){o.default(this._element).off(v),o.default.removeData(this._element,"bs.carousel"),this._items=null,this._config=null,this._element=null,this._interval=null,this._isPaused=null,this._isSliding=null,this._activeElement=null,this._indicatorsElement=null},e._getConfig=function(t){return t=l({},b,t),d.typeCheckConfig(g,t,y),t},e._handleSwipe=function(){var t=Math.abs(this.touchDeltaX);if(!(t<=40)){var e=t/this.touchDeltaX;this.touchDeltaX=0,e>0&&this.prev(),e<0&&this.next()}},e._addEventListeners=function(){var t=this;this._config.keyboard&&o.default(this._element).on("keydown.bs.carousel",(function(e){return t._keydown(e)})),"hover"===this._config.pause&&o.default(this._element).on("mouseenter.bs.carousel",(function(e){return t.pause(e)})).on("mouseleave.bs.carousel",(function(e){return t.cycle(e)})),this._config.touch&&this._addTouchEventListeners()},e._addTouchEventListeners=function(){var t=this;if(this._touchSupported){var e=function(e){t._pointerEvent&&w[e.originalEvent.pointerType.toUpperCase()]?t.touchStartX=e.originalEvent.clientX:t._pointerEvent||(t.touchStartX=e.originalEvent.touches[0].clientX)},n=function(e){t._pointerEvent&&w[e.originalEvent.pointerType.toUpperCase()]&&(t.touchDeltaX=e.originalEvent.clientX-t.touchStartX),t._handleSwipe(),"hover"===t._config.pause&&(t.pause(),t.touchTimeout&&clearTimeout(t.touchTimeout),t.touchTimeout=setTimeout((function(e){return t.cycle(e)}),500+t._config.interval))};o.default(this._element.querySelectorAll(".carousel-item img")).on("dragstart.bs.carousel",(function(t){return t.preventDefault()})),this._pointerEvent?(o.default(this._element).on("pointerdown.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("pointerup.bs.carousel",(function(t){return n(t)})),this._element.classList.add("pointer-event")):(o.default(this._element).on("touchstart.bs.carousel",(function(t){return e(t)})),o.default(this._element).on("touchmove.bs.carousel",(function(e){return function(e){t.touchDeltaX=e.originalEvent.touches&&e.originalEvent.touches.length>1?0:e.originalEvent.touches[0].clientX-t.touchStartX}(e)})),o.default(this._element).on("touchend.bs.carousel",(function(t){return n(t)})))}},e._keydown=function(t){if(!/input|textarea/i.test(t.target.tagName))switch(t.which){case 37:t.preventDefault(),this.prev();break;case 39:t.preventDefault(),this.next()}},e._getItemIndex=function(t){return this._items=t&&t.parentNode?[].slice.call(t.parentNode.querySelectorAll(".carousel-item")):[],this._items.indexOf(t)},e._getItemByDirection=function(t,e){var n="next"===t,i="prev"===t,o=this._getItemIndex(e),r=this._items.length-1;if((i&&0===o||n&&o===r)&&!this._config.wrap)return e;var a=(o+("prev"===t?-1:1))%this._items.length;return-1===a?this._items[this._items.length-1]:this._items[a]},e._triggerSlideEvent=function(t,e){var n=this._getItemIndex(t),i=this._getItemIndex(this._element.querySelector(".active.carousel-item")),r=o.default.Event("slide.bs.carousel",{relatedTarget:t,direction:e,from:i,to:n});return o.default(this._element).trigger(r),r},e._setActiveIndicatorElement=function(t){if(this._indicatorsElement){var e=[].slice.call(this._indicatorsElement.querySelectorAll(".active"));o.default(e).removeClass("active");var n=this._indicatorsElement.children[this._getItemIndex(t)];n&&o.default(n).addClass("active")}},e._updateInterval=function(){var t=this._activeElement||this._element.querySelector(".active.carousel-item");if(t){var e=parseInt(t.getAttribute("data-interval"),10);e?(this._config.defaultInterval=this._config.defaultInterval||this._config.interval,this._config.interval=e):this._config.interval=this._config.defaultInterval||this._config.interval}},e._slide=function(t,e){var n,i,r,a=this,s=this._element.querySelector(".active.carousel-item"),l=this._getItemIndex(s),u=e||s&&this._getItemByDirection(t,s),f=this._getItemIndex(u),c=Boolean(this._interval);if("next"===t?(n="carousel-item-left",i="carousel-item-next",r="left"):(n="carousel-item-right",i="carousel-item-prev",r="right"),u&&o.default(u).hasClass("active"))this._isSliding=!1;else if(!this._triggerSlideEvent(u,r).isDefaultPrevented()&&s&&u){this._isSliding=!0,c&&this.pause(),this._setActiveIndicatorElement(u),this._activeElement=u;var h=o.default.Event("slid.bs.carousel",{relatedTarget:u,direction:r,from:l,to:f});if(o.default(this._element).hasClass("slide")){o.default(u).addClass(i),d.reflow(u),o.default(s).addClass(n),o.default(u).addClass(n);var p=d.getTransitionDurationFromElement(s);o.default(s).one(d.TRANSITION_END,(function(){o.default(u).removeClass(n+" "+i).addClass("active"),o.default(s).removeClass("active "+i+" "+n),a._isSliding=!1,setTimeout((function(){return o.default(a._element).trigger(h)}),0)})).emulateTransitionEnd(p)}else o.default(s).removeClass("active"),o.default(u).addClass("active"),this._isSliding=!1,o.default(this._element).trigger(h);c&&this.cycle()}},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.carousel"),i=l({},b,o.default(this).data());"object"==typeof e&&(i=l({},i,e));var r="string"==typeof e?e:i.slide;if(n||(n=new t(this,i),o.default(this).data("bs.carousel",n)),"number"==typeof e)n.to(e);else if("string"==typeof r){if(void 0===n[r])throw new TypeError('No method named "'+r+'"');n[r]()}else i.interval&&i.ride&&(n.pause(),n.cycle())}))},t._dataApiClickHandler=function(e){var n=d.getSelectorFromElement(this);if(n){var i=o.default(n)[0];if(i&&o.default(i).hasClass("carousel")){var r=l({},o.default(i).data(),o.default(this).data()),a=this.getAttribute("data-slide-to");a&&(r.interval=!1),t._jQueryInterface.call(o.default(i),r),a&&o.default(i).data("bs.carousel").to(a),e.preventDefault()}}},s(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return b}}]),t}();o.default(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",E._dataApiClickHandler),o.default(window).on("load.bs.carousel.data-api",(function(){for(var t=[].slice.call(document.querySelectorAll('[data-ride="carousel"]')),e=0,n=t.length;e0&&(this._selector=a,this._triggerArray.push(r))}this._parent=this._config.parent?this._getParent():null,this._config.parent||this._addAriaAndCollapsedClass(this._element,this._triggerArray),this._config.toggle&&this.toggle()}var e=t.prototype;return e.toggle=function(){o.default(this._element).hasClass("show")?this.hide():this.show()},e.show=function(){var e,n,i=this;if(!(this._isTransitioning||o.default(this._element).hasClass("show")||(this._parent&&0===(e=[].slice.call(this._parent.querySelectorAll(".show, .collapsing")).filter((function(t){return"string"==typeof i._config.parent?t.getAttribute("data-parent")===i._config.parent:t.classList.contains("collapse")}))).length&&(e=null),e&&(n=o.default(e).not(this._selector).data("bs.collapse"))&&n._isTransitioning))){var r=o.default.Event("show.bs.collapse");if(o.default(this._element).trigger(r),!r.isDefaultPrevented()){e&&(t._jQueryInterface.call(o.default(e).not(this._selector),"hide"),n||o.default(e).data("bs.collapse",null));var a=this._getDimension();o.default(this._element).removeClass("collapse").addClass("collapsing"),this._element.style[a]=0,this._triggerArray.length&&o.default(this._triggerArray).removeClass("collapsed").attr("aria-expanded",!0),this.setTransitioning(!0);var s="scroll"+(a[0].toUpperCase()+a.slice(1)),l=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,(function(){o.default(i._element).removeClass("collapsing").addClass("collapse show"),i._element.style[a]="",i.setTransitioning(!1),o.default(i._element).trigger("shown.bs.collapse")})).emulateTransitionEnd(l),this._element.style[a]=this._element[s]+"px"}}},e.hide=function(){var t=this;if(!this._isTransitioning&&o.default(this._element).hasClass("show")){var e=o.default.Event("hide.bs.collapse");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){var n=this._getDimension();this._element.style[n]=this._element.getBoundingClientRect()[n]+"px",d.reflow(this._element),o.default(this._element).addClass("collapsing").removeClass("collapse show");var i=this._triggerArray.length;if(i>0)for(var r=0;r0},e._getOffset=function(){var t=this,e={};return"function"==typeof this._config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,t._config.offset(e.offsets,t._element)),e}:e.offset=this._config.offset,e},e._getPopperConfig=function(){var t={placement:this._getPlacement(),modifiers:{offset:this._getOffset(),flip:{enabled:this._config.flip},preventOverflow:{boundariesElement:this._config.boundary}}};return"static"===this._config.display&&(t.modifiers.applyStyle={enabled:!1}),l({},t,this._config.popperConfig)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this).data("bs.dropdown");if(n||(n=new t(this,"object"==typeof e?e:null),o.default(this).data("bs.dropdown",n)),"string"==typeof e){if(void 0===n[e])throw new TypeError('No method named "'+e+'"');n[e]()}}))},t._clearMenus=function(e){if(!e||3!==e.which&&("keyup"!==e.type||9===e.which))for(var n=[].slice.call(document.querySelectorAll('[data-toggle="dropdown"]')),i=0,r=n.length;i0&&a--,40===e.which&&adocument.documentElement.clientHeight;n||(this._element.style.overflowY="hidden"),this._element.classList.add("modal-static");var i=d.getTransitionDurationFromElement(this._dialog);o.default(this._element).off(d.TRANSITION_END),o.default(this._element).one(d.TRANSITION_END,(function(){t._element.classList.remove("modal-static"),n||o.default(t._element).one(d.TRANSITION_END,(function(){t._element.style.overflowY=""})).emulateTransitionEnd(t._element,i)})).emulateTransitionEnd(i),this._element.focus()}},e._showElement=function(t){var e=this,n=o.default(this._element).hasClass("fade"),i=this._dialog?this._dialog.querySelector(".modal-body"):null;this._element.parentNode&&this._element.parentNode.nodeType===Node.ELEMENT_NODE||document.body.appendChild(this._element),this._element.style.display="block",this._element.removeAttribute("aria-hidden"),this._element.setAttribute("aria-modal",!0),this._element.setAttribute("role","dialog"),o.default(this._dialog).hasClass("modal-dialog-scrollable")&&i?i.scrollTop=0:this._element.scrollTop=0,n&&d.reflow(this._element),o.default(this._element).addClass("show"),this._config.focus&&this._enforceFocus();var r=o.default.Event("shown.bs.modal",{relatedTarget:t}),a=function(){e._config.focus&&e._element.focus(),e._isTransitioning=!1,o.default(e._element).trigger(r)};if(n){var s=d.getTransitionDurationFromElement(this._dialog);o.default(this._dialog).one(d.TRANSITION_END,a).emulateTransitionEnd(s)}else a()},e._enforceFocus=function(){var t=this;o.default(document).off("focusin.bs.modal").on("focusin.bs.modal",(function(e){document!==e.target&&t._element!==e.target&&0===o.default(t._element).has(e.target).length&&t._element.focus()}))},e._setEscapeEvent=function(){var t=this;this._isShown?o.default(this._element).on("keydown.dismiss.bs.modal",(function(e){t._config.keyboard&&27===e.which?(e.preventDefault(),t.hide()):t._config.keyboard||27!==e.which||t._triggerBackdropTransition()})):this._isShown||o.default(this._element).off("keydown.dismiss.bs.modal")},e._setResizeEvent=function(){var t=this;this._isShown?o.default(window).on("resize.bs.modal",(function(e){return t.handleUpdate(e)})):o.default(window).off("resize.bs.modal")},e._hideModal=function(){var t=this;this._element.style.display="none",this._element.setAttribute("aria-hidden",!0),this._element.removeAttribute("aria-modal"),this._element.removeAttribute("role"),this._isTransitioning=!1,this._showBackdrop((function(){o.default(document.body).removeClass("modal-open"),t._resetAdjustments(),t._resetScrollbar(),o.default(t._element).trigger("hidden.bs.modal")}))},e._removeBackdrop=function(){this._backdrop&&(o.default(this._backdrop).remove(),this._backdrop=null)},e._showBackdrop=function(t){var e=this,n=o.default(this._element).hasClass("fade")?"fade":"";if(this._isShown&&this._config.backdrop){if(this._backdrop=document.createElement("div"),this._backdrop.className="modal-backdrop",n&&this._backdrop.classList.add(n),o.default(this._backdrop).appendTo(document.body),o.default(this._element).on("click.dismiss.bs.modal",(function(t){e._ignoreBackdropClick?e._ignoreBackdropClick=!1:t.target===t.currentTarget&&("static"===e._config.backdrop?e._triggerBackdropTransition():e.hide())})),n&&d.reflow(this._backdrop),o.default(this._backdrop).addClass("show"),!t)return;if(!n)return void t();var i=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,t).emulateTransitionEnd(i)}else if(!this._isShown&&this._backdrop){o.default(this._backdrop).removeClass("show");var r=function(){e._removeBackdrop(),t&&t()};if(o.default(this._element).hasClass("fade")){var a=d.getTransitionDurationFromElement(this._backdrop);o.default(this._backdrop).one(d.TRANSITION_END,r).emulateTransitionEnd(a)}else r()}else t&&t()},e._adjustDialog=function(){var t=this._element.scrollHeight>document.documentElement.clientHeight;!this._isBodyOverflowing&&t&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!t&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var t=document.body.getBoundingClientRect();this._isBodyOverflowing=Math.round(t.left+t.right)
',trigger:"hover focus",title:"",delay:0,html:!1,selector:!1,placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent",customClass:"",sanitize:!0,sanitizeFn:null,whiteList:B,popperConfig:null},X={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string|function)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)",customClass:"(string|function)",sanitize:"boolean",sanitizeFn:"(null|function)",whiteList:"object",popperConfig:"(null|object)"},$={HIDE:"hide.bs.tooltip",HIDDEN:"hidden.bs.tooltip",SHOW:"show.bs.tooltip",SHOWN:"shown.bs.tooltip",INSERTED:"inserted.bs.tooltip",CLICK:"click.bs.tooltip",FOCUSIN:"focusin.bs.tooltip",FOCUSOUT:"focusout.bs.tooltip",MOUSEENTER:"mouseenter.bs.tooltip",MOUSELEAVE:"mouseleave.bs.tooltip"},G=function(){function t(t,e){if(void 0===r.default)throw new TypeError("Bootstrap's tooltips require Popper (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=t,this.config=this._getConfig(e),this.tip=null,this._setListeners()}var e=t.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(t){if(this._isEnabled)if(t){var e=this.constructor.DATA_KEY,n=o.default(t.currentTarget).data(e);n||(n=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(e,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(o.default(this.getTipElement()).hasClass("show"))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),o.default.removeData(this.element,this.constructor.DATA_KEY),o.default(this.element).off(this.constructor.EVENT_KEY),o.default(this.element).closest(".modal").off("hide.bs.modal",this._hideModalHandler),this.tip&&o.default(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,this._activeTrigger=null,this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===o.default(this.element).css("display"))throw new Error("Please use show on visible elements");var e=o.default.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){o.default(this.element).trigger(e);var n=d.findShadowRoot(this.element),i=o.default.contains(null!==n?n:this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!i)return;var a=this.getTipElement(),s=d.getUID(this.constructor.NAME);a.setAttribute("id",s),this.element.setAttribute("aria-describedby",s),this.setContent(),this.config.animation&&o.default(a).addClass("fade");var l="function"==typeof this.config.placement?this.config.placement.call(this,a,this.element):this.config.placement,u=this._getAttachment(l);this.addAttachmentClass(u);var f=this._getContainer();o.default(a).data(this.constructor.DATA_KEY,this),o.default.contains(this.element.ownerDocument.documentElement,this.tip)||o.default(a).appendTo(f),o.default(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new r.default(this.element,a,this._getPopperConfig(u)),o.default(a).addClass("show"),o.default(a).addClass(this.config.customClass),"ontouchstart"in document.documentElement&&o.default(document.body).children().on("mouseover",null,o.default.noop);var c=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,o.default(t.element).trigger(t.constructor.Event.SHOWN),"out"===e&&t._leave(null,t)};if(o.default(this.tip).hasClass("fade")){var h=d.getTransitionDurationFromElement(this.tip);o.default(this.tip).one(d.TRANSITION_END,c).emulateTransitionEnd(h)}else c()}},e.hide=function(t){var e=this,n=this.getTipElement(),i=o.default.Event(this.constructor.Event.HIDE),r=function(){"show"!==e._hoverState&&n.parentNode&&n.parentNode.removeChild(n),e._cleanTipClass(),e.element.removeAttribute("aria-describedby"),o.default(e.element).trigger(e.constructor.Event.HIDDEN),null!==e._popper&&e._popper.destroy(),t&&t()};if(o.default(this.element).trigger(i),!i.isDefaultPrevented()){if(o.default(n).removeClass("show"),"ontouchstart"in document.documentElement&&o.default(document.body).children().off("mouseover",null,o.default.noop),this._activeTrigger.click=!1,this._activeTrigger.focus=!1,this._activeTrigger.hover=!1,o.default(this.tip).hasClass("fade")){var a=d.getTransitionDurationFromElement(n);o.default(n).one(d.TRANSITION_END,r).emulateTransitionEnd(a)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-tooltip-"+t)},e.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},e.setContent=function(){var t=this.getTipElement();this.setElementContent(o.default(t.querySelectorAll(".tooltip-inner")),this.getTitle()),o.default(t).removeClass("fade show")},e.setElementContent=function(t,e){"object"!=typeof e||!e.nodeType&&!e.jquery?this.config.html?(this.config.sanitize&&(e=Q(e,this.config.whiteList,this.config.sanitizeFn)),t.html(e)):t.text(e):this.config.html?o.default(e).parent().is(t)||t.empty().append(e):t.text(o.default(e).text())},e.getTitle=function(){var t=this.element.getAttribute("data-original-title");return t||(t="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),t},e._getPopperConfig=function(t){var e=this;return l({},{placement:t,modifiers:{offset:this._getOffset(),flip:{behavior:this.config.fallbackPlacement},arrow:{element:".arrow"},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(t){t.originalPlacement!==t.placement&&e._handlePopperPlacementChange(t)},onUpdate:function(t){return e._handlePopperPlacementChange(t)}},this.config.popperConfig)},e._getOffset=function(){var t=this,e={};return"function"==typeof this.config.offset?e.fn=function(e){return e.offsets=l({},e.offsets,t.config.offset(e.offsets,t.element)),e}:e.offset=this.config.offset,e},e._getContainer=function(){return!1===this.config.container?document.body:d.isElement(this.config.container)?o.default(this.config.container):o.default(document).find(this.config.container)},e._getAttachment=function(t){return z[t.toUpperCase()]},e._setListeners=function(){var t=this;this.config.trigger.split(" ").forEach((function(e){if("click"===e)o.default(t.element).on(t.constructor.Event.CLICK,t.config.selector,(function(e){return t.toggle(e)}));else if("manual"!==e){var n="hover"===e?t.constructor.Event.MOUSEENTER:t.constructor.Event.FOCUSIN,i="hover"===e?t.constructor.Event.MOUSELEAVE:t.constructor.Event.FOCUSOUT;o.default(t.element).on(n,t.config.selector,(function(e){return t._enter(e)})).on(i,t.config.selector,(function(e){return t._leave(e)}))}})),this._hideModalHandler=function(){t.element&&t.hide()},o.default(this.element).closest(".modal").on("hide.bs.modal",this._hideModalHandler),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var t=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==t)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusin"===t.type?"focus":"hover"]=!0),o.default(e.getTipElement()).hasClass("show")||"show"===e._hoverState?e._hoverState="show":(clearTimeout(e._timeout),e._hoverState="show",e.config.delay&&e.config.delay.show?e._timeout=setTimeout((function(){"show"===e._hoverState&&e.show()}),e.config.delay.show):e.show())},e._leave=function(t,e){var n=this.constructor.DATA_KEY;(e=e||o.default(t.currentTarget).data(n))||(e=new this.constructor(t.currentTarget,this._getDelegateConfig()),o.default(t.currentTarget).data(n,e)),t&&(e._activeTrigger["focusout"===t.type?"focus":"hover"]=!1),e._isWithActiveTrigger()||(clearTimeout(e._timeout),e._hoverState="out",e.config.delay&&e.config.delay.hide?e._timeout=setTimeout((function(){"out"===e._hoverState&&e.hide()}),e.config.delay.hide):e.hide())},e._isWithActiveTrigger=function(){for(var t in this._activeTrigger)if(this._activeTrigger[t])return!0;return!1},e._getConfig=function(t){var e=o.default(this.element).data();return Object.keys(e).forEach((function(t){-1!==Y.indexOf(t)&&delete e[t]})),"number"==typeof(t=l({},this.constructor.Default,e,"object"==typeof t&&t?t:{})).delay&&(t.delay={show:t.delay,hide:t.delay}),"number"==typeof t.title&&(t.title=t.title.toString()),"number"==typeof t.content&&(t.content=t.content.toString()),d.typeCheckConfig(W,t,this.constructor.DefaultType),t.sanitize&&(t.template=Q(t.template,t.whiteList,t.sanitizeFn)),t},e._getDelegateConfig=function(){var t={};if(this.config)for(var e in this.config)this.constructor.Default[e]!==this.config[e]&&(t[e]=this.config[e]);return t},e._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(V);null!==e&&e.length&&t.removeClass(e.join(""))},e._handlePopperPlacementChange=function(t){this.tip=t.instance.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(t.placement))},e._fixTransition=function(){var t=this.getTipElement(),e=this.config.animation;null===t.getAttribute("x-placement")&&(o.default(t).removeClass("fade"),this.config.animation=!1,this.hide(),this.show(),this.config.animation=e)},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.tooltip"),r="object"==typeof e&&e;if((i||!/dispose|hide/.test(e))&&(i||(i=new t(this,r),n.data("bs.tooltip",i)),"string"==typeof e)){if(void 0===i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},s(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return K}},{key:"NAME",get:function(){return W}},{key:"DATA_KEY",get:function(){return"bs.tooltip"}},{key:"Event",get:function(){return $}},{key:"EVENT_KEY",get:function(){return".bs.tooltip"}},{key:"DefaultType",get:function(){return X}}]),t}();o.default.fn[W]=G._jQueryInterface,o.default.fn[W].Constructor=G,o.default.fn[W].noConflict=function(){return o.default.fn[W]=U,G._jQueryInterface};var J="popover",Z=o.default.fn[J],tt=new RegExp("(^|\\s)bs-popover\\S+","g"),et=l({},G.Default,{placement:"right",trigger:"click",content:"",template:''}),nt=l({},G.DefaultType,{content:"(string|element|function)"}),it={HIDE:"hide.bs.popover",HIDDEN:"hidden.bs.popover",SHOW:"show.bs.popover",SHOWN:"shown.bs.popover",INSERTED:"inserted.bs.popover",CLICK:"click.bs.popover",FOCUSIN:"focusin.bs.popover",FOCUSOUT:"focusout.bs.popover",MOUSEENTER:"mouseenter.bs.popover",MOUSELEAVE:"mouseleave.bs.popover"},ot=function(t){var e,n;function i(){return t.apply(this,arguments)||this}n=t,(e=i).prototype=Object.create(n.prototype),e.prototype.constructor=e,u(e,n);var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(t){o.default(this.getTipElement()).addClass("bs-popover-"+t)},r.getTipElement=function(){return this.tip=this.tip||o.default(this.config.template)[0],this.tip},r.setContent=function(){var t=o.default(this.getTipElement());this.setElementContent(t.find(".popover-header"),this.getTitle());var e=this._getContent();"function"==typeof e&&(e=e.call(this.element)),this.setElementContent(t.find(".popover-body"),e),t.removeClass("fade show")},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var t=o.default(this.getTipElement()),e=t.attr("class").match(tt);null!==e&&e.length>0&&t.removeClass(e.join(""))},i._jQueryInterface=function(t){return this.each((function(){var e=o.default(this).data("bs.popover"),n="object"==typeof t?t:null;if((e||!/dispose|hide/.test(t))&&(e||(e=new i(this,n),o.default(this).data("bs.popover",e)),"string"==typeof t)){if(void 0===e[t])throw new TypeError('No method named "'+t+'"');e[t]()}}))},s(i,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"Default",get:function(){return et}},{key:"NAME",get:function(){return J}},{key:"DATA_KEY",get:function(){return"bs.popover"}},{key:"Event",get:function(){return it}},{key:"EVENT_KEY",get:function(){return".bs.popover"}},{key:"DefaultType",get:function(){return nt}}]),i}(G);o.default.fn[J]=ot._jQueryInterface,o.default.fn[J].Constructor=ot,o.default.fn[J].noConflict=function(){return o.default.fn[J]=Z,ot._jQueryInterface};var rt="scrollspy",at=o.default.fn[rt],st={offset:10,method:"auto",target:""},lt={offset:"number",method:"string",target:"(string|element)"},ut=function(){function t(t,e){var n=this;this._element=t,this._scrollElement="BODY"===t.tagName?window:t,this._config=this._getConfig(e),this._selector=this._config.target+" .nav-link,"+this._config.target+" .list-group-item,"+this._config.target+" .dropdown-item",this._offsets=[],this._targets=[],this._activeTarget=null,this._scrollHeight=0,o.default(this._scrollElement).on("scroll.bs.scrollspy",(function(t){return n._process(t)})),this.refresh(),this._process()}var e=t.prototype;return e.refresh=function(){var t=this,e=this._scrollElement===this._scrollElement.window?"offset":"position",n="auto"===this._config.method?e:this._config.method,i="position"===n?this._getScrollTop():0;this._offsets=[],this._targets=[],this._scrollHeight=this._getScrollHeight(),[].slice.call(document.querySelectorAll(this._selector)).map((function(t){var e,r=d.getSelectorFromElement(t);if(r&&(e=document.querySelector(r)),e){var a=e.getBoundingClientRect();if(a.width||a.height)return[o.default(e)[n]().top+i,r]}return null})).filter((function(t){return t})).sort((function(t,e){return t[0]-e[0]})).forEach((function(e){t._offsets.push(e[0]),t._targets.push(e[1])}))},e.dispose=function(){o.default.removeData(this._element,"bs.scrollspy"),o.default(this._scrollElement).off(".bs.scrollspy"),this._element=null,this._scrollElement=null,this._config=null,this._selector=null,this._offsets=null,this._targets=null,this._activeTarget=null,this._scrollHeight=null},e._getConfig=function(t){if("string"!=typeof(t=l({},st,"object"==typeof t&&t?t:{})).target&&d.isElement(t.target)){var e=o.default(t.target).attr("id");e||(e=d.getUID(rt),o.default(t.target).attr("id",e)),t.target="#"+e}return d.typeCheckConfig(rt,t,lt),t},e._getScrollTop=function(){return this._scrollElement===window?this._scrollElement.pageYOffset:this._scrollElement.scrollTop},e._getScrollHeight=function(){return this._scrollElement.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)},e._getOffsetHeight=function(){return this._scrollElement===window?window.innerHeight:this._scrollElement.getBoundingClientRect().height},e._process=function(){var t=this._getScrollTop()+this._config.offset,e=this._getScrollHeight(),n=this._config.offset+e-this._getOffsetHeight();if(this._scrollHeight!==e&&this.refresh(),t>=n){var i=this._targets[this._targets.length-1];this._activeTarget!==i&&this._activate(i)}else{if(this._activeTarget&&t0)return this._activeTarget=null,void this._clear();for(var o=this._offsets.length;o--;)this._activeTarget!==this._targets[o]&&t>=this._offsets[o]&&(void 0===this._offsets[o+1]||t li > .active":".active";n=(n=o.default.makeArray(o.default(i).find(a)))[n.length-1]}var s=o.default.Event("hide.bs.tab",{relatedTarget:this._element}),l=o.default.Event("show.bs.tab",{relatedTarget:n});if(n&&o.default(n).trigger(s),o.default(this._element).trigger(l),!l.isDefaultPrevented()&&!s.isDefaultPrevented()){r&&(e=document.querySelector(r)),this._activate(this._element,i);var u=function(){var e=o.default.Event("hidden.bs.tab",{relatedTarget:t._element}),i=o.default.Event("shown.bs.tab",{relatedTarget:n});o.default(n).trigger(e),o.default(t._element).trigger(i)};e?this._activate(e,e.parentNode,u):u()}}},e.dispose=function(){o.default.removeData(this._element,"bs.tab"),this._element=null},e._activate=function(t,e,n){var i=this,r=(!e||"UL"!==e.nodeName&&"OL"!==e.nodeName?o.default(e).children(".active"):o.default(e).find("> li > .active"))[0],a=n&&r&&o.default(r).hasClass("fade"),s=function(){return i._transitionComplete(t,r,n)};if(r&&a){var l=d.getTransitionDurationFromElement(r);o.default(r).removeClass("show").one(d.TRANSITION_END,s).emulateTransitionEnd(l)}else s()},e._transitionComplete=function(t,e,n){if(e){o.default(e).removeClass("active");var i=o.default(e.parentNode).find("> .dropdown-menu .active")[0];i&&o.default(i).removeClass("active"),"tab"===e.getAttribute("role")&&e.setAttribute("aria-selected",!1)}o.default(t).addClass("active"),"tab"===t.getAttribute("role")&&t.setAttribute("aria-selected",!0),d.reflow(t),t.classList.contains("fade")&&t.classList.add("show");var r=t.parentNode;if(r&&"LI"===r.nodeName&&(r=r.parentNode),r&&o.default(r).hasClass("dropdown-menu")){var a=o.default(t).closest(".dropdown")[0];if(a){var s=[].slice.call(a.querySelectorAll(".dropdown-toggle"));o.default(s).addClass("active")}t.setAttribute("aria-expanded",!0)}n&&n()},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.tab");if(i||(i=new t(this),n.data("bs.tab",i)),"string"==typeof e){if(void 0===i[e])throw new TypeError('No method named "'+e+'"');i[e]()}}))},s(t,null,[{key:"VERSION",get:function(){return"4.6.1"}}]),t}();o.default(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',(function(t){t.preventDefault(),dt._jQueryInterface.call(o.default(this),"show")})),o.default.fn.tab=dt._jQueryInterface,o.default.fn.tab.Constructor=dt,o.default.fn.tab.noConflict=function(){return o.default.fn.tab=ft,dt._jQueryInterface};var ct="toast",ht=o.default.fn[ct],pt={animation:!0,autohide:!0,delay:500},mt={animation:"boolean",autohide:"boolean",delay:"number"},gt=function(){function t(t,e){this._element=t,this._config=this._getConfig(e),this._timeout=null,this._setListeners()}var e=t.prototype;return e.show=function(){var t=this,e=o.default.Event("show.bs.toast");if(o.default(this._element).trigger(e),!e.isDefaultPrevented()){this._clearTimeout(),this._config.animation&&this._element.classList.add("fade");var n=function(){t._element.classList.remove("showing"),t._element.classList.add("show"),o.default(t._element).trigger("shown.bs.toast"),t._config.autohide&&(t._timeout=setTimeout((function(){t.hide()}),t._config.delay))};if(this._element.classList.remove("hide"),d.reflow(this._element),this._element.classList.add("showing"),this._config.animation){var i=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,n).emulateTransitionEnd(i)}else n()}},e.hide=function(){if(this._element.classList.contains("show")){var t=o.default.Event("hide.bs.toast");o.default(this._element).trigger(t),t.isDefaultPrevented()||this._close()}},e.dispose=function(){this._clearTimeout(),this._element.classList.contains("show")&&this._element.classList.remove("show"),o.default(this._element).off("click.dismiss.bs.toast"),o.default.removeData(this._element,"bs.toast"),this._element=null,this._config=null},e._getConfig=function(t){return t=l({},pt,o.default(this._element).data(),"object"==typeof t&&t?t:{}),d.typeCheckConfig(ct,t,this.constructor.DefaultType),t},e._setListeners=function(){var t=this;o.default(this._element).on("click.dismiss.bs.toast",'[data-dismiss="toast"]',(function(){return t.hide()}))},e._close=function(){var t=this,e=function(){t._element.classList.add("hide"),o.default(t._element).trigger("hidden.bs.toast")};if(this._element.classList.remove("show"),this._config.animation){var n=d.getTransitionDurationFromElement(this._element);o.default(this._element).one(d.TRANSITION_END,e).emulateTransitionEnd(n)}else e()},e._clearTimeout=function(){clearTimeout(this._timeout),this._timeout=null},t._jQueryInterface=function(e){return this.each((function(){var n=o.default(this),i=n.data("bs.toast");if(i||(i=new t(this,"object"==typeof e&&e),n.data("bs.toast",i)),"string"==typeof e){if(void 0===i[e])throw new TypeError('No method named "'+e+'"');i[e](this)}}))},s(t,null,[{key:"VERSION",get:function(){return"4.6.1"}},{key:"DefaultType",get:function(){return mt}},{key:"Default",get:function(){return pt}}]),t}();o.default.fn[ct]=gt._jQueryInterface,o.default.fn[ct].Constructor=gt,o.default.fn[ct].noConflict=function(){return o.default.fn[ct]=ht,gt._jQueryInterface},t.Alert=h,t.Button=m,t.Carousel=E,t.Collapse=D,t.Dropdown=j,t.Modal=R,t.Popover=ot,t.Scrollspy=ut,t.Tab=dt,t.Toast=gt,t.Tooltip=G,t.Util=d,Object.defineProperty(t,"__esModule",{value:!0})}(e,n(0),n(1))},function(t,e){var n;n=function(){return this}();try{n=n||new Function("return this")()}catch(t){"object"==typeof window&&(n=window)}t.exports=n},function(t,e,n){"use strict";n.r(e);n(0),n(3),n.p;$((function(){var t=document.querySelector("div.bd-sidebar");let e=parseInt(sessionStorage.getItem("sidebar-scroll-top"),10);if(isNaN(e)){var n=document.getElementById("bd-docs-nav").querySelectorAll(".active");if(n.length>0){var i=n[n.length-1],o=i.getBoundingClientRect().y-t.getBoundingClientRect().y;if(i.getBoundingClientRect().y>.5*window.innerHeight){let e=.25;t.scrollTop=o-t.clientHeight*e,console.log("[PST]: Scrolled sidebar using last active link...")}}}else t.scrollTop=e,console.log("[PST]: Scrolled sidebar using stored browser position...");window.addEventListener("beforeunload",()=>{sessionStorage.setItem("sidebar-scroll-top",t.scrollTop)})})),$((function(){$(window).on("activate.bs.scrollspy",(function(){document.querySelectorAll("#bd-toc-nav a").forEach(t=>{t.parentElement.classList.remove("active")});document.querySelectorAll("#bd-toc-nav a.active").forEach(t=>{t.parentElement.classList.add("active")})}))}))}]); \ No newline at end of file diff --git a/_static/scripts/sphinx-book-theme.js b/_static/scripts/sphinx-book-theme.js new file mode 100644 index 00000000..a8a305eb --- /dev/null +++ b/_static/scripts/sphinx-book-theme.js @@ -0,0 +1,2 @@ +!function(e){var t={};function n(o){if(t[o])return t[o].exports;var r=t[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,n),r.l=!0,r.exports}n.m=e,n.c=t,n.d=function(e,t,o){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(n.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var r in e)n.d(o,r,function(t){return e[t]}.bind(null,r));return o},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){e.exports=n(1)},function(e,t,n){"use strict";n.r(t);n.p;var o=e=>{"loading"!=document.readyState?e():document.addEventListener?document.addEventListener("DOMContentLoaded",e):document.attachEvent("onreadystatechange",(function(){"complete"==document.readyState&&e()}))};window.initThebeSBT=()=>{var e=$("div.section h1")[0];$(e).next().hasClass("thebe-launch-button")||$("").insertAfter($(e)),initThebe()},window.printPdf=e=>{let t=$(e).attr("aria-describedby"),n=$("#"+t).detach();window.print(),$("body").append(n)},window.toggleFullScreen=()=>{var e=document.fullscreenElement&&null!==document.fullscreenElement||document.webkitFullscreenElement&&null!==document.webkitFullscreenElement;let t=document.documentElement;e?(console.log("[SBT]: Exiting full screen"),document.exitFullscreen?document.exitFullscreen():document.webkitExitFullscreen&&document.webkitExitFullscreen()):(console.log("[SBT]: Entering full screen"),t.requestFullscreen?t.requestFullscreen():t.webkitRequestFullscreen&&t.webkitRequestFullscreen())},o(()=>{$(document).ready((function(){$('[data-toggle="tooltip"]').tooltip({trigger:"hover",delay:{show:500,hide:100}})}))}),o(()=>{var e=document.getElementById("site-navigation"),t=e.querySelectorAll(".active"),n=t[t.length-1];void 0!==n&&n.offsetTop>.5*$(window).height()&&(e.scrollTop=n.offsetTop-.2*$(window).height())}),o(()=>{var e=[];let t=new IntersectionObserver((t,n)=>{t.forEach(t=>{if(t.isIntersecting)e.push(t.target);else for(let n=0;n0?$("div.bd-toc").removeClass("show"):$("div.bd-toc").addClass("show")});let n=[];["marginnote","sidenote","margin","margin-caption","full-width","sidebar","popout"].forEach(e=>{n.push("."+e,".tag_"+e,"."+e.replace("-","_"),".tag_"+e.replace("-","_"))}),document.querySelectorAll(n.join(", ")).forEach(e=>{t.observe(e)}),new IntersectionObserver((e,t)=>{e[0].boundingClientRect.y<0?document.body.classList.add("scrolled"):document.body.classList.remove("scrolled")}).observe(document.querySelector(".sbt-scroll-pixel-helper"))}),o((function(){new MutationObserver((e,t)=>{e.forEach(e=>{0!==e.addedNodes.length&&void 0!==e.addedNodes[0].data&&-1!=e.addedNodes[0].data.search("Inserted RTD Footer")&&e.addedNodes.forEach(e=>{document.getElementById("rtd-footer-container").append(e)})})}).observe(document.body,{childList:!0})}))}]); +//# sourceMappingURL=sphinx-book-theme.js.map \ No newline at end of file diff --git a/_static/scripts/sphinx-book-theme.js.map b/_static/scripts/sphinx-book-theme.js.map new file mode 100644 index 00000000..dccd768e --- /dev/null +++ b/_static/scripts/sphinx-book-theme.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./src/sphinx_book_theme/assets/styles/index.scss","webpack:///./src/sphinx_book_theme/assets/scripts/index.js"],"names":["installedModules","__webpack_require__","moduleId","exports","module","i","l","modules","call","m","c","d","name","getter","o","Object","defineProperty","enumerable","get","r","Symbol","toStringTag","value","t","mode","__esModule","ns","create","key","bind","n","object","property","prototype","hasOwnProperty","p","s","sbRunWhenDOMLoaded","cb","document","readyState","addEventListener","attachEvent","window","initThebeSBT","title","$","next","hasClass","insertAfter","initThebe","printPdf","el","tooltipID","attr","tooltipTextDiv","detach","print","append","toggleFullScreen","isInFullScreen","fullscreenElement","webkitFullscreenElement","docElm","documentElement","console","log","exitFullscreen","webkitExitFullscreen","requestFullscreen","webkitRequestFullscreen","ready","tooltip","trigger","delay","show","hide","navbar","getElementById","active_pages","querySelectorAll","active_page","length","undefined","offsetTop","height","scrollTop","onScreenItems","tocObserver","IntersectionObserver","entries","observer","forEach","entry","isIntersecting","push","target","ii","splice","removeClass","addClass","marginSelector","replace","join","observe","boundingClientRect","y","body","classList","add","remove","querySelector","MutationObserver","mutationList","mutation","addedNodes","data","search","node","childList"],"mappings":"aACE,IAAIA,EAAmB,GAGvB,SAASC,EAAoBC,GAG5B,GAAGF,EAAiBE,GACnB,OAAOF,EAAiBE,GAAUC,QAGnC,IAAIC,EAASJ,EAAiBE,GAAY,CACzCG,EAAGH,EACHI,GAAG,EACHH,QAAS,IAUV,OANAI,EAAQL,GAAUM,KAAKJ,EAAOD,QAASC,EAAQA,EAAOD,QAASF,GAG/DG,EAAOE,GAAI,EAGJF,EAAOD,QAKfF,EAAoBQ,EAAIF,EAGxBN,EAAoBS,EAAIV,EAGxBC,EAAoBU,EAAI,SAASR,EAASS,EAAMC,GAC3CZ,EAAoBa,EAAEX,EAASS,IAClCG,OAAOC,eAAeb,EAASS,EAAM,CAAEK,YAAY,EAAMC,IAAKL,KAKhEZ,EAAoBkB,EAAI,SAAShB,GACX,oBAAXiB,QAA0BA,OAAOC,aAC1CN,OAAOC,eAAeb,EAASiB,OAAOC,YAAa,CAAEC,MAAO,WAE7DP,OAAOC,eAAeb,EAAS,aAAc,CAAEmB,OAAO,KAQvDrB,EAAoBsB,EAAI,SAASD,EAAOE,GAEvC,GADU,EAAPA,IAAUF,EAAQrB,EAAoBqB,IAC/B,EAAPE,EAAU,OAAOF,EACpB,GAAW,EAAPE,GAA8B,iBAAVF,GAAsBA,GAASA,EAAMG,WAAY,OAAOH,EAChF,IAAII,EAAKX,OAAOY,OAAO,MAGvB,GAFA1B,EAAoBkB,EAAEO,GACtBX,OAAOC,eAAeU,EAAI,UAAW,CAAET,YAAY,EAAMK,MAAOA,IACtD,EAAPE,GAA4B,iBAATF,EAAmB,IAAI,IAAIM,KAAON,EAAOrB,EAAoBU,EAAEe,EAAIE,EAAK,SAASA,GAAO,OAAON,EAAMM,IAAQC,KAAK,KAAMD,IAC9I,OAAOF,GAIRzB,EAAoB6B,EAAI,SAAS1B,GAChC,IAAIS,EAAST,GAAUA,EAAOqB,WAC7B,WAAwB,OAAOrB,EAAgB,SAC/C,WAA8B,OAAOA,GAEtC,OADAH,EAAoBU,EAAEE,EAAQ,IAAKA,GAC5BA,GAIRZ,EAAoBa,EAAI,SAASiB,EAAQC,GAAY,OAAOjB,OAAOkB,UAAUC,eAAe1B,KAAKuB,EAAQC,IAGzG/B,EAAoBkC,EAAI,GAIjBlC,EAAoBA,EAAoBmC,EAAI,G,sEClFtC,QCSXC,EAAsBC,IACG,WAAvBC,SAASC,WACXF,IACSC,SAASE,iBAClBF,SAASE,iBAAiB,mBAAoBH,GAE9CC,SAASG,YAAY,sBAAsB,WACd,YAAvBH,SAASC,YAA0BF,QAyM7CK,OAAOC,aAjDY,KACjB,IAAIC,EAAQC,EAAE,kBAAkB,GAC3BA,EAAED,GAAOE,OAAOC,SAAS,wBAC5BF,EAAE,iDAAiDG,YAAYH,EAAED,IAEnEK,aA6CFP,OAAOQ,SAhJSC,IAGd,IAAIC,EAAYP,EAAEM,GAAIE,KAAK,oBACvBC,EAAiBT,EAAE,IAAMO,GAAWG,SACxCb,OAAOc,QACPX,EAAE,QAAQY,OAAOH,IA2InBZ,OAAOgB,iBA/LgB,KACrB,IAAIC,EACDrB,SAASsB,mBAAoD,OAA/BtB,SAASsB,mBACvCtB,SAASuB,yBAC6B,OAArCvB,SAASuB,wBACb,IAAIC,EAASxB,SAASyB,gBACjBJ,GAQHK,QAAQC,IAAI,8BACR3B,SAAS4B,eACX5B,SAAS4B,iBACA5B,SAAS6B,sBAClB7B,SAAS6B,yBAXXH,QAAQC,IAAI,+BACRH,EAAOM,kBACTN,EAAOM,oBACEN,EAAOO,yBAChBP,EAAOO,4BAyLbjC,EA7CmB,KACjBS,EAAEP,UAAUgC,OAAM,WAChBzB,EAAE,2BAA2B0B,QAAQ,CACnCC,QAAS,QACTC,MAAO,CAAEC,KAAM,IAAKC,KAAM,YA0ChCvC,EAxKqB,KACnB,IAAIwC,EAAStC,SAASuC,eAAe,mBACjCC,EAAeF,EAAOG,iBAAiB,WACvCC,EAAcF,EAAaA,EAAaG,OAAS,QAGnCC,IAAhBF,GACAA,EAAYG,UAAiC,GAArBtC,EAAEH,QAAQ0C,WAElCR,EAAOS,UAAYL,EAAYG,UAAiC,GAArBtC,EAAEH,QAAQ0C,YAgKzDhD,EAjIkB,KAChB,IAAIkD,EAAgB,GACpB,IAkCIC,EAAc,IAAIC,qBAlCA,CAACC,EAASC,KAE9BD,EAAQE,QAASC,IACf,GAAIA,EAAMC,eAERP,EAAcQ,KAAKF,EAAMG,aAGzB,IAAK,IAAIC,EAAK,EAAGA,EAAKV,EAAcL,OAAQe,IAC1C,GAAIV,EAAcU,KAAQJ,EAAMG,OAAQ,CACtCT,EAAcW,OAAOD,EAAI,GACzB,SAOJV,EAAcL,OAAS,EACzBpC,EAAE,cAAcqD,YAAY,QAE5BrD,EAAE,cAAcsD,SAAS,UAwB7B,IAAIC,EAAiB,GATG,CACtB,aACA,WACA,SACA,iBACA,aACA,UACA,UAGcT,QAASK,IAEvBI,EAAeN,KAEX,IAAIE,EACJ,QAAQA,EACR,IAAIA,EAAGK,QAAQ,IAAK,KACpB,QAAQL,EAAGK,QAAQ,IAAK,QAI9B/D,SAASyC,iBAAiBqB,EAAeE,KAAK,OAAOX,QAASK,IAC5DT,EAAYgB,QAAQP,KAID,IAAIR,qBAtCO,CAACC,EAASC,KAEpCD,EAAQ,GAAGe,mBAAmBC,EAAI,EACpCnE,SAASoE,KAAKC,UAAUC,IAAI,YAE5BtE,SAASoE,KAAKC,UAAUE,OAAO,cAkCpBN,QAAQjE,SAASwE,cAAc,+BAiEhD1E,GApCA,WAkBmB,IAAI2E,iBAjBG,CAACC,EAActB,KACrCsB,EAAarB,QAASsB,IAEe,IAA/BA,EAASC,WAAWjC,aAGYC,IAAhC+B,EAASC,WAAW,GAAGC,OAGuC,GAA9DF,EAASC,WAAW,GAAGC,KAAKC,OAAO,wBACrCH,EAASC,WAAWvB,QAAS0B,IAC3B/E,SAASuC,eAAe,wBAAwBpB,OAAO4D,SAQtDd,QAAQjE,SAASoE,KADX,CAAEY,WAAW","file":"scripts/sphinx-book-theme.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = 0);\n","export default __webpack_public_path__ + \"styles/sphinx-book-theme.css\";","// Import CSS variables\n// ref: https://css-tricks.com/getting-javascript-to-talk-to-css-and-sass/\nimport \"../styles/index.scss\";\n\n/**\n * A helper function to load scripts when the DOM is loaded.\n * This waits for everything to be on the page first before running, since\n * some functionality doesn't behave properly until everything is ready.\n */\nvar sbRunWhenDOMLoaded = (cb) => {\n if (document.readyState != \"loading\") {\n cb();\n } else if (document.addEventListener) {\n document.addEventListener(\"DOMContentLoaded\", cb);\n } else {\n document.attachEvent(\"onreadystatechange\", function () {\n if (document.readyState == \"complete\") cb();\n });\n }\n};\n\n/**\n * Toggle full-screen with button\n *\n * There are some browser-specific hacks in here:\n * - Safari requires a `webkit` prefix, so this uses conditionals to check for that\n * ref: https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API\n */\nvar toggleFullScreen = () => {\n var isInFullScreen =\n (document.fullscreenElement && document.fullscreenElement !== null) ||\n (document.webkitFullscreenElement &&\n document.webkitFullscreenElement !== null);\n let docElm = document.documentElement;\n if (!isInFullScreen) {\n console.log(\"[SBT]: Entering full screen\");\n if (docElm.requestFullscreen) {\n docElm.requestFullscreen();\n } else if (docElm.webkitRequestFullscreen) {\n docElm.webkitRequestFullscreen();\n }\n } else {\n console.log(\"[SBT]: Exiting full screen\");\n if (document.exitFullscreen) {\n document.exitFullscreen();\n } else if (document.webkitExitFullscreen) {\n document.webkitExitFullscreen();\n }\n }\n};\n\n/**\n * Sidebar scroll on load.\n *\n * Detect the active page in the sidebar, and scroll so that it is centered on\n * the screen.\n */\nvar scrollToActive = () => {\n var navbar = document.getElementById(\"site-navigation\");\n var active_pages = navbar.querySelectorAll(\".active\");\n var active_page = active_pages[active_pages.length - 1];\n // Only scroll the navbar if the active link is lower than 50% of the page\n if (\n active_page !== undefined &&\n active_page.offsetTop > $(window).height() * 0.5\n ) {\n navbar.scrollTop = active_page.offsetTop - $(window).height() * 0.2;\n }\n};\n\n/**\n * Called when the \"print to PDF\" button is clicked.\n * This is a hack to prevent tooltips from showing up in the printed PDF.\n */\nvar printPdf = (el) => {\n // Detach the tooltip text from DOM to hide in PDF\n // and then reattach it for HTML\n let tooltipID = $(el).attr(\"aria-describedby\");\n let tooltipTextDiv = $(\"#\" + tooltipID).detach();\n window.print();\n $(\"body\").append(tooltipTextDiv);\n};\n\n/**\n * Manage scrolling behavior. This is primarily two things:\n *\n * 1. Hide the Table of Contents any time sidebar content is on the screen.\n *\n * This will be triggered any time a sidebar item enters or exits the screen.\n * It adds/removes items from an array if they have entered the screen, and\n * removes them when they exit the screen. It hides the TOC if anything is\n * on-screen.\n *\n * ref: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API\n *\n * 2. Add a `scrolled` class to to trigger CSS changes.\n */\nvar initTocHide = () => {\n var onScreenItems = [];\n let hideTocCallback = (entries, observer) => {\n // Check whether any sidebar item is displayed\n entries.forEach((entry) => {\n if (entry.isIntersecting) {\n // If an element just came on screen, add it our list\n onScreenItems.push(entry.target);\n } else {\n // Otherwise, if it's in our list then remove it\n for (let ii = 0; ii < onScreenItems.length; ii++) {\n if (onScreenItems[ii] === entry.target) {\n onScreenItems.splice(ii, 1);\n break;\n }\n }\n }\n });\n\n // Hide the TOC if any margin content is displayed on the screen\n if (onScreenItems.length > 0) {\n $(\"div.bd-toc\").removeClass(\"show\");\n } else {\n $(\"div.bd-toc\").addClass(\"show\");\n }\n };\n let manageScrolledClassOnBody = (entries, observer) => {\n // The pixel is at the top, so if we're < 0 that it means we've scrolled\n if (entries[0].boundingClientRect.y < 0) {\n document.body.classList.add(\"scrolled\");\n } else {\n document.body.classList.remove(\"scrolled\");\n }\n };\n\n // Set up the intersection observer to watch all margin content\n let tocObserver = new IntersectionObserver(hideTocCallback);\n // TODO: deprecate popout after v0.5.0\n const selectorClasses = [\n \"marginnote\",\n \"sidenote\",\n \"margin\",\n \"margin-caption\",\n \"full-width\",\n \"sidebar\",\n \"popout\",\n ];\n let marginSelector = [];\n selectorClasses.forEach((ii) => {\n // Use three permutations of each class name because `tag_` and `_` used to be supported\n marginSelector.push(\n ...[\n `.${ii}`,\n `.tag_${ii}`,\n `.${ii.replace(\"-\", \"_\")}`,\n `.tag_${ii.replace(\"-\", \"_\")}`,\n ]\n );\n });\n document.querySelectorAll(marginSelector.join(\", \")).forEach((ii) => {\n tocObserver.observe(ii);\n });\n\n // Set up the observer to check if we've scrolled from top of page\n let scrollObserver = new IntersectionObserver(manageScrolledClassOnBody);\n scrollObserver.observe(document.querySelector(\".sbt-scroll-pixel-helper\"));\n};\n\n/**\n * Activate Thebe with a custom button click.\n */\nvar initThebeSBT = () => {\n var title = $(\"div.section h1\")[0];\n if (!$(title).next().hasClass(\"thebe-launch-button\")) {\n $(\"\").insertAfter($(title));\n }\n initThebe();\n};\n\n/**\n * Use Bootstrap helper function to enable tooltips.\n */\nvar initTooltips = () => {\n $(document).ready(function () {\n $('[data-toggle=\"tooltip\"]').tooltip({\n trigger: \"hover\",\n delay: { show: 500, hide: 100 },\n });\n });\n};\n\n/**\n * MutationObserver to move the ReadTheDocs button\n */\nfunction initRTDObserver() {\n const mutatedCallback = (mutationList, observer) => {\n mutationList.forEach((mutation) => {\n // Check whether the mutation is for RTD, which will have a specific structure\n if (mutation.addedNodes.length === 0) {\n return;\n }\n if (mutation.addedNodes[0].data === undefined) {\n return;\n }\n if (mutation.addedNodes[0].data.search(\"Inserted RTD Footer\") != -1) {\n mutation.addedNodes.forEach((node) => {\n document.getElementById(\"rtd-footer-container\").append(node);\n });\n }\n });\n };\n\n const observer = new MutationObserver(mutatedCallback);\n const config = { childList: true };\n observer.observe(document.body, config);\n}\n\n/**\n * Set up callback functions for UI click actions\n */\nwindow.initThebeSBT = initThebeSBT;\nwindow.printPdf = printPdf;\nwindow.toggleFullScreen = toggleFullScreen;\n\n/**\n * Set up functions to load when the DOM is ready\n */\nsbRunWhenDOMLoaded(initTooltips);\nsbRunWhenDOMLoaded(scrollToActive);\nsbRunWhenDOMLoaded(initTocHide);\nsbRunWhenDOMLoaded(initRTDObserver);\n"],"sourceRoot":""} \ No newline at end of file diff --git a/_static/searchtools.js b/_static/searchtools.js new file mode 100644 index 00000000..0a44e858 --- /dev/null +++ b/_static/searchtools.js @@ -0,0 +1,525 @@ +/* + * searchtools.js + * ~~~~~~~~~~~~~~~~ + * + * Sphinx JavaScript utilities for the full-text search. + * + * :copyright: Copyright 2007-2022 by the Sphinx team, see AUTHORS. + * :license: BSD, see LICENSE for details. + * + */ + +if (!Scorer) { + /** + * Simple result scoring code. + */ + var Scorer = { + // Implement the following function to further tweak the score for each result + // The function takes a result array [filename, title, anchor, descr, score] + // and returns the new score. + /* + score: function(result) { + return result[4]; + }, + */ + + // query matches the full name of an object + objNameMatch: 11, + // or matches in the last dotted part of the object name + objPartialMatch: 6, + // Additive scores depending on the priority of the object + objPrio: {0: 15, // used to be importantResults + 1: 5, // used to be objectResults + 2: -5}, // used to be unimportantResults + // Used when the priority is not in the mapping. + objPrioDefault: 0, + + // query found in title + title: 15, + partialTitle: 7, + // query found in terms + term: 5, + partialTerm: 2 + }; +} + +if (!splitQuery) { + function splitQuery(query) { + return query.split(/\s+/); + } +} + +/** + * Search Module + */ +var Search = { + + _index : null, + _queued_query : null, + _pulse_status : -1, + + htmlToText : function(htmlString) { + var virtualDocument = document.implementation.createHTMLDocument('virtual'); + var htmlElement = $(htmlString, virtualDocument); + htmlElement.find('.headerlink').remove(); + docContent = htmlElement.find('[role=main]')[0]; + if(docContent === undefined) { + console.warn("Content block not found. Sphinx search tries to obtain it " + + "via '[role=main]'. Could you check your theme or template."); + return ""; + } + return docContent.textContent || docContent.innerText; + }, + + init : function() { + var params = $.getQueryParameters(); + if (params.q) { + var query = params.q[0]; + $('input[name="q"]')[0].value = query; + this.performSearch(query); + } + }, + + loadIndex : function(url) { + $.ajax({type: "GET", url: url, data: null, + dataType: "script", cache: true, + complete: function(jqxhr, textstatus) { + if (textstatus != "success") { + document.getElementById("searchindexloader").src = url; + } + }}); + }, + + setIndex : function(index) { + var q; + this._index = index; + if ((q = this._queued_query) !== null) { + this._queued_query = null; + Search.query(q); + } + }, + + hasIndex : function() { + return this._index !== null; + }, + + deferQuery : function(query) { + this._queued_query = query; + }, + + stopPulse : function() { + this._pulse_status = 0; + }, + + startPulse : function() { + if (this._pulse_status >= 0) + return; + function pulse() { + var i; + Search._pulse_status = (Search._pulse_status + 1) % 4; + var dotString = ''; + for (i = 0; i < Search._pulse_status; i++) + dotString += '.'; + Search.dots.text(dotString); + if (Search._pulse_status > -1) + window.setTimeout(pulse, 500); + } + pulse(); + }, + + /** + * perform a search for something (or wait until index is loaded) + */ + performSearch : function(query) { + // create the required interface elements + this.out = $('#search-results'); + this.title = $('

' + _('Searching') + '

').appendTo(this.out); + this.dots = $('').appendTo(this.title); + this.status = $('

 

').appendTo(this.out); + this.output = $('